1 /*
2 * This file contains the maze-solving strategy.
3 */
4
5 #include <pololu/3pi.h>
6 #include "follow-segment.h"
7 #include "turn.h"
8
9 // The path variable will store the path that the robot has taken. It
10 // is stored as an array of characters, each of which represents the
11 // turn that should be made at one intersection in the sequence:
12 // 'L' for left
13 // 'R' for right
14 // 'S' for straight (going straight through an intersection)
15 // 'B' for back (U-turn)
16 //
17 // Whenever the robot makes a U-turn, the path can be simplified by
18 // removing the dead end. The follow_next_turn() function checks for
19 // this case every time it makes a turn, and it simplifies the path
20 // appropriately.
21 char path[100] = "";
22 unsigned char path_length = 0; // the length of the path
23
24 // Displays the current path on the LCD, using two rows if necessary.
display_path()25 void display_path()
26 {
27 // Set the last character of the path to a 0 so that the print()
28 // function can find the end of the string. This is how strings
29 // are normally terminated in C.
30 path[path_length] = 0;
31
32 clear();
33 print(path);
34
35 if(path_length > 8)
36 {
37 lcd_goto_xy(0,1);
38 print(path+8);
39 }
40 }
41
42 // This function decides which way to turn during the learning phase of
43 // maze solving. It uses the variables found_left, found_straight, and
44 // found_right, which indicate whether there is an exit in each of the
45 // three directions, applying the "left hand on the wall" strategy.
select_turn(unsigned char found_left,unsigned char found_straight,unsigned char found_right)46 char select_turn(unsigned char found_left, unsigned char found_straight, unsigned char found_right)
47 {
48 // Make a decision about how to turn. The following code
49 // implements a left-hand-on-the-wall strategy, where we always
50 // turn as far to the left as possible.
51 if(found_left)
52 return 'L';
53 else if(found_straight)
54 return 'S';
55 else if(found_right)
56 return 'R';
57 else
58 return 'B';
59 }
60
61 // Path simplification. The strategy is that whenever we encounter a
62 // sequence xBx, we can simplify it by cutting out the dead end. For
63 // example, LBL -> S, because a single S bypasses the dead end
64 // represented by LBL.
simplify_path()65 void simplify_path()
66 {
67 // only simplify the path if the second-to-last turn was a 'B'
68 if(path_length < 3 || path[path_length-2] != 'B')
69 return;
70
71 int total_angle = 0;
72 int i;
73 for(i=1;i<=3;i++)
74 {
75 switch(path[path_length-i])
76 {
77 case 'R':
78 total_angle += 90;
79 break;
80 case 'L':
81 total_angle += 270;
82 break;
83 case 'B':
84 total_angle += 180;
85 break;
86 }
87 }
88
89 // Get the angle as a number between 0 and 360 degrees.
90 total_angle = total_angle % 360;
91
92 // Replace all of those turns with a single one.
93 switch(total_angle)
94 {
95 case 0:
96 path[path_length - 3] = 'S';
97 break;
98 case 90:
99 path[path_length - 3] = 'R';
100 break;
101 case 180:
102 path[path_length - 3] = 'B';
103 break;
104 case 270:
105 path[path_length - 3] = 'L';
106 break;
107 }
108
109 // The path is now two steps shorter.
110 path_length -= 2;
111 }
112
113 // This function is called once, from main.c.
maze_solve()114 void maze_solve()
115 {
116 // Loop until we have solved the maze.
117 while(1)
118 {
119 // FIRST MAIN LOOP BODY
120 follow_segment();
121
122 // Drive straight a bit. This helps us in case we entered the
123 // intersection at an angle.
124 // Note that we are slowing down - this prevents the robot
125 // from tipping forward too much.
126 set_motors(50,50);
127 delay_ms(50);
128
129 // These variables record whether the robot has seen a line to the
130 // left, straight ahead, and right, whil examining the current
131 // intersection.
132 unsigned char found_left=0;
133 unsigned char found_straight=0;
134 unsigned char found_right=0;
135
136 // Now read the sensors and check the intersection type.
137 unsigned int sensors[5];
138 read_line(sensors,IR_EMITTERS_ON);
139
140 // Check for left and right exits.
141 if(sensors[0] > 100)
142 found_left = 1;
143 if(sensors[4] > 100)
144 found_right = 1;
145
146 // Drive straight a bit more - this is enough to line up our
147 // wheels with the intersection.
148 set_motors(40,40);
149 delay_ms(200);
150
151 // Check for a straight exit.
152 read_line(sensors,IR_EMITTERS_ON);
153 if(sensors[1] > 200 || sensors[2] > 200 || sensors[3] > 200)
154 found_straight = 1;
155
156 // Check for the ending spot.
157 // If all three middle sensors are on dark black, we have
158 // solved the maze.
159 if(sensors[1] > 600 && sensors[2] > 600 && sensors[3] > 600)
160 break;
161
162 // Intersection identification is complete.
163 // If the maze has been solved, we can follow the existing
164 // path. Otherwise, we need to learn the solution.
165 unsigned char dir = select_turn(found_left, found_straight, found_right);
166
167 // Make the turn indicated by the path.
168 turn(dir);
169
170 // Store the intersection in the path variable.
171 path[path_length] = dir;
172 path_length ++;
173
174 // You should check to make sure that the path_length does not
175 // exceed the bounds of the array. We'll ignore that in this
176 // example.
177
178 // Simplify the learned path.
179 simplify_path();
180
181 // Display the path on the LCD.
182 display_path();
183 }
184
185 // Solved the maze!
186
187 // Now enter an infinite loop - we can re-run the maze as many
188 // times as we want to.
189 while(1)
190 {
191 // Beep to show that we finished the maze.
192 set_motors(0,0);
193 play(">>a32");
194
195 // Wait for the user to press a button, while displaying
196 // the solution.
197 while(!button_is_pressed(BUTTON_B))
198 {
199 if(get_ms() % 2000 < 1000)
200 {
201 clear();
202 print("Solved!");
203 lcd_goto_xy(0,1);
204 print("Press B");
205 }
206 else
207 display_path();
208 delay_ms(30);
209 }
210 while(button_is_pressed(BUTTON_B));
211
212 delay_ms(1000);
213
214 // Re-run the maze. It's not necessary to identify the
215 // intersections, so this loop is really simple.
216 int i;
217 for(i=0;i<path_length;i++)
218 {
219 // SECOND MAIN LOOP BODY
220 follow_segment();
221
222 // Drive straight while slowing down, as before.
223 set_motors(50,50);
224 delay_ms(50);
225 set_motors(40,40);
226 delay_ms(200);
227
228 // Make a turn according to the instruction stored in
229 // path[i].
230 turn(path[i]);
231 }
232
233 // Follow the last segment up to the finish.
234 follow_segment();
235
236 // Now we should be at the finish! Restart the loop.
237 }
238 }
239
240 // Local Variables: **
241 // mode: C **
242 // c-basic-offset: 4 **
243 // tab-width: 4 **
244 // indent-tabs-mode: t **
245 // end: **
246