]> git.cworth.org Git - rrsolve/blob - src/rrsolve.c
Always issue NOBID after bidding. Fixed to use game from command line.
[rrsolve] / src / rrsolve.c
1 /* rrsolve - Simple RR solver and librr client.
2  *
3  * Copyright © 2003 Carl Worth
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of Carl Worth
10  * not be used in advertising or publicity pertaining to distribution
11  * of the software without specific, written prior permission.
12  * Carl Worth makes no representations about the suitability of this
13  * software for any purpose.  It is provided "as is" without express
14  * or implied warranty.
15  * 
16  * CARL WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18  * NO EVENT SHALL CARL WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: Carl Worth <carl@theworths.org>
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/time.h>
31 #include <time.h>
32
33 #include "rrsolve.h"
34
35 /* Tuning this can reduce excess reallocs */
36 #define RRS_BRANCHING_FACTOR_ESTIMATE 10
37
38 #define RRS_STATE_SET_ROBOT(s, ri, x, y) ((s) |= (((y) << ((ri)<<3)) | ((x) << (((ri)<<3) + 4))))
39 #define RRS_STATE_GET_ROBOT(s, ri, x, y) { (y) = ((s) >> ((ri)<<3)) & 0xf; (x) = ((s) >> (((ri)<<3) + 4)) & 0xf; }
40
41 static void
42 handle_events (rr_client_t *client);
43
44 static rrs_state_t
45 rrs_state_get_from_board (rr_board_t *board);
46
47 static void
48 rrs_solution_print (rrs_solution_t *solution);
49
50 static rr_status_t
51 solve_board (rr_board_t *board, rrs_solution_t *solution);
52
53 static rr_status_t
54 find_solution_states (rr_board_t *board,
55                       rrs_state_buf_t ***solution_states,
56                       int *num_state_buf,
57                       rrs_state_t *final_state);
58
59 static int
60 rrs_find_new_states (rr_board_t *board,
61                      rrs_state_buf_t **previous,
62                      int moves,
63                      rrs_state_buf_t *new,
64                      rrs_state_t *solution_state);
65
66 static void
67 trace_solution (rr_board_t *board,
68                 rrs_state_buf_t **states, int moves,
69                 rrs_state_t solution_state,
70                 rrs_solution_t *solution);
71
72 int
73 main (int argc, char *argv[])
74 {
75     args_t args;
76     rr_status_t status;
77     rr_client_t *client;
78
79     args_parse (&args, argc, argv);
80
81     if (args.files) {
82         int i;
83         rr_board_t *board;
84         rrs_solution_t solution;
85
86         for (i = 0; args.files[i]; i++) {
87             board = rr_board_create_from_file (args.files[i]);
88             if (board == NULL)
89                 continue;
90             rrs_solution_init (&solution);
91             solve_board (board, &solution);
92             rrs_solution_print (&solution);
93             rrs_solution_fini (&solution);
94             rr_board_destroy (board);
95         }
96     } else {
97         client = rr_client_create (args.host, args.port, args.user);
98         if (client == NULL) {
99             fprintf (stderr, "Failed connecting to %s:%s as %s\n",
100                      args.host, args.port, args.user);
101             return 1;
102         }
103
104         status = rr_client_join (client, args.game);
105         if (status == RR_STATUS_NO_GAME)
106             status = rr_client_new (client, args.game);
107         if (status) {
108             fprintf (stderr, "Error joining or creating game: %s\n", rr_status_str (status));
109             return 1;
110         }
111
112         handle_events (client);
113
114         rr_client_destroy (client);
115     }
116
117     return 0;
118 }
119
120 static void
121 handle_events (rr_client_t *client)
122 {
123     int i;
124     rr_status_t status;
125     rr_notice_t *notice;
126     rrs_solution_t solution;
127     rr_board_t *board;
128     char *diagram;
129     struct timespec move_delay = { 1, 200000000l };
130
131     /* XXX: This block of code can go away when add a NOTICE BOARD
132        for new users joining a game. */
133     {
134         status = rr_client_show (client, &diagram);
135         if (status) {
136             fprintf (stderr, "Error in rr_client_show: %s\n", rr_status_str (status));
137             goto DONE;
138         }
139         board = rr_board_create_from_str (diagram);
140         free (diagram);
141     }
142
143     /* XXX: This block of code can go away when we add a NOTICE TURN
144        for new users joining a game in progress. */
145     {
146         rrs_solution_init (&solution);
147         solve_board (board, &solution);
148         rr_client_bid (client, solution.num_moves);
149         rr_client_nobid (client);
150     }
151
152     while (1) {
153         status = rr_client_next_notice (client, &notice);
154         if (status) {
155             if (status == RR_STATUS_EOF)
156                 fprintf (stderr, "Server has disconnected. Exiting.\n");
157             else
158                 fprintf (stderr, "ERROR during rr_client_next_notice: %s\n",
159                          rr_status_str (status));
160             return;
161         }
162         if (!notice) {
163             fprintf (stderr, "No notice during rr_client_next_notice\n");
164             continue;
165         }
166
167         switch  (notice->type) {
168         /* XXX: The processing needed for GAMEOVER, JOIN, and TURN
169            is a mess right now. There should be one NOTICE to say
170            the board has changed and one to say a TURN has started,
171            rather than the current mess. */
172         case RR_NOTICE_GAMEOVER:
173             status = rr_client_show (client, &diagram);
174             if (status) {
175                 fprintf (stderr, "Error in rr_client_show: %s\n", rr_status_str (status));
176                 goto DONE;
177             }
178             rr_board_parse (board, diagram);
179             free (diagram);
180             break;
181         case RR_NOTICE_TURN:
182             rr_board_set_goal_target (board, notice->u.target);
183             rrs_solution_init (&solution);
184             solve_board (board, &solution);
185             rr_client_bid (client, solution.num_moves);
186             rr_client_nobid (client);
187             break;
188         case RR_NOTICE_ACTIVATE:
189             for (i = 0; i < solution.num_moves; i++) {
190                 status = rr_client_move (client,
191                                          solution.move[i].robot,
192                                          solution.move[i].dir);
193                 if (status) {
194                     rr_client_message (client, "Drat, looks like I was wrong.");
195                     rr_client_pass (client);
196                     break;
197                 }
198                 nanosleep (&move_delay, NULL);
199             }
200             rrs_solution_fini (&solution);
201             break;
202         case RR_NOTICE_ABANDON:
203             rr_client_abandon (client);
204             break;
205         case RR_NOTICE_NOBID:
206             rr_client_nobid (client);
207             break;
208         case RR_NOTICE_POSITION:
209             rr_board_position_robot (board,
210                                      notice->u.position.robot,
211                                      notice->u.position.x,
212                                      notice->u.position.y);
213             break;
214         case RR_NOTICE_GAMESTATE:
215             if (notice->u.gamestate == RR_GAMESTATE_SHOW) {
216                 if (solution.num_moves) {
217                     printf ("My solution (%d moves):", solution.num_moves);
218                     rrs_solution_print (&solution);
219                 }
220             }
221             break;
222         case RR_NOTICE_GAME:
223         case RR_NOTICE_USER:
224         case RR_NOTICE_JOIN:
225         case RR_NOTICE_QUIT:
226         case RR_NOTICE_DISPOSE:
227         case RR_NOTICE_MESSAGE:
228         case RR_NOTICE_WATCH:
229         case RR_NOTICE_PART:
230         case RR_NOTICE_BID:
231         case RR_NOTICE_REVOKE:
232         case RR_NOTICE_TIMER:
233         case RR_NOTICE_ACTIVE:
234         case RR_NOTICE_MOVE:
235         case RR_NOTICE_UNDO:
236         case RR_NOTICE_RESET:
237         case RR_NOTICE_SCORE:
238             /* Ignore these notices */
239             break;
240         }
241         free (notice);
242     }
243
244   DONE:
245     if (notice)
246         free (notice);
247     if (board)
248         rr_board_destroy (board);
249 }
250
251 static void
252 rrs_solution_print (rrs_solution_t *solution)
253 {
254     int i;
255     rr_robot_t last_robot;
256     rrs_move_t *move;
257
258     last_robot = RR_ROBOT_NONE;
259     for (i=0; i < solution->num_moves; i++) {
260         move = &solution->move[i];
261         if (move->robot == last_robot)
262             printf (", %s", rr_direction_str (move->dir));
263         else
264             printf ("\n Move #%d: %s %s",
265                     i+1, rr_robot_str (move->robot), rr_direction_str (move->dir));
266         last_robot = move->robot;
267     }
268     printf ("\n");
269 }
270
271 static rrs_state_t
272 rrs_state_get_from_board (rr_board_t *board)
273 {
274     int i;
275     int x, y;
276     rrs_state_t state;
277
278     state = 0;
279
280     for (i=0; i < RR_NUM_ROBOTS; i++) {
281         rr_board_find_robot (board, rr_robot_from_idx (i), &x, &y);
282         RRS_STATE_SET_ROBOT (state, i, x, y);
283     }
284
285     return state;
286 }
287
288 static void
289 rrs_state_set_board (rrs_state_t state, rr_board_t *board)
290 {
291     int i;
292     int x, y;
293
294     for (i=0; i < RR_NUM_ROBOTS; i++) {
295         RRS_STATE_GET_ROBOT (state, i, x, y);
296         rr_board_position_robot (board, rr_robot_from_idx (i), x, y);
297     }
298
299 }
300
301 static rr_status_t
302 solve_board (rr_board_t *board, rrs_solution_t *solution)
303 {
304     rr_status_t status;
305     int i, moves;
306     rrs_state_t solution_state;
307     rrs_state_buf_t **states;
308     struct timeval tv_start, tv_end;
309
310     if (rr_board_solved (board)) {
311         printf ("Board is solved to begin with.\n");
312         return RR_STATUS_SUCCESS;
313     }
314
315     printf ("Now trying to solve:");
316     puts (rr_board_to_str (board));
317     
318     gettimeofday (&tv_start, NULL);
319
320     status = find_solution_states (board, &states, &moves, &solution_state);
321     if (status)
322         return status;
323
324     gettimeofday (&tv_end, NULL);
325
326     if (moves < 0) {
327         printf ("It seems impossible.\n");
328     } else {
329         printf ("Found solution of %d moves in %g seconds.\n",
330                 moves,
331                 tv_end.tv_sec - tv_start.tv_sec
332                 + (tv_end.tv_usec - tv_start.tv_usec) / 1e6);
333
334         gettimeofday (&tv_start, NULL);
335
336         trace_solution (board, states, moves, solution_state, solution);
337
338         gettimeofday (&tv_end, NULL);
339
340         printf ("Traced solution in %g seconds.\n",
341                 tv_end.tv_sec - tv_start.tv_sec
342                 + (tv_end.tv_usec - tv_start.tv_usec) / 1e6);
343     }
344
345     for (i=0; i <= moves; i++) {
346         rrs_state_buf_destroy (states[i]);
347         states[i] = NULL;
348     }
349
350     free (states);
351
352     return RR_STATUS_SUCCESS;
353 }
354
355 static int
356 rrs_state_connected (rrs_state_t a, rrs_state_t b,
357                      rr_robot_t *robot, rr_direction_t *dir)
358 {
359     int i;
360     int nib;
361     rrs_state_t diff, mask;
362     int shift, delta;
363
364     diff = a ^ b;
365
366     nib = -1;
367     for (i=0; i < 8; i++) {
368         if (diff & 0xf) {
369             if (nib > 0)
370                 return 0;
371             else
372                 nib = i;
373         }
374         diff >>= 4;
375     }
376
377     switch (nib) {
378     case 0:
379     case 1:
380         *robot = RR_ROBOT_BLUE;
381         break;
382     case 2:
383     case 3:
384         *robot = RR_ROBOT_GREEN;
385         break;
386     case 4:
387     case 5:
388         *robot = RR_ROBOT_RED;
389         break;
390     case 6:
391     case 7:
392         *robot = RR_ROBOT_YELLOW;
393         break;
394     }
395
396     shift = 4 * nib;
397     mask = 0xf << shift;
398     delta = ((b & mask) >> shift) - ((a & mask) >> shift);
399     
400     if (nib % 2)
401         if (delta < 0)
402             *dir = RR_DIRECTION_WEST;
403         else
404             *dir = RR_DIRECTION_EAST;
405     else
406         if (delta < 0)
407             *dir = RR_DIRECTION_NORTH;
408         else
409             *dir = RR_DIRECTION_SOUTH;
410
411     return 1;
412 }
413
414 static void
415 trace_solution (rr_board_t *board,
416                 rrs_state_buf_t **states, int moves,
417                 rrs_state_t solution_state,
418                 rrs_solution_t *solution)
419 {
420     rr_status_t status;
421     int i, j;
422     rrs_state_buf_t *buf;
423     int found_move;
424     rr_robot_t robot, robot_found, last_robot_found = RR_ROBOT_NONE;
425     rr_direction_t dir, dir_found;
426     rrs_state_t state_found;
427
428     for (i = moves-1; i >= 0; i--) {
429         buf = states[i];
430         for (j = 0; j < buf->num_states; j++) {
431             if (rrs_state_connected (buf->state[j], solution_state,
432                                      &robot, &dir)) {
433                 rrs_state_set_board (buf->state[j], board);
434                 status = rr_board_move (board, robot, dir);
435                 if (status == RR_STATUS_SUCCESS) {
436                     if (rrs_state_get_from_board (board) == solution_state) {
437                         found_move = 1;
438                         robot_found = robot;
439                         dir_found = dir;
440                         state_found = buf->state[j];
441                         if (robot_found == last_robot_found)
442                             goto NEXT_MOVE;
443                         else
444                             last_robot_found = robot_found;
445                     }
446                 }
447             }
448         }
449       NEXT_MOVE:
450         if (found_move) {
451             rrs_solution_prepend (solution, robot_found, dir_found);
452             solution_state = state_found;
453         } else {
454             fprintf (stderr, "ERROR: Failed to trace solution backwards to 0x%x at move %d\n",
455                      solution_state, i+1);
456             break;
457         }
458     }
459 }
460
461 static rr_status_t
462 find_solution_states (rr_board_t *board,
463                       rrs_state_buf_t ***solution_states,
464                       int *num_state_buf,
465                       rrs_state_t *final_state)
466 {
467     int solved, moves;
468     rrs_state_t initial;
469     rrs_state_buf_t **states, **new_states;
470
471     states = malloc (sizeof (rrs_state_buf_t *));
472     if (states == NULL)
473         return RR_STATUS_NO_MEMORY;
474
475     initial = rrs_state_get_from_board (board);
476
477     states[0] = rrs_state_buf_create (1);
478     if (states[0] == NULL)
479         return RR_STATUS_NO_MEMORY;
480     rrs_state_buf_add (states[0], initial);
481
482     moves = 0;
483     while (1) {
484         moves++;
485
486         new_states = realloc (states, (moves + 1) * sizeof (rrs_state_buf_t *));
487         if (new_states == NULL)
488             return RR_STATUS_NO_MEMORY;
489
490         states = new_states;
491         states[moves] = rrs_state_buf_create (RRS_BRANCHING_FACTOR_ESTIMATE
492                                               * states[moves-1]->num_states);
493         if (states[moves] == NULL)
494             return RR_STATUS_NO_MEMORY;
495
496         solved = rrs_find_new_states (board, states, moves-1, states[moves], final_state);
497
498         if (solved)
499             break;
500
501         if (states[moves]->num_states == 0) {
502             printf ("Can't make any further progress after %d moves.\n", moves);
503             moves = -1;
504             break;
505         } else {
506             printf ("Move #%d generated %d new states.\n",
507                     moves, states[moves]->num_states);
508         }
509     }
510
511     *num_state_buf = moves;
512     *solution_states = states;
513
514     return RR_STATUS_SUCCESS;
515 }
516
517 static int
518 state_has_been_seen (rrs_state_buf_t **previous,
519                      int moves,
520                      rrs_state_t state)
521 {
522     int i;
523
524     for (i=0; i <= moves; i++)
525         if (rrs_state_buf_contains (previous[i], state))
526             return 1;
527     return 0;
528 }
529
530 static int
531 rrs_find_new_states (rr_board_t *board,
532                      rrs_state_buf_t **previous,
533                      int moves,
534                      rrs_state_buf_t *new,
535                      rrs_state_t *solution_state)
536 {
537     int i, ri;
538     rr_direction_t dir;
539     rrs_state_t state, new_state;
540     rrs_state_buf_t *initial;
541     rr_status_t status;
542     rr_robot_t robot;
543
544     initial = previous[moves];
545
546     for (i = 0; i < initial->num_states; i++) {
547         state = initial->state[i];
548
549         rrs_state_set_board (state, board);
550         for (ri = 0; ri < RR_NUM_ROBOTS; ri++) {
551             robot = rr_robot_from_idx (ri);
552             for (dir = RR_DIRECTION_NORTH; dir <= RR_DIRECTION_EAST; dir++) {
553                 status = rr_board_move (board, robot, dir);
554                 if (status == RR_STATUS_SUCCESS) {
555                     new_state = rrs_state_get_from_board (board);
556                     if (! state_has_been_seen (previous, moves, new_state)) {
557                         rrs_state_buf_add_sorted (new, new_state);
558                         if (rr_board_solved (board)) {
559                             *solution_state = new_state;
560                             return 1;
561                         }
562                     }
563                     rr_board_undo (board);
564                 }
565             }
566         }
567     }
568
569     return 0;
570 }
571