SHOW:
|
|
- or go back to the newest paste.
1 | /** | |
2 | * | |
3 | * Homework 4 - Water Jug Problem | |
4 | * author: @rgadhia - Raj Gadhia and Albert Chen | |
5 | * Pledge: I pledge my honor that I have abided by the Stevens Honor System. | |
6 | * Date: 10/08/19 | |
7 | */ | |
8 | ||
9 | #include <iostream> | |
10 | #include <vector> | |
11 | #include <algorithm> | |
12 | #include <sstream> | |
13 | #include <iomanip> | |
14 | #include <list> | |
15 | ||
16 | using namespace std; | |
17 | ||
18 | // Struct to represent state of water in the jugs. | |
19 | struct State { | |
20 | int a, b, c; | |
21 | vector<string> directions; | |
22 | ||
23 | State(int _a, int _b, int _c) : a(_a), b(_b), c(_c) { } | |
24 | ||
25 | // String representation of state in tuple form. | |
26 | string to_string() { | |
27 | ostringstream oss; | |
28 | oss << "(" << a << ", " << b << ", " << c << ")"; | |
29 | return oss.str(); | |
30 | } | |
31 | }; | |
32 | ||
33 | //if been visited already, dont put it in the queue | |
34 | //break outta queue while not empty loop when u find the goal | |
35 | //if the queue is empty, return 0 | |
36 | //function that returns a vector that contains all the possible states u can go from current | |
37 | //vectors of strings in the state class: add the vector after every move | |
38 | ||
39 | //dhorowit@stevens.edu | |
40 | ||
41 | //returns a vector of the possible moves for that state | |
42 | vector<State> possiblepours(State s, int capA, int capB, int capC) { | |
43 | vector<State> possible; | |
44 | //C to A | |
45 | if (s.a != capA && s.c != 0) | |
46 | { | |
47 | State temp(s.a,s.b,s.c); | |
48 | int pamount; | |
49 | temp.directions = s.directions; | |
50 | if (temp.c + temp.a <= capA) | |
51 | { | |
52 | pamount = temp.c; | |
53 | temp.a += pamount; | |
54 | temp.c = 0; | |
55 | } | |
56 | else | |
57 | { | |
58 | pamount = capA - temp.a; | |
59 | temp.a += pamount; | |
60 | temp.c -= pamount; | |
61 | } | |
62 | if(pamount == 1){ | |
63 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from C to A. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
64 | } else { | |
65 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from C to A. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
66 | } | |
67 | possible.push_back(temp); | |
68 | } | |
69 | //B to A | |
70 | if (s.a != capA && s.b != 0) | |
71 | { | |
72 | State temp(s.a,s.b,s.c); | |
73 | temp.directions = s.directions; | |
74 | int pamount; | |
75 | if (temp.b + temp.a <= capA) | |
76 | { | |
77 | pamount = temp.b; | |
78 | temp.a += pamount; | |
79 | temp.b = 0; | |
80 | } | |
81 | else | |
82 | { | |
83 | pamount = capA - temp.a; | |
84 | temp.a += pamount; | |
85 | temp.b -= pamount; | |
86 | } | |
87 | if(pamount == 1){ | |
88 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from B to A. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
89 | } else { | |
90 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from B to A. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
91 | } | |
92 | possible.push_back(temp); | |
93 | } | |
94 | //C to B | |
95 | if (s.b != capB && s.c != 0) // (0,0,8) -> (0,5,3) | |
96 | { | |
97 | State temp(s.a,s.b,s.c); | |
98 | temp.directions = s.directions; | |
99 | int pamount; | |
100 | if (temp.c + temp.b <= capB) | |
101 | { | |
102 | pamount = temp.c; | |
103 | temp.b += pamount; | |
104 | temp.c = 0; | |
105 | } | |
106 | else | |
107 | { | |
108 | pamount = capB - temp.b; | |
109 | temp.b += pamount; | |
110 | temp.c -= pamount; | |
111 | } | |
112 | if(pamount == 1){ | |
113 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from C to B. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
114 | } else { | |
115 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from C to B. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
116 | } | |
117 | possible.push_back(temp); | |
118 | } | |
119 | //A to B | |
120 | if (s.b != capB && s.a != 0) | |
121 | { | |
122 | State temp(s.a,s.b,s.c); | |
123 | temp.directions = s.directions; | |
124 | int pamount; | |
125 | if (temp.a + temp.b <= capB) | |
126 | { | |
127 | pamount = temp.a; | |
128 | temp.b += pamount; | |
129 | temp.a = 0; | |
130 | } | |
131 | else | |
132 | { | |
133 | pamount = capB - temp.b; | |
134 | temp.b += pamount; | |
135 | temp.a -= pamount; | |
136 | } | |
137 | if(pamount == 1){ | |
138 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from A to B. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
139 | } else { | |
140 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from A to B. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
141 | } | |
142 | possible.push_back(temp); | |
143 | } | |
144 | //B to C | |
145 | if (s.c != capC && s.b != 0) | |
146 | { | |
147 | State temp(s.a,s.b,s.c); | |
148 | temp.directions = s.directions; | |
149 | int pamount; | |
150 | if (temp.b + temp.c <= capC) | |
151 | { | |
152 | pamount = temp.b; | |
153 | temp.c += pamount; | |
154 | temp.b = 0; | |
155 | } | |
156 | else | |
157 | { | |
158 | pamount = capC - temp.c; | |
159 | temp.c += pamount; | |
160 | temp.b -= pamount; | |
161 | } | |
162 | if(pamount == 1){ | |
163 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from B to C. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
164 | } else { | |
165 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from B to C. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
166 | } | |
167 | possible.push_back(temp); | |
168 | } | |
169 | //A to C | |
170 | if (s.c != capC && s.a != 0) | |
171 | { | |
172 | State temp(s.a,s.b,s.c); | |
173 | int pamount; | |
174 | temp.directions = s.directions; | |
175 | if (temp.a + temp.c <= capC) | |
176 | { | |
177 | pamount = temp.a; | |
178 | temp.c += pamount; | |
179 | temp.a = 0; | |
180 | } | |
181 | else | |
182 | { | |
183 | pamount = capC - temp.c; | |
184 | temp.c += pamount; | |
185 | temp.a -= pamount; | |
186 | } | |
187 | if(pamount == 1){ | |
188 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallon from A to C. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
189 | } else { | |
190 | temp.directions.push_back("Pour " + std::to_string(pamount)+ " gallons from A to C. " + "(" + std::to_string(temp.a) + ", " + std::to_string(temp.b) + ", " + std::to_string(temp.c) + ")\n"); | |
191 | } | |
192 | possible.push_back(temp); | |
193 | } | |
194 | return possible; | |
195 | } | |
196 | ||
197 | State breadthSearch (vector<int> nums) { | |
198 | int cols = nums[0]+1; | |
199 | int rows = nums[1]+1; | |
200 | bool** visited = new bool*[rows]; | |
201 | // go through rows filling / visiting | |
202 | for (int i=0; i<rows; i++) | |
203 | { | |
204 | visited[i] = new bool[cols]; | |
205 | fill(visited[i], visited[i] + cols, false); | |
206 | } | |
207 | ||
208 | list<State> queue; | |
209 | vector<State> moves; | |
210 | State start(0, 0, nums[2]); | |
211 | State goal(nums[3], nums[4], nums[5]); | |
212 | State current(0, 0, 0); | |
213 | queue.push_back(start); | |
214 | while (!(queue.empty())) { | |
215 | current = queue.front(); //extracts first element of queue | |
216 | queue.pop_front(); //pops it out | |
217 | ||
218 | if (current.a == goal.a && current.b == goal.b && current.c == goal.c) | |
219 | { | |
220 | queue.push_back(current); | |
221 | break; | |
222 | } | |
223 | else | |
224 | { | |
225 | moves = possiblepours(current, nums[0], nums[1], nums[2]); | |
226 | for (int i = 0; i < (int)moves.size(); i++) | |
227 | { | |
228 | current = moves[i]; | |
229 | if (current.a == goal.a && current.b == goal.b && current.c == goal.c) | |
230 | { | |
231 | ||
232 | for (int i = 0; i < cols; i++) | |
233 | { | |
234 | delete [] visited[i]; | |
235 | } | |
236 | delete [] visited; | |
237 | ||
238 | return current; | |
239 | } | |
240 | if (visited[current.a][current.b] == false) | |
241 | { | |
242 | queue.push_back(current); | |
243 | visited[current.a][current.b] = true; | |
244 | } | |
245 | } | |
246 | } | |
247 | } | |
248 | for (int i = 0; i < cols; i++) { | |
249 | delete [] visited[i]; | |
250 | } | |
251 | ||
252 | delete [] visited; | |
253 | ||
254 | if (queue.empty()) { | |
255 | return State(-1, -1, -1); | |
256 | } | |
257 | return current; | |
258 | } | |
259 | ||
260 | int main(int argc, char * const argv[]) { | |
261 | vector<int> nums; | |
262 | ||
263 | //user input should only have seven arguments | |
264 | if (argc != 7) | |
265 | { | |
266 | cout << "Usage: ./waterjugpuzzle <cap A> <cap B> <cap C> <goal A> <goal B> <goal C>" << endl; | |
267 | return 0; | |
268 | } | |
269 | ||
270 | // ensure there is no error for integer capacity of jugs | |
271 | // whenever there is no error, we pushback the capacity and goals into the nums vector | |
272 | for (int i = 1; i < 4; i++) { | |
273 | istringstream iss(argv[i]); | |
274 | int isnum; | |
275 | // if assigning iss to isnum fails, that means it's not an integer | |
276 | if (!(iss>>isnum) || isnum <= 0) | |
277 | { | |
278 | if (i == 1) | |
279 | { | |
280 | cout << "Error: Invalid capacity '" << argv[i] << "' for jug A." << endl; | |
281 | } | |
282 | if (i == 2) | |
283 | { | |
284 | cout << "Error: Invalid capacity '" << argv[i] << "' for jug B." << endl; | |
285 | } | |
286 | if (i == 3) | |
287 | { | |
288 | cout << "Error: Invalid capacity '" << argv[i] << "' for jug C." << endl; | |
289 | } | |
290 | iss.clear(); | |
291 | return 1; | |
292 | } | |
293 | nums.push_back(isnum); | |
294 | iss.clear(); | |
295 | } | |
296 | for (int i = 4; i < 7; i++) | |
297 | { | |
298 | istringstream iss(argv[i]); | |
299 | int isnum; | |
300 | if (!(iss>>isnum) || isnum < 0) | |
301 | { | |
302 | if (i == 4) | |
303 | { | |
304 | cout << "Error: Invalid goal '" << argv[i] << "' for jug A." << endl; | |
305 | } | |
306 | if (i == 5) | |
307 | { | |
308 | cout << "Error: Invalid goal '" << argv[i] << "' for jug B." << endl; | |
309 | } | |
310 | if (i == 6) | |
311 | { | |
312 | cout << "Error: Invalid goal '" << argv[i] << "' for jug C." << endl; | |
313 | } | |
314 | iss.clear(); | |
315 | return 1; | |
316 | } | |
317 | nums.push_back(isnum); | |
318 | iss.clear(); | |
319 | } | |
320 | for (int i = 4; i < 7; i++) | |
321 | { | |
322 | if (nums[i-1] > nums[i-4]) | |
323 | { | |
324 | if (i == 4) | |
325 | { | |
326 | cout << "Error: Goal cannot exceed capacity of jug A." << endl; | |
327 | } | |
328 | if (i == 5) | |
329 | { | |
330 | cout << "Error: Goal cannot exceed capacity of jug B." << endl; | |
331 | } | |
332 | if (i == 6) | |
333 | { | |
334 | cout << "Error: Goal cannot exceed capacity of jug C." << endl; | |
335 | } | |
336 | return 1; | |
337 | } | |
338 | } | |
339 | if (!(nums[3] + nums[4] + nums[5] == nums[2])) | |
340 | { | |
341 | cout << "Error: Total gallons in goal state must be equal to the capacity of jug C." << endl; | |
342 | return 0; | |
343 | } | |
344 | ||
345 | //---------------------------------------------------------------------------------------------------- | |
346 | // Do the BFS if there are no errors | |
347 | ||
348 | State found = breadthSearch(nums); | |
349 | ||
350 | if(found.a < 0){ | |
351 | cout << "No solution."; | |
352 | return 1; | |
353 | } | |
354 | ||
355 | cout << "Initial state. (0, 0, " << nums[2] <<")" << endl; | |
356 | ||
357 | for (int i = 0; i < (int) found.directions.size(); i++) { | |
358 | cout << found.directions[i]; | |
359 | } | |
360 | ||
361 | return 0; | |
362 | } |