1*071d4279SBram Moolenaar" These macros 'solve' any maze produced by the a-maze-ing maze.c program. 2*071d4279SBram Moolenaar" 3*071d4279SBram Moolenaar" First, a bit of maze theory. 4*071d4279SBram Moolenaar" If you were put into a maze, a guaranteed method of finding your way 5*071d4279SBram Moolenaar" out of the maze is to put your left hand onto a wall and just keep walking, 6*071d4279SBram Moolenaar" never taking your hand off the wall. This technique is only guaranteed to 7*071d4279SBram Moolenaar" work if the maze does not have any 'islands', or if the 'exit' is on the 8*071d4279SBram Moolenaar" same island as your starting point. These conditions hold for the mazes 9*071d4279SBram Moolenaar" under consideration. 10*071d4279SBram Moolenaar" 11*071d4279SBram Moolenaar" Assuming that the maze is made up of horizontal and vertical walls spaced 12*071d4279SBram Moolenaar" one step apart and that you can move either north, south, east or west, 13*071d4279SBram Moolenaar" then you can automate this procedure by carrying out the following steps. 14*071d4279SBram Moolenaar" 15*071d4279SBram Moolenaar" 1. Put yourself somewhere in the maze near a wall. 16*071d4279SBram Moolenaar" 2. Check if you have a wall on your left. If so, go to step 4. 17*071d4279SBram Moolenaar" 3. There is no wall on your left, so turn on the spot to your left and step 18*071d4279SBram Moolenaar" forward by one step and repeat step 2. 19*071d4279SBram Moolenaar" 4. Check what is directly in front of you. If it is a wall, turn on the 20*071d4279SBram Moolenaar" spot to your right by 90 degrees and repeat step 4. 21*071d4279SBram Moolenaar" 5. There is no wall in front of you, so step forward one step and 22*071d4279SBram Moolenaar" go to step 2. 23*071d4279SBram Moolenaar" 24*071d4279SBram Moolenaar" In this way you will cover all the corridors of the maze (until you get back 25*071d4279SBram Moolenaar" to where you started from, if you do not stop). 26*071d4279SBram Moolenaar" 27*071d4279SBram Moolenaar" By examining a maze produced by the maze.c program you will see that 28*071d4279SBram Moolenaar" each square of the maze is one character high and two characters wide. 29*071d4279SBram Moolenaar" To go north or south, you move by a one character step, but to move east or 30*071d4279SBram Moolenaar" west you move by a two character step. Also note that in any position 31*071d4279SBram Moolenaar" there are four places where walls could be put - to the north, to the south, 32*071d4279SBram Moolenaar" to the east and to the west. 33*071d4279SBram Moolenaar" A wall exists to the north of you if the character to the north of 34*071d4279SBram Moolenaar" you is a _ (otherwise it is a space). 35*071d4279SBram Moolenaar" A wall exists to the east of you if the character to the east of you 36*071d4279SBram Moolenaar" is a | (otherwise it is a .). 37*071d4279SBram Moolenaar" A wall exists to the west of you if the character to the west of you 38*071d4279SBram Moolenaar" is a | (otherwise it is a .). 39*071d4279SBram Moolenaar" A wall exists to the south of you if the character where you are 40*071d4279SBram Moolenaar" is a _ (otherwise it is a space). 41*071d4279SBram Moolenaar" 42*071d4279SBram Moolenaar" Note the difference for direction south, where we must examine the character 43*071d4279SBram Moolenaar" where the cursor is rather than an adjacent cell. 44*071d4279SBram Moolenaar" 45*071d4279SBram Moolenaar" If you were implementing the above procedure is a normal computer language 46*071d4279SBram Moolenaar" you could use a loop with if statements and continue statements, 47*071d4279SBram Moolenaar" However, these constructs are not available in vi macros so I have used 48*071d4279SBram Moolenaar" a state machine with 8 states. Each state signifies the direction you 49*071d4279SBram Moolenaar" are going in and whether or not you have checked if there is a wall on 50*071d4279SBram Moolenaar" your left. 51*071d4279SBram Moolenaar" 52*071d4279SBram Moolenaar" The transition from state to state and the actions taken on each transition 53*071d4279SBram Moolenaar" are given in the state table below. 54*071d4279SBram Moolenaar" The names of the states are N1, N2, S1, S2, E1, E2, W1, W2, where each letter 55*071d4279SBram Moolenaar" stands for a direction of the compass, the number 1 indicates that the we 56*071d4279SBram Moolenaar" have not yet checked to see if there is a wall on our left and the number 2 57*071d4279SBram Moolenaar" indicates that we have checked and there is a wall on our left. 58*071d4279SBram Moolenaar" 59*071d4279SBram Moolenaar" For each state we must consider the existence or not of a wall in a 60*071d4279SBram Moolenaar" particular direction. This direction is given in the following table. 61*071d4279SBram Moolenaar" 62*071d4279SBram Moolenaar" NextChar table: 63*071d4279SBram Moolenaar" state direction vi commands 64*071d4279SBram Moolenaar" N1 W hF 65*071d4279SBram Moolenaar" N2 N kF 66*071d4279SBram Moolenaar" S1 E lF 67*071d4279SBram Moolenaar" S2 S F 68*071d4279SBram Moolenaar" E1 N kF 69*071d4279SBram Moolenaar" E2 E lF 70*071d4279SBram Moolenaar" W1 S F 71*071d4279SBram Moolenaar" W2 W hF 72*071d4279SBram Moolenaar" 73*071d4279SBram Moolenaar" where F is a macro which yanks the character under the cursor into 74*071d4279SBram Moolenaar" the NextChar register (n). 75*071d4279SBram Moolenaar" 76*071d4279SBram Moolenaar" State table: 77*071d4279SBram Moolenaar" In the 'vi commands' column is given the actions to carry out when in 78*071d4279SBram Moolenaar" this state and the NextChar is as given. The commands k, j, ll, hh move 79*071d4279SBram Moolenaar" the current position north, south, east and west respectively. The 80*071d4279SBram Moolenaar" command mm is used as a no-op command. 81*071d4279SBram Moolenaar" In the 'next state' column is given the new state of the machine after 82*071d4279SBram Moolenaar" the action is carried out. 83*071d4279SBram Moolenaar" 84*071d4279SBram Moolenaar" current state NextChar vi commands next state 85*071d4279SBram Moolenaar" N1 . hh W1 86*071d4279SBram Moolenaar" N1 | mm N2 87*071d4279SBram Moolenaar" N2 _ mm E1 88*071d4279SBram Moolenaar" N2 space k N1 89*071d4279SBram Moolenaar" S1 . ll E1 90*071d4279SBram Moolenaar" S1 | mm S2 91*071d4279SBram Moolenaar" S2 _ mm W1 92*071d4279SBram Moolenaar" S2 space j S1 93*071d4279SBram Moolenaar" E1 space k N1 94*071d4279SBram Moolenaar" E1 _ mm E2 95*071d4279SBram Moolenaar" E2 | mm S1 96*071d4279SBram Moolenaar" E2 . ll E1 97*071d4279SBram Moolenaar" W1 space j S1 98*071d4279SBram Moolenaar" W1 _ mm W2 99*071d4279SBram Moolenaar" W2 | mm N1 100*071d4279SBram Moolenaar" W2 . hh W1 101*071d4279SBram Moolenaar" 102*071d4279SBram Moolenaar" 103*071d4279SBram Moolenaar" Complaint about vi macros: 104*071d4279SBram Moolenaar" It seems that you cannot have more than one 'undo-able' vi command 105*071d4279SBram Moolenaar" in the one macro, so you have to make lots of little macros and 106*071d4279SBram Moolenaar" put them together. 107*071d4279SBram Moolenaar" 108*071d4279SBram Moolenaar" I'll explain what I mean by an example. Edit a file and 109*071d4279SBram Moolenaar" type ':map Q rXY'. This should map the Q key to 'replace the 110*071d4279SBram Moolenaar" character under the cursor with X and yank the line'. 111*071d4279SBram Moolenaar" But when I type Q, vi tells me 'Can't yank inside global/macro' and 112*071d4279SBram Moolenaar" goes into ex mode. However if I type ':map Q rXT' and ':map T Y', 113*071d4279SBram Moolenaar" everything is OK. I`m doing all this on a Sparcstation. 114*071d4279SBram Moolenaar" If anyone reading this has an answer to this problem, the author would 115*071d4279SBram Moolenaar" love to find out. Mail to [email protected]. 116*071d4279SBram Moolenaar" 117*071d4279SBram Moolenaar" The macros: 118*071d4279SBram Moolenaar" The macro to run the maze solver is 'g'. This simply calls two other 119*071d4279SBram Moolenaar" macros: I, to initialise everything, and L, to loop forever running 120*071d4279SBram Moolenaar" through the state table. 121*071d4279SBram Moolenaar" Both of these macros are long sequences of calls to other macros. All 122*071d4279SBram Moolenaar" of these other macros are quite simple and so to understand how this 123*071d4279SBram Moolenaar" works, all you need to do is examine macros I and L and learn what they 124*071d4279SBram Moolenaar" do (a simple sequence of vi actions) and how L loops (by calling U, which 125*071d4279SBram Moolenaar" simply calls L again). 126*071d4279SBram Moolenaar" 127*071d4279SBram Moolenaar" Macro I sets up the state table and NextChar table at the end of the file. 128*071d4279SBram Moolenaar" Macro L then searches these tables to find out what actions to perform and 129*071d4279SBram Moolenaar" what state changes to make. 130*071d4279SBram Moolenaar" 131*071d4279SBram Moolenaar" The entries in the state table all begin with a key consisting of the 132*071d4279SBram Moolenaar" letter 's', the current state and the NextChar. After this is the 133*071d4279SBram Moolenaar" action to take in this state and after this is the next state to change to. 134*071d4279SBram Moolenaar" 135*071d4279SBram Moolenaar" The entries in the NextChar table begin with a key consisting of the 136*071d4279SBram Moolenaar" letter 'n' and the current state. After this is the action to take to 137*071d4279SBram Moolenaar" obtain NextChar - the character that must be examined to change state. 138*071d4279SBram Moolenaar" 139*071d4279SBram Moolenaar" One way to see what each part of the macros is doing is to type in the 140*071d4279SBram Moolenaar" body of the macros I and L manually (instead of typing 'g') and see 141*071d4279SBram Moolenaar" what happens at each step. 142*071d4279SBram Moolenaar" 143*071d4279SBram Moolenaar" Good luck. 144*071d4279SBram Moolenaar" 145*071d4279SBram Moolenaar" Registers used by the macros: 146*071d4279SBram Moolenaar" s (State) - holds the state the machine is in 147*071d4279SBram Moolenaar" c (Char) - holds the character under the current position 148*071d4279SBram Moolenaar" m (Macro) - holds a vi command string to be executed later 149*071d4279SBram Moolenaar" n (NextChar) - holds the character we must examine to change state 150*071d4279SBram Moolenaar" r (Second Macro) - holds a second vi command string to be executed later 151*071d4279SBram Moolenaar" 152*071d4279SBram Moolenaarset remap 153*071d4279SBram Moolenaarset nomagic 154*071d4279SBram Moolenaarset noterse 155*071d4279SBram Moolenaarset wrapscan 156*071d4279SBram Moolenaar" 157*071d4279SBram Moolenaar"================================================================ 158*071d4279SBram Moolenaar" g - go runs the whole show 159*071d4279SBram Moolenaar" I - initialise 160*071d4279SBram Moolenaar" L - then loop forever 161*071d4279SBram Moolenaarmap g IL 162*071d4279SBram Moolenaar" 163*071d4279SBram Moolenaar"================================================================ 164*071d4279SBram Moolenaar" I - initialise everything before running the loop 165*071d4279SBram Moolenaar" G$?.^M - find the last . in the maze 166*071d4279SBram Moolenaar" ^ - replace it with an X (the goal) 167*071d4279SBram Moolenaar" GYKeDP - print the state table and next char table at the end of the file 168*071d4279SBram Moolenaar" 0S - initialise the state of the machine to E1 169*071d4279SBram Moolenaar" 2Gl - move to the top left cell of the maze 170*071d4279SBram Moolenaarmap I G$?. 171*071d4279SBram Moolenaar^GYKeDP0S2Gl 172*071d4279SBram Moolenaar" 173*071d4279SBram Moolenaar"================================================================ 174*071d4279SBram Moolenaar" L - the loop which is executed forever 175*071d4279SBram Moolenaar" Q - save the current character in the Char register 176*071d4279SBram Moolenaar" A - replace the current character with an 'O' 177*071d4279SBram Moolenaar" ma - mark the current position with mark 'a' 178*071d4279SBram Moolenaar" GNB - on bottom line, create a command to search the NextChar table 179*071d4279SBram Moolenaar" for the current state 180*071d4279SBram Moolenaar" 0M0E@m^M - yank the command into the Macro register and execute it 181*071d4279SBram Moolenaar" wX - we have now found the entry in the table, now yank the 182*071d4279SBram Moolenaar" following word into the Macro register 183*071d4279SBram Moolenaar" `a@m - go back to the current position and execute the macro, this will 184*071d4279SBram Moolenaar" yank the NextChar in register n 185*071d4279SBram Moolenaar" GT$B$R - on bottom line, create a command to search the state table 186*071d4279SBram Moolenaar" for the current state and NextChar 187*071d4279SBram Moolenaar" 0M0E@m^M - yank the command into the Macro register and execute it 188*071d4279SBram Moolenaar" 2WS - we have now found the entry in the table, now yank the 189*071d4279SBram Moolenaar" next state into the State macro 190*071d4279SBram Moolenaar" bX - and yank the action corresponding to this state table entry 191*071d4279SBram Moolenaar" into the Macro register 192*071d4279SBram Moolenaar" GVJ - on bottom line, create a command to restore the current character 193*071d4279SBram Moolenaar" 0H - and save the command into the second Macro register 194*071d4279SBram Moolenaar" `a@r - go back to the current position and exectute the macro to restore 195*071d4279SBram Moolenaar" the current character 196*071d4279SBram Moolenaar" @m - execute the action associated with this state 197*071d4279SBram Moolenaar" U - and repeat 198*071d4279SBram Moolenaarmap L QAmaGNB0M0E@m 199*071d4279SBram MoolenaarwX`a@mGT$B$R0M0E@m 200*071d4279SBram Moolenaar2WSbXGVJ0H`a@r@mU 201*071d4279SBram Moolenaar" 202*071d4279SBram Moolenaar"================================================================ 203*071d4279SBram Moolenaar" U - no tail recursion allowed in vi macros so cheat and set U = L 204*071d4279SBram Moolenaarmap U L 205*071d4279SBram Moolenaar" 206*071d4279SBram Moolenaar"================================================================ 207*071d4279SBram Moolenaar" S - yank the next two characters into the State register 208*071d4279SBram Moolenaarmap S "sy2l 209*071d4279SBram Moolenaar" 210*071d4279SBram Moolenaar"================================================================ 211*071d4279SBram Moolenaar" Q - save the current character in the Char register 212*071d4279SBram Moolenaarmap Q "cyl 213*071d4279SBram Moolenaar" 214*071d4279SBram Moolenaar"================================================================ 215*071d4279SBram Moolenaar" A - replace the current character with an 'O' 216*071d4279SBram Moolenaarmap A rO 217*071d4279SBram Moolenaar" 218*071d4279SBram Moolenaar"================================================================ 219*071d4279SBram Moolenaar" N - replace this line with the string 'n' 220*071d4279SBram Moolenaarmap N C/n 221*071d4279SBram Moolenaar" 222*071d4279SBram Moolenaar"================================================================ 223*071d4279SBram Moolenaar" B - put the current state 224*071d4279SBram Moolenaarmap B "sp 225*071d4279SBram Moolenaar" 226*071d4279SBram Moolenaar"================================================================ 227*071d4279SBram Moolenaar" M - yank this line into the Macro register 228*071d4279SBram Moolenaarmap M "my$ 229*071d4279SBram Moolenaar" 230*071d4279SBram Moolenaar"================================================================ 231*071d4279SBram Moolenaar" E - delete to the end of the line 232*071d4279SBram Moolenaarmap E d$ 233*071d4279SBram Moolenaar" 234*071d4279SBram Moolenaar"================================================================ 235*071d4279SBram Moolenaar" X - yank this word into the Macro register 236*071d4279SBram Moolenaarmap X "myt 237*071d4279SBram Moolenaar" 238*071d4279SBram Moolenaar"================================================================ 239*071d4279SBram Moolenaar" T - replace this line with the string 's' 240*071d4279SBram Moolenaarmap T C/s 241*071d4279SBram Moolenaar" 242*071d4279SBram Moolenaar"================================================================ 243*071d4279SBram Moolenaar" R - put NextChar 244*071d4279SBram Moolenaarmap R "np 245*071d4279SBram Moolenaar" 246*071d4279SBram Moolenaar"================================================================ 247*071d4279SBram Moolenaar" V - add the letter 'r' (the replace vi command) 248*071d4279SBram Moolenaarmap V ar 249*071d4279SBram Moolenaar" 250*071d4279SBram Moolenaar"================================================================ 251*071d4279SBram Moolenaar" J - restore the current character 252*071d4279SBram Moolenaarmap J "cp 253*071d4279SBram Moolenaar" 254*071d4279SBram Moolenaar"================================================================ 255*071d4279SBram Moolenaar" H - yank this line into the second Macro register 256*071d4279SBram Moolenaarmap H "ry$ 257*071d4279SBram Moolenaar" 258*071d4279SBram Moolenaar"================================================================ 259*071d4279SBram Moolenaar" F - yank NextChar (this macro is called from the Macro register) 260*071d4279SBram Moolenaarmap F "nyl 261*071d4279SBram Moolenaar" 262*071d4279SBram Moolenaar"================================================================ 263*071d4279SBram Moolenaar" ^ - replace the current character with an 'X' 264*071d4279SBram Moolenaarmap ^ rX 265*071d4279SBram Moolenaar" 266*071d4279SBram Moolenaar"================================================================ 267*071d4279SBram Moolenaar" YKeDP - create the state table, NextChar table and initial state 268*071d4279SBram Moolenaar" Note that you have to escape the bar character, since it is special to 269*071d4279SBram Moolenaar" the map command (it indicates a new line). 270*071d4279SBram Moolenaarmap Y osE1 k N1 sE1_ mm E2 sE2| mm S1 sE2. ll E1 271*071d4279SBram Moolenaarmap K osW1 j S1 sW1_ mm W2 sW2| mm N1 sW2. hh W1 272map e osN1. hh W1 sN1| mm N2 sN2 k N1 sN2_ mm E1 273map D osS1. ll E1 sS1| mm S2 sS2 j S1 sS2_ mm W1 274map P onE1 kF nE2 lF nW1 G$JF nW2 hF nN1 hF nN2 kF nS1 lF nS2 G$JF 275E1 276