Optimal solution to go to opposite corner of a rectangle

Retina, 54 53 bytes

\d+
$*.
S_`(?<=(.+)¶.*).|\D
T`.`#`.¶|.*$
:m-1=`^#
X
#

Takes input separated by a linefeed and outputs the solution grid followed by the move count.

Try it online!

Explanation

\d+
$*.

Turn both integers into that many .s, i.e. convert them to unary.

S_`(?<=(.+)¶.*).|\D

This builds a grid of .s, by matching each . in the unary height and capturing the unary representation of the width. The S activates split mode which returns the captured strings, and the |\D and _ together ensure that everything else is removed from the string.

T`.`#`.¶|.*$

This turns the last character of each line as well as the entire last line into #s.

:m-1=`^#
X

This uses a ton of options to convert only the first # on the last row to X (we need to make sure that only the last row is affected because of width-1 inputs). m activates multi-line mode which makes ^ match the beginning of lines. -1= tells Retina to perform the substitution only on the last match. Finally, : switches off the default silent mode such that the grid is printed to STDOUT as an intermediate result.

#

Finally, we simply count the number # in the string, which corresponds to the number of moves.


Pyke, 26 bytes

DtQ+RtF; Q\.*t\#+)\X\#Qt*+

Try it here


Or a noncompetitive 34 bytes, add apply node with an ast)

jUa]Dm!X|RZjht]q+".#X"R@)Fs
);jmts

Try it here!

Or 30 bytes if allowed spaces as padding

jUa]Dm!X|RZjht]q+".#X"R@)Pjmts

Pyth, 32 29 24 bytes

AtMQVH+*\.G\#;+\X*\#G+GH

Try it online!

Sample input:

(4, 5)

Sample output:

...#
...#
...#
...#
X###
7

How it works:

AtMQVH+*\.G\#;+\X*\#G+GH
                           assign('Q',eval_input())
AtMQ                       assign('[G,H]',Pmap(lambda d:tail(d),Q))
    VH       ;             for N in range(H):
      +*\.G\#                  implicit_print(plus(times(".",G),"#"))
              +\X*\#G      implicit_print(plus("X",times("#",G)))
                     +GH   implicit_print(plus(G,H))

Previous attempt:

JthQK@Q1+*++*\.J\#btK+\X*\#Jt+JK

Try it online!

Sample input:

(4, 5)

Sample output:

...#
...#
...#
...#
X###
7

How it works:

JthQK@Q1+*++*\.J\#btK+\X*\#Jt+JK
                                 assign('Q',eval_input())        --Q is now an official pair of numbers (4, 5)
JthQ                             assign("J",decrement(first(Q))) --gets the first element, and then take 1 from it, and assign it to J
    K@Q1                         assign("K",lookup(Q,1))         --K is now the second element (count from 0) of the pair.
        +            +\X*\#J     concat(-----------------------------------------------------------,concat("X",times("#",J)))
         *         tK                   repeat(--------------------------------------,decrement(K))
          +       b                            concat(-------------------------,"\n")
           +    \#                                    concat(-------------,"#")
            *\.J                                             repeat(".",J)
                            t+JK decrement(add(J,K)) <--- auto-print