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