#JavaScript (ES6), 175 bytes

Takes input in <s>curling</s> currying syntax `([x, y])(a)`, where **x** and **y** are the 0-indexed coordinates of the starting position and **a[&nbsp;]** is a matrix of integers, with `0` = ice, `1` = wall, `2` = sand, `3` = hole and `4` = water

Returns `0` if there's no solution.

<!-- language-all: lang-js -->

 p=>a=>(r=F=([x,y],n,R=a[y],c=R[x])=>R[c==(R[x]=4)|n>=r||[-1,0,1,2].map(d=>(g=_=>(k=a[v=Y,Y+=d%2][h=X,X+=~-d%2])||g())(X=x,Y=y)>3?0:k>2?r=-~n:F(k>1?[X,Y]:[h,v],-~n)),x]=c)(p)|r

[Try it online!](https://tio.run/##zVdbi@IwFH73VxwISxsmBnVmb0JqYWFeF@ZJKWUp1bnszFrR2UFB8tfd9GqbnMTOsg8bQXNOzvU7Jxd/Jm/JLt0@bV6H62y5Ot2L00YEiQj8rbgVfrRnh5it2Z1IIjVJxV20j6kI7qJUCD8nxA09rgOxPR6j4ZiN2JhNYv4r2fhLZeNB/FDfz0r5TSzY4kosP0zi6FHM2fxKyGFO0ePxwafUn4s9W4gDDa5no@lzMJltxVCup7f@czCeRXO2iKfRI3uLmeJSypTjlPobetyeXle7VxCQgAjAH0A@gaQIwd8yWNCcvy3plMG8oFMQArzQgxn4eyU/Z3BQPwsGIwpT8IDwTHr8ab1c7b/f@ylVLpXp@xwQOMTUT@iADgZptt5lLyv@kj343rfs93a3AjL2GOQx@ZHSiDjnHlHDi1lDQtghM9khC@FBTG32J6j9jokQoOsw02ht/aLPa8OnlLJlQYYdinco@EuqayXrULl3R7w3OEY6TrIcOi/UeQowriDrykHx6fK4Qpob9gDxYfg9x@fI6yNWB6nVQn2BhmTINTRNmTwZrW4cuI66vIT8JzvyRO9BhaoaJhdMrizYWhfn0KLcQro/N@sjG6Jc0q9unx2o6M1i@FCV4vmCGaTJLGR1dZ63IJajRFLkmSVDZ4JfXGXHBpo1tH@qgUOhtmRtCaqpBZ@WKY7abEDLP@VmlTbvFZKlJK@tFh8c3lJS2m2WmJfBEZmBPfdLeDqq87Xfcag8hsgWMY4vjMeLLDgiBxd5vMgeQRDt@V66/fz@RzFLxIfs77dXzL15@ZEI8G@urPGoz9HQcc/LgW8DcG97ou11fZWAS7fSz5Utq8WuJubq@XCx@a1OBIJbLp4drlV5wS935Fsp2vJ1rRL3amZZJQ3SWMzNIU9Ij2PO3V7j9948RixGS5H6ItKTOndHe1am2X1vn9PvzExRUj4u2z0JVVUx0dpifVMWDSkN0Xqx7J1KwrGfmuYOq0ur1NHA4lXeiFWiX5Xk3HiFqB0B3kETnLiS9sXfDFu1zgk3E4JbbTYZnB8OqGiVPfJOed91faGpJ72a2nw4208gIHXtsHKBgRVyqpI2kPrfya4BzDhpbXs0tCY2NLR6klkO/BBc2rbQiPO52VUlaN6kj3alj/SS685wo6aBBu9pB2tDnv4A "JavaScript (Node.js) – Try It Online")

###Commented

 p => a => ( // given the starting position p[] and the matrix a[]
 r = // r = best result, initialized to a non-numeric value
 F = ( // F = recursive function taking:
 [x, y], // (x, y) = current position
 n, // n = number of shots, initially undefined
 R = a[y], // R = current row in the matrix
 c = R[x] // c = value of the current cell
 ) => //
 R[ // this will update R[x] once the inner code is executed
 c == (R[x] = 4) | // set the current cell to 4 (water); abort if it was
 n >= r || // already set to 4 or n is greater than or equal to r
 [-1, 0, 1, 2].map(d => // otherwise, for each direction d:
 (g = _ => ( // g = recursive function performing the shot by
 k = a[ // saving a backup (h, v) of (X, Y)
 v = Y, Y += d % 2][ // and updating (X, Y) until we reach a cell
 h = X, X += ~-d % 2]) // whose value k is not ice (k != 0)
 || g() // 
 )(X = x, Y = y) // initial call to g() with (X, Y) = (x, y)
 > 3 ? // if k = 4 (water -> fail):
 0 // abort immediately
 : // else:
 k > 2 ? // if k = 3 (hole -> success):
 r = -~n // set r to n + 1
 : // else:
 F( // do a recursive call to F():
 k > 1 ? // if k = 2 (sand):
 [X, Y] // start the next shots from the last cell
 : // else (wall):
 [h, v], // start from the last ice cell
 -~n // increment the number of shots
 ) // end of recursive call
 ), x // end of map(); x = actual index used to access R[]
 ] = c // restore the value of the current cell to c
 )(p) | r // initial call to F() at the starting position; return r