algorithm - A* Admissible Heuristic for die rolling on grid -


i need finding heuristic following problem:

you given r-by-c grid , six-sided die. let start , end 2 distinct cells on grid. find path start end such sum of faces of die looking up, die turning along path, minimal.

the starting orientation of die following (the "2" facing south):

enter image description here

the way modeled problem considering value of die's face cost of edge in graph. graph's vertices of form (row, col, die) (i.e, position in grid , current state/orientation of die). reason vertex not (row, col) because can end on same cell multiple configurations/orientations of die.

i used a* find solution problem; answers given correct, not efficient enough. i've determined problem heuristic i'm using. i'm using manhattan distance, admissible. if multiply heuristic constant, it's no longer admissible: runs faster doesn't find right answer.

i need in finding better heuristic manhattan distance.

here's algorithm applied paul's example of 300x300 grid, starting (23,25) , ending @ (282, 199). finds minimum path , sum (1515, 2 points less paul's result of 1517) in 0.52 seconds. version look-up tables instead of calculating small sections took 0.13 seconds.

haskell code:

import data.list (minimumby) import data.ord (comparing) import control.monad (guard)  rolldie die@[left,right,top,bottom,front,back] move   | move == "u" = [left,right,front,back,bottom,top]   | move == "d" = [left,right,back,front,top,bottom]   | move == "l" = [top,bottom,right,left,front,back]   | move == "r" = [bottom,top,left,right,front,back]  dietop die = die!!2  --diestartingorientation = [4,3,1,6,2,5] --left,right,top,bottom,front,back  rows = 300 columns = 300  paths (startrow,startcolumn) (endrow,endcolumn) diestartingorientation =    solve (dietop diestartingorientation,[]) [(startrow,startcolumn)] diestartingorientation     leftborder = max 0 (min startcolumn endcolumn)     rightborder = min columns (max startcolumn endcolumn)     topborder = endrow     bottomborder = startrow     solve result@(cost,moves) ((i,j):pathtail) die =       if (i,j) == (endrow,endcolumn)          [(result,die)]          else            ((i',j'),move) <- ((i+1,j),"u"):next            guard (i' <= topborder && i' >= bottomborder && j' <= rightborder && j' >= leftborder)            solve (cost + dietop (rolldie die move),move:moves) ((i',j'):(i,j):pathtail) (rolldie die move)          next | null pathtail            = [((i,j+1),"r"),((i,j-1),"l")]                    | head pathtail == (i,j-1) = [((i,j+1),"r")]                    | head pathtail == (i,j+1) = [((i,j-1),"l")]                    | otherwise                = [((i,j+1),"r"),((i,j-1),"l")]  --300x300 grid starting @ (23, 25) , ending @ (282,199)  applicationnum =    let (r,c) = (282-22, 199-24)       numrowreductions = floor (r/4) - 1       numcolumnreductions = floor (c/4) - 1       minimalr = r - 4 * frominteger numrowreductions       minimalc = c - 4 * frominteger numcolumnreductions   in (fst . fst . minimumby (comparing fst) $ paths (1,1) (minimalr,minimalc) [4,3,1,6,2,5])      + 14*numrowreductions + 14*numcolumnreductions  applicationpath = [firstleg] ++ secondleg ++ thirdleg                ++ [((0,["r"]),[])] ++ [minimumby (comparing fst) $ paths (1,1) (2,4) die2]            (r,c) = (282-22, 199-24) --(260,175)       numrowreductions = floor (r/4) - 1       numcolumnreductions = floor (c/4) - 1       minimalr = r - 4 * frominteger numrowreductions       minimalc = c - 4 * frominteger numcolumnreductions       firstleg = minimumby (comparing fst) $ paths (1,1) (minimalr,minimalc) [4,3,1,6,2,5]       die0 = snd firstleg       secondleg = tail . foldr mfs0 [((0,["r"]),die0)] $ [1..numcolumnreductions - 1]       die1 = snd . last $ secondleg       thirdleg = tail . foldr mfs1  [((0,[]),die1)] $ [1..numrowreductions - 3 * div (numcolumnreductions - 1) 4 - 1]       die2 = rolldie (snd . last $ thirdleg) "r"       mfs0 b = b ++ [((0,["r"]),[])] ++ [minimumby (comparing fst) $ paths (1,1) (4,4) (rolldie (snd . last $ b) "r")]       mfs1 b = b ++ [((0,["u"]),[])] ++ [minimumby (comparing fst) $ paths (1,1) (4,1) (rolldie (snd . last $ b) "u")] 

output:

*main> applicationnum 1515  *main> applicationpath [((31,["r","r","r","r","u","u","r","u","r"]),[5,2,1,6,4,3]) ,((0,["r"]),[]),((25,["r","r","r","u","u","u"]),[3,4,1,6,5,2]) ,((0,["r"]),[]),((24,["r","u","r","r","u","u"]),[5,2,1,6,4,3]) ................((17,["r","r","r","u"]),[5,2,1,6,4,3])] (0.52 secs, 32093988 bytes) 

list of "r" , "u":

*main> let listrl = concatmap (\((a,b),c) -> b) applicationpath *main> listrl ["r","r","r","r","u","u","r","u","r","r","r","r","r","u","u","u","r","r","u","r" ..."u","r","r","r","r","u"] 

sum of path using starting die , list of "r" , "u":

*main> let sumpath path = foldr (\move (cost,die) -> (cost + dietop (rolldie die move), rolldie die move)) (1,[4,3,1,6,2,5]) path *main> sumpath listrl (1515,[5,2,1,6,4,3]) 

calculation of (r,c) (1,1) using list of "r" , "u" (since start @ (1,1,), (r,c) gets adjusted (282-22, 199-24):

*main> let rc path = foldr (\move (r,c) -> if move == "r" (r,c+1) else (r+1,c)) (1,1) path *main> rc listrl  (260,175) 


algorithm/solution

continuing research below, seems minimal face-sum path (mfs)  can reduced, mod 4, either rows or columns so:  mfs (1,1) (r,c) == mfs (1,1) (r-4,c) + 14, r > 7                 == mfs (1,1) (r,c-4) + 14, c > 7  makes finding number minimal path straightforward:  mfs (1,1) (r,c) =    let numrowreductions = floor (r/4) - 1       numcolumnreductions = floor (c/4) - 1       minimalr = r - 4 * numrowreductions       minimalc = c - 4 * numcolumnreductions   in mfs (1,1) (minimalr,minimalc) + 14*numrowreductions + 14*numcolumnreductions  minimalr , minimalc less eight, means can pre-calculate minimal-face-sums these , use table output overall solution. 

but how find path?
from testing, seems work out similarly:

mfs (1,1) (1,anything) = trivial mfs (1,1) (anything,1) = trivial mfs (1,1) (r,c), r,c < 5 = calculate solution in favorite way mfs (1,1) (r,c), either or both r,c > 4 =    mfs (1,1) (minimalr,minimalc) -> roll ->    mfs (1,1) (min 4 r-1, min 4 c-1) -> roll ->    ...sections must arranged last 1 includes       4 rotations 1 axis , @ least 1 other.      keeping 1 row or column same till end seems work.      (for paul's example above, after initial mfs box, moved in       fours along x-axis, rolling 4x4 boxes right,       means y-axis advanced in threes , section in fours       going up, until last box of 2x4. suspect, haven't checked,       sections must divide @ least 1 axis in fours       work)...   mfs (1,1) (either (if r > 4 4 else min 2 r, 4)               or     (4, if c > 4 4 else min 2 c))   => (r,c) reached 

for example,

  mfs (1,1) (5,13) = mfs (1,1) (1,5) -> roll right ->                       mfs (1,1) (1,4) -> roll right -> mfs (1,1) (5,4)    mfs (1,1) (2,13) = mfs (1,1) (1,5) -> roll right ->                       mfs (1,1) (1,4) -> roll right -> mfs (1,1) (2,4) 


properties of dice observed in empirical testing

for target points farther (1,1) (2,3), example (1,1) (3,4) or (1,1) (4,6), minimum path top-face-sum (mfs) equal if  reverse target (r,c). in other words:   1. mfs (1,1) (r,c) == mfs (1,1) (c,r), r,c > 2 

not that.

2. mfs (1,1) (r,c) == mfs (1,1) (r',c'), r,c,r',c' > 2 , r + c == r' + c'    e.g., mfs (1,1) (4,5) == mfs (1,1) (5,4) == mfs (1,1) (3,6) == mfs (1,1) (6,3) 

but here's gets interesting:

the mfs target box (meaning startpoint endpoint) can reduced symmetrical combination of (r,c) (r,c) or (r,c) (c,r),  r,c > 2, can expressed sum of mfs of 2 smaller symmetrical  parts, if die-roll (the change in orientation) between 2 parts  accounted for. in other words, if true, can breakdown calculation smaller parts, much faster.  example:   target-box (1,1) (7,6) can expressed as:   (1,1) (4,3) -> roll right -> (1,1) (4,3) different starting orientation   check it, baby:   mfs  (1,1) (7,6) = mfs (1,1) (4,3) + mfs (1,1) (4,3)   (when accounting change in starting orientation, rolling right in   between)   eq. 2., implies mfs (1,1) (7,6) == mfs (1,1) (5,8)  , mfs (1,1) (5,8) can expressed (1,1) (3,4) -> roll right -> (1,1) (3,4)   check again:   mfs (1,1) (7,6) = mfs (1,1) (5,8) = mfs (1,1) (3,4) + mfs (1,1) (3,4)  (when accounting change in starting orientation, rolling right in  between) 

not that.

the symmetrical parts can apparently combined in way:  3. mfs (1,1) (r,c) -> roll-right -> mfs (1,1) (r,c) equals    mfs (1,1) (r,c) -> roll-right -> mfs (1,1) (c,r) equals    mfs (1,1) (r,c) -> roll-up -> mfs (1,1) (r,c) equals    mfs (1,1) (r,c) -> roll-up -> mfs (1,1) (c,r) equals    mfs (1,1) (2*r-1, 2*c) equals    mfs (1,1) (2*r, 2*c-1), r,c > 2 

Comments

Popular posts from this blog

jquery - How can I dynamically add a browser tab? -

node.js - Getting the socket id,user id pair of a logged in user(s) -

keyboard - C++ GetAsyncKeyState alternative -