Added an automatic `Solve' feature to most games. This is useful for
[sgt/puzzles] / netslide.c
1 /*
2 * netslide.c: cross between Net and Sixteen, courtesy of Richard
3 * Boulton.
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <ctype.h>
11 #include <math.h>
12
13 #include "puzzles.h"
14 #include "tree234.h"
15
16 #define PI 3.141592653589793238462643383279502884197169399
17
18 #define MATMUL(xr,yr,m,x,y) do { \
19 float rx, ry, xx = (x), yy = (y), *mat = (m); \
20 rx = mat[0] * xx + mat[2] * yy; \
21 ry = mat[1] * xx + mat[3] * yy; \
22 (xr) = rx; (yr) = ry; \
23 } while (0)
24
25 /* Direction and other bitfields */
26 #define R 0x01
27 #define U 0x02
28 #define L 0x04
29 #define D 0x08
30 #define FLASHING 0x10
31 #define ACTIVE 0x20
32 /* Corner flags go in the barriers array */
33 #define RU 0x10
34 #define UL 0x20
35 #define LD 0x40
36 #define DR 0x80
37
38 /* Get tile at given coordinate */
39 #define T(state, x, y) ( (y) * (state)->width + (x) )
40
41 /* Rotations: Anticlockwise, Clockwise, Flip, general rotate */
42 #define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) )
43 #define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) )
44 #define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) )
45 #define ROT(x, n) ( ((n)&3) == 0 ? (x) : \
46 ((n)&3) == 1 ? A(x) : \
47 ((n)&3) == 2 ? F(x) : C(x) )
48
49 /* X and Y displacements */
50 #define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 )
51 #define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 )
52
53 /* Bit count */
54 #define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \
55 (((x) & 0x02) >> 1) + ((x) & 0x01) )
56
57 #define TILE_SIZE 48
58 #define BORDER TILE_SIZE
59 #define TILE_BORDER 1
60 #define WINDOW_OFFSET 0
61
62 #define ANIM_TIME 0.13F
63 #define FLASH_FRAME 0.07F
64
65 enum {
66 COL_BACKGROUND,
67 COL_FLASHING,
68 COL_BORDER,
69 COL_WIRE,
70 COL_ENDPOINT,
71 COL_POWERED,
72 COL_BARRIER,
73 COL_LOWLIGHT,
74 COL_TEXT,
75 NCOLOURS
76 };
77
78 struct game_params {
79 int width;
80 int height;
81 int wrapping;
82 float barrier_probability;
83 };
84
85 struct solved_game_state {
86 int width, height;
87 int refcount;
88 unsigned char *tiles;
89 };
90
91 struct game_state {
92 int width, height, cx, cy, wrapping, completed;
93 int used_solve, just_used_solve;
94 int move_count;
95
96 /* position (row or col number, starting at 0) of last move. */
97 int last_move_row, last_move_col;
98
99 /* direction of last move: +1 or -1 */
100 int last_move_dir;
101
102 unsigned char *tiles;
103 unsigned char *barriers;
104 struct solved_game_state *solution;
105 };
106
107 #define OFFSET(x2,y2,x1,y1,dir,state) \
108 ( (x2) = ((x1) + (state)->width + X((dir))) % (state)->width, \
109 (y2) = ((y1) + (state)->height + Y((dir))) % (state)->height)
110
111 #define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
112 #define tile(state, x, y) index(state, (state)->tiles, x, y)
113 #define barrier(state, x, y) index(state, (state)->barriers, x, y)
114
115 struct xyd {
116 int x, y, direction;
117 };
118
119 static int xyd_cmp(void *av, void *bv) {
120 struct xyd *a = (struct xyd *)av;
121 struct xyd *b = (struct xyd *)bv;
122 if (a->x < b->x)
123 return -1;
124 if (a->x > b->x)
125 return +1;
126 if (a->y < b->y)
127 return -1;
128 if (a->y > b->y)
129 return +1;
130 if (a->direction < b->direction)
131 return -1;
132 if (a->direction > b->direction)
133 return +1;
134 return 0;
135 };
136
137 static struct xyd *new_xyd(int x, int y, int direction)
138 {
139 struct xyd *xyd = snew(struct xyd);
140 xyd->x = x;
141 xyd->y = y;
142 xyd->direction = direction;
143 return xyd;
144 }
145
146 static void slide_col(game_state *state, int dir, int col);
147 static void slide_row(game_state *state, int dir, int row);
148
149 /* ----------------------------------------------------------------------
150 * Manage game parameters.
151 */
152 static game_params *default_params(void)
153 {
154 game_params *ret = snew(game_params);
155
156 ret->width = 3;
157 ret->height = 3;
158 ret->wrapping = FALSE;
159 ret->barrier_probability = 1.0;
160
161 return ret;
162 }
163
164 static int game_fetch_preset(int i, char **name, game_params **params)
165 {
166 game_params *ret;
167 char str[80];
168 static const struct { int x, y, wrap, bprob; const char* desc; } values[] = {
169 {3, 3, FALSE, 1.0, " easy"},
170 {3, 3, FALSE, 0.0, " medium"},
171 {3, 3, TRUE, 0.0, " hard"},
172 {4, 4, FALSE, 1.0, " easy"},
173 {4, 4, FALSE, 0.0, " medium"},
174 {4, 4, TRUE, 0.0, " hard"},
175 {5, 5, FALSE, 1.0, " easy"},
176 {5, 5, FALSE, 0.0, " medium"},
177 {5, 5, TRUE, 0.0, " hard"},
178 };
179
180 if (i < 0 || i >= lenof(values))
181 return FALSE;
182
183 ret = snew(game_params);
184 ret->width = values[i].x;
185 ret->height = values[i].y;
186 ret->wrapping = values[i].wrap;
187 ret->barrier_probability = values[i].bprob;
188
189 sprintf(str, "%dx%d%s", ret->width, ret->height,
190 values[i].desc);
191
192 *name = dupstr(str);
193 *params = ret;
194 return TRUE;
195 }
196
197 static void free_params(game_params *params)
198 {
199 sfree(params);
200 }
201
202 static game_params *dup_params(game_params *params)
203 {
204 game_params *ret = snew(game_params);
205 *ret = *params; /* structure copy */
206 return ret;
207 }
208
209 static game_params *decode_params(char const *string)
210 {
211 game_params *ret = default_params();
212 char const *p = string;
213
214 ret->wrapping = FALSE;
215 ret->barrier_probability = 0.0;
216
217 ret->width = atoi(p);
218 while (*p && isdigit(*p)) p++;
219 if (*p == 'x') {
220 p++;
221 ret->height = atoi(p);
222 while (*p && isdigit(*p)) p++;
223 if ( (ret->wrapping = (*p == 'w')) != 0 )
224 p++;
225 if (*p == 'b')
226 ret->barrier_probability = atof(p+1);
227 } else {
228 ret->height = ret->width;
229 }
230
231 return ret;
232 }
233
234 static char *encode_params(game_params *params)
235 {
236 char ret[400];
237 int len;
238
239 len = sprintf(ret, "%dx%d", params->width, params->height);
240 if (params->wrapping)
241 ret[len++] = 'w';
242 if (params->barrier_probability)
243 len += sprintf(ret+len, "b%g", params->barrier_probability);
244 assert(len < lenof(ret));
245 ret[len] = '\0';
246
247 return dupstr(ret);
248 }
249
250 static config_item *game_configure(game_params *params)
251 {
252 config_item *ret;
253 char buf[80];
254
255 ret = snewn(5, config_item);
256
257 ret[0].name = "Width";
258 ret[0].type = C_STRING;
259 sprintf(buf, "%d", params->width);
260 ret[0].sval = dupstr(buf);
261 ret[0].ival = 0;
262
263 ret[1].name = "Height";
264 ret[1].type = C_STRING;
265 sprintf(buf, "%d", params->height);
266 ret[1].sval = dupstr(buf);
267 ret[1].ival = 0;
268
269 ret[2].name = "Walls wrap around";
270 ret[2].type = C_BOOLEAN;
271 ret[2].sval = NULL;
272 ret[2].ival = params->wrapping;
273
274 ret[3].name = "Barrier probability";
275 ret[3].type = C_STRING;
276 sprintf(buf, "%g", params->barrier_probability);
277 ret[3].sval = dupstr(buf);
278 ret[3].ival = 0;
279
280 ret[4].name = NULL;
281 ret[4].type = C_END;
282 ret[4].sval = NULL;
283 ret[4].ival = 0;
284
285 return ret;
286 }
287
288 static game_params *custom_params(config_item *cfg)
289 {
290 game_params *ret = snew(game_params);
291
292 ret->width = atoi(cfg[0].sval);
293 ret->height = atoi(cfg[1].sval);
294 ret->wrapping = cfg[2].ival;
295 ret->barrier_probability = (float)atof(cfg[3].sval);
296
297 return ret;
298 }
299
300 static char *validate_params(game_params *params)
301 {
302 if (params->width <= 1 && params->height <= 1)
303 return "Width and height must both be greater than one";
304 if (params->width <= 1)
305 return "Width must be greater than one";
306 if (params->height <= 1)
307 return "Height must be greater than one";
308 if (params->barrier_probability < 0)
309 return "Barrier probability may not be negative";
310 if (params->barrier_probability > 1)
311 return "Barrier probability may not be greater than 1";
312 return NULL;
313 }
314
315 /* ----------------------------------------------------------------------
316 * Randomly select a new game seed.
317 */
318
319 static char *new_game_seed(game_params *params, random_state *rs,
320 game_aux_info **aux)
321 {
322 /*
323 * The full description of a Net game is far too large to
324 * encode directly in the seed, so by default we'll have to go
325 * for the simple approach of providing a random-number seed.
326 *
327 * (This does not restrict me from _later on_ inventing a seed
328 * string syntax which can never be generated by this code -
329 * for example, strings beginning with a letter - allowing me
330 * to type in a precise game, and have new_game detect it and
331 * understand it and do something completely different.)
332 */
333 char buf[40];
334 sprintf(buf, "%lu", random_bits(rs, 32));
335 return dupstr(buf);
336 }
337
338 static void game_free_aux_info(game_aux_info *aux)
339 {
340 assert(!"Shouldn't happen");
341 }
342
343 static char *validate_seed(game_params *params, char *seed)
344 {
345 /*
346 * Since any string at all will suffice to seed the RNG, there
347 * is no validation required.
348 */
349 return NULL;
350 }
351
352 /* ----------------------------------------------------------------------
353 * Construct an initial game state, given a seed and parameters.
354 */
355
356 static game_state *new_game(game_params *params, char *seed)
357 {
358 random_state *rs;
359 game_state *state;
360 tree234 *possibilities, *barriers;
361 int w, h, x, y, nbarriers;
362
363 assert(params->width > 0 && params->height > 0);
364 assert(params->width > 1 || params->height > 1);
365
366 /*
367 * Create a blank game state.
368 */
369 state = snew(game_state);
370 w = state->width = params->width;
371 h = state->height = params->height;
372 state->cx = state->width / 2;
373 state->cy = state->height / 2;
374 state->wrapping = params->wrapping;
375 state->completed = 0;
376 state->used_solve = state->just_used_solve = FALSE;
377 state->move_count = 0;
378 state->last_move_row = -1;
379 state->last_move_col = -1;
380 state->last_move_dir = 0;
381 state->tiles = snewn(state->width * state->height, unsigned char);
382 memset(state->tiles, 0, state->width * state->height);
383 state->barriers = snewn(state->width * state->height, unsigned char);
384 memset(state->barriers, 0, state->width * state->height);
385
386 /*
387 * Set up border barriers if this is a non-wrapping game.
388 */
389 if (!state->wrapping) {
390 for (x = 0; x < state->width; x++) {
391 barrier(state, x, 0) |= U;
392 barrier(state, x, state->height-1) |= D;
393 }
394 for (y = 0; y < state->height; y++) {
395 barrier(state, 0, y) |= L;
396 barrier(state, state->width-1, y) |= R;
397 }
398 }
399
400 /*
401 * Seed the internal random number generator.
402 */
403 rs = random_init(seed, strlen(seed));
404
405 /*
406 * Construct the unshuffled grid.
407 *
408 * To do this, we simply start at the centre point, repeatedly
409 * choose a random possibility out of the available ways to
410 * extend a used square into an unused one, and do it. After
411 * extending the third line out of a square, we remove the
412 * fourth from the possibilities list to avoid any full-cross
413 * squares (which would make the game too easy because they
414 * only have one orientation).
415 *
416 * The slightly worrying thing is the avoidance of full-cross
417 * squares. Can this cause our unsophisticated construction
418 * algorithm to paint itself into a corner, by getting into a
419 * situation where there are some unreached squares and the
420 * only way to reach any of them is to extend a T-piece into a
421 * full cross?
422 *
423 * Answer: no it can't, and here's a proof.
424 *
425 * Any contiguous group of such unreachable squares must be
426 * surrounded on _all_ sides by T-pieces pointing away from the
427 * group. (If not, then there is a square which can be extended
428 * into one of the `unreachable' ones, and so it wasn't
429 * unreachable after all.) In particular, this implies that
430 * each contiguous group of unreachable squares must be
431 * rectangular in shape (any deviation from that yields a
432 * non-T-piece next to an `unreachable' square).
433 *
434 * So we have a rectangle of unreachable squares, with T-pieces
435 * forming a solid border around the rectangle. The corners of
436 * that border must be connected (since every tile connects all
437 * the lines arriving in it), and therefore the border must
438 * form a closed loop around the rectangle.
439 *
440 * But this can't have happened in the first place, since we
441 * _know_ we've avoided creating closed loops! Hence, no such
442 * situation can ever arise, and the naive grid construction
443 * algorithm will guaranteeably result in a complete grid
444 * containing no unreached squares, no full crosses _and_ no
445 * closed loops. []
446 */
447 possibilities = newtree234(xyd_cmp);
448
449 if (state->cx+1 < state->width)
450 add234(possibilities, new_xyd(state->cx, state->cy, R));
451 if (state->cy-1 >= 0)
452 add234(possibilities, new_xyd(state->cx, state->cy, U));
453 if (state->cx-1 >= 0)
454 add234(possibilities, new_xyd(state->cx, state->cy, L));
455 if (state->cy+1 < state->height)
456 add234(possibilities, new_xyd(state->cx, state->cy, D));
457
458 while (count234(possibilities) > 0) {
459 int i;
460 struct xyd *xyd;
461 int x1, y1, d1, x2, y2, d2, d;
462
463 /*
464 * Extract a randomly chosen possibility from the list.
465 */
466 i = random_upto(rs, count234(possibilities));
467 xyd = delpos234(possibilities, i);
468 x1 = xyd->x;
469 y1 = xyd->y;
470 d1 = xyd->direction;
471 sfree(xyd);
472
473 OFFSET(x2, y2, x1, y1, d1, state);
474 d2 = F(d1);
475 #ifdef DEBUG
476 printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n",
477 x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]);
478 #endif
479
480 /*
481 * Make the connection. (We should be moving to an as yet
482 * unused tile.)
483 */
484 tile(state, x1, y1) |= d1;
485 assert(tile(state, x2, y2) == 0);
486 tile(state, x2, y2) |= d2;
487
488 /*
489 * If we have created a T-piece, remove its last
490 * possibility.
491 */
492 if (COUNT(tile(state, x1, y1)) == 3) {
493 struct xyd xyd1, *xydp;
494
495 xyd1.x = x1;
496 xyd1.y = y1;
497 xyd1.direction = 0x0F ^ tile(state, x1, y1);
498
499 xydp = find234(possibilities, &xyd1, NULL);
500
501 if (xydp) {
502 #ifdef DEBUG
503 printf("T-piece; removing (%d,%d,%c)\n",
504 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
505 #endif
506 del234(possibilities, xydp);
507 sfree(xydp);
508 }
509 }
510
511 /*
512 * Remove all other possibilities that were pointing at the
513 * tile we've just moved into.
514 */
515 for (d = 1; d < 0x10; d <<= 1) {
516 int x3, y3, d3;
517 struct xyd xyd1, *xydp;
518
519 OFFSET(x3, y3, x2, y2, d, state);
520 d3 = F(d);
521
522 xyd1.x = x3;
523 xyd1.y = y3;
524 xyd1.direction = d3;
525
526 xydp = find234(possibilities, &xyd1, NULL);
527
528 if (xydp) {
529 #ifdef DEBUG
530 printf("Loop avoidance; removing (%d,%d,%c)\n",
531 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
532 #endif
533 del234(possibilities, xydp);
534 sfree(xydp);
535 }
536 }
537
538 /*
539 * Add new possibilities to the list for moving _out_ of
540 * the tile we have just moved into.
541 */
542 for (d = 1; d < 0x10; d <<= 1) {
543 int x3, y3;
544
545 if (d == d2)
546 continue; /* we've got this one already */
547
548 if (!state->wrapping) {
549 if (d == U && y2 == 0)
550 continue;
551 if (d == D && y2 == state->height-1)
552 continue;
553 if (d == L && x2 == 0)
554 continue;
555 if (d == R && x2 == state->width-1)
556 continue;
557 }
558
559 OFFSET(x3, y3, x2, y2, d, state);
560
561 if (tile(state, x3, y3))
562 continue; /* this would create a loop */
563
564 #ifdef DEBUG
565 printf("New frontier; adding (%d,%d,%c)\n",
566 x2, y2, "0RU3L567D9abcdef"[d]);
567 #endif
568 add234(possibilities, new_xyd(x2, y2, d));
569 }
570 }
571 /* Having done that, we should have no possibilities remaining. */
572 assert(count234(possibilities) == 0);
573 freetree234(possibilities);
574
575 /*
576 * Now compute a list of the possible barrier locations.
577 */
578 barriers = newtree234(xyd_cmp);
579 for (y = 0; y < state->height; y++) {
580 for (x = 0; x < state->width; x++) {
581
582 if (!(tile(state, x, y) & R) &&
583 (state->wrapping || x < state->width-1))
584 add234(barriers, new_xyd(x, y, R));
585 if (!(tile(state, x, y) & D) &&
586 (state->wrapping || y < state->height-1))
587 add234(barriers, new_xyd(x, y, D));
588 }
589 }
590
591 /*
592 * Save the unshuffled grid. We do this using a separate
593 * reference-counted structure since it's a large chunk of
594 * memory which we don't want to have to replicate in every
595 * game state while playing.
596 */
597 {
598 struct solved_game_state *solution;
599
600 solution = snew(struct solved_game_state);
601 solution->width = state->width;
602 solution->height = state->height;
603 solution->refcount = 1;
604 solution->tiles = snewn(state->width * state->height, unsigned char);
605 memcpy(solution->tiles, state->tiles, state->width * state->height);
606
607 state->solution = solution;
608 }
609
610 /*
611 * Now shuffle the grid.
612 * FIXME - this simply does a set of random moves to shuffle the pieces.
613 * A better way would be to number all the pieces, generate a placement
614 * for all the numbers as for "sixteen", observing parity constraints if
615 * neccessary, and then place the pieces according to their numbering.
616 * BUT - I'm not sure if this will work, since we disallow movement of
617 * the middle row and column.
618 */
619 {
620 int i;
621 int cols = state->width - 1;
622 int rows = state->height - 1;
623 for (i = 0; i < cols * rows * 2; i++) {
624 /* Choose a direction: 0,1,2,3 = up, right, down, left. */
625 int dir = random_upto(rs, 4);
626 if (dir % 2 == 0) {
627 int col = random_upto(rs, cols);
628 if (col >= state->cx) col += 1;
629 slide_col(state, 1 - dir, col);
630 } else {
631 int row = random_upto(rs, rows);
632 if (row >= state->cy) row += 1;
633 slide_row(state, 2 - dir, row);
634 }
635 }
636 }
637
638 /*
639 * And now choose barrier locations. (We carefully do this
640 * _after_ shuffling, so that changing the barrier rate in the
641 * params while keeping the game seed the same will give the
642 * same shuffled grid and _only_ change the barrier locations.
643 * Also the way we choose barrier locations, by repeatedly
644 * choosing one possibility from the list until we have enough,
645 * is designed to ensure that raising the barrier rate while
646 * keeping the seed the same will provide a superset of the
647 * previous barrier set - i.e. if you ask for 10 barriers, and
648 * then decide that's still too hard and ask for 20, you'll get
649 * the original 10 plus 10 more, rather than getting 20 new
650 * ones and the chance of remembering your first 10.)
651 */
652 nbarriers = (int)(params->barrier_probability * count234(barriers));
653 assert(nbarriers >= 0 && nbarriers <= count234(barriers));
654
655 while (nbarriers > 0) {
656 int i;
657 struct xyd *xyd;
658 int x1, y1, d1, x2, y2, d2;
659
660 /*
661 * Extract a randomly chosen barrier from the list.
662 */
663 i = random_upto(rs, count234(barriers));
664 xyd = delpos234(barriers, i);
665
666 assert(xyd != NULL);
667
668 x1 = xyd->x;
669 y1 = xyd->y;
670 d1 = xyd->direction;
671 sfree(xyd);
672
673 OFFSET(x2, y2, x1, y1, d1, state);
674 d2 = F(d1);
675
676 barrier(state, x1, y1) |= d1;
677 barrier(state, x2, y2) |= d2;
678
679 nbarriers--;
680 }
681
682 /*
683 * Clean up the rest of the barrier list.
684 */
685 {
686 struct xyd *xyd;
687
688 while ( (xyd = delpos234(barriers, 0)) != NULL)
689 sfree(xyd);
690
691 freetree234(barriers);
692 }
693
694 /*
695 * Set up the barrier corner flags, for drawing barriers
696 * prettily when they meet.
697 */
698 for (y = 0; y < state->height; y++) {
699 for (x = 0; x < state->width; x++) {
700 int dir;
701
702 for (dir = 1; dir < 0x10; dir <<= 1) {
703 int dir2 = A(dir);
704 int x1, y1, x2, y2, x3, y3;
705 int corner = FALSE;
706
707 if (!(barrier(state, x, y) & dir))
708 continue;
709
710 if (barrier(state, x, y) & dir2)
711 corner = TRUE;
712
713 x1 = x + X(dir), y1 = y + Y(dir);
714 if (x1 >= 0 && x1 < state->width &&
715 y1 >= 0 && y1 < state->height &&
716 (barrier(state, x1, y1) & dir2))
717 corner = TRUE;
718
719 x2 = x + X(dir2), y2 = y + Y(dir2);
720 if (x2 >= 0 && x2 < state->width &&
721 y2 >= 0 && y2 < state->height &&
722 (barrier(state, x2, y2) & dir))
723 corner = TRUE;
724
725 if (corner) {
726 barrier(state, x, y) |= (dir << 4);
727 if (x1 >= 0 && x1 < state->width &&
728 y1 >= 0 && y1 < state->height)
729 barrier(state, x1, y1) |= (A(dir) << 4);
730 if (x2 >= 0 && x2 < state->width &&
731 y2 >= 0 && y2 < state->height)
732 barrier(state, x2, y2) |= (C(dir) << 4);
733 x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2);
734 if (x3 >= 0 && x3 < state->width &&
735 y3 >= 0 && y3 < state->height)
736 barrier(state, x3, y3) |= (F(dir) << 4);
737 }
738 }
739 }
740 }
741
742 random_free(rs);
743
744 return state;
745 }
746
747 static game_state *dup_game(game_state *state)
748 {
749 game_state *ret;
750
751 ret = snew(game_state);
752 ret->width = state->width;
753 ret->height = state->height;
754 ret->cx = state->cx;
755 ret->cy = state->cy;
756 ret->wrapping = state->wrapping;
757 ret->completed = state->completed;
758 ret->used_solve = state->used_solve;
759 ret->just_used_solve = state->just_used_solve;
760 ret->move_count = state->move_count;
761 ret->last_move_row = state->last_move_row;
762 ret->last_move_col = state->last_move_col;
763 ret->last_move_dir = state->last_move_dir;
764 ret->tiles = snewn(state->width * state->height, unsigned char);
765 memcpy(ret->tiles, state->tiles, state->width * state->height);
766 ret->barriers = snewn(state->width * state->height, unsigned char);
767 memcpy(ret->barriers, state->barriers, state->width * state->height);
768 ret->solution = state->solution;
769 if (ret->solution)
770 ret->solution->refcount++;
771
772 return ret;
773 }
774
775 static void free_game(game_state *state)
776 {
777 if (state->solution && --state->solution->refcount <= 0) {
778 sfree(state->solution->tiles);
779 sfree(state->solution);
780 }
781 sfree(state->tiles);
782 sfree(state->barriers);
783 sfree(state);
784 }
785
786 static game_state *solve_game(game_state *state, game_aux_info *aux,
787 char **error)
788 {
789 game_state *ret;
790
791 if (!state->solution) {
792 /*
793 * 2005-05-02: This shouldn't happen, at the time of
794 * writing, because Net is incapable of receiving a puzzle
795 * description from outside. If in future it becomes so,
796 * then we will have puzzles for which we don't know the
797 * solution.
798 */
799 *error = "Solution not known for this puzzle";
800 return NULL;
801 }
802
803 assert(state->solution->width == state->width);
804 assert(state->solution->height == state->height);
805 ret = dup_game(state);
806 memcpy(ret->tiles, state->solution->tiles, ret->width * ret->height);
807 ret->used_solve = ret->just_used_solve = TRUE;
808 ret->completed = ret->move_count;
809
810 return ret;
811 }
812
813 static char *game_text_format(game_state *state)
814 {
815 return NULL;
816 }
817
818 /* ----------------------------------------------------------------------
819 * Utility routine.
820 */
821
822 /*
823 * Compute which squares are reachable from the centre square, as a
824 * quick visual aid to determining how close the game is to
825 * completion. This is also a simple way to tell if the game _is_
826 * completed - just call this function and see whether every square
827 * is marked active.
828 *
829 * squares in the moving_row and moving_col are always inactive - this
830 * is so that "current" doesn't appear to jump across moving lines.
831 */
832 static unsigned char *compute_active(game_state *state,
833 int moving_row, int moving_col)
834 {
835 unsigned char *active;
836 tree234 *todo;
837 struct xyd *xyd;
838
839 active = snewn(state->width * state->height, unsigned char);
840 memset(active, 0, state->width * state->height);
841
842 /*
843 * We only store (x,y) pairs in todo, but it's easier to reuse
844 * xyd_cmp and just store direction 0 every time.
845 */
846 todo = newtree234(xyd_cmp);
847 index(state, active, state->cx, state->cy) = ACTIVE;
848 add234(todo, new_xyd(state->cx, state->cy, 0));
849
850 while ( (xyd = delpos234(todo, 0)) != NULL) {
851 int x1, y1, d1, x2, y2, d2;
852
853 x1 = xyd->x;
854 y1 = xyd->y;
855 sfree(xyd);
856
857 for (d1 = 1; d1 < 0x10; d1 <<= 1) {
858 OFFSET(x2, y2, x1, y1, d1, state);
859 d2 = F(d1);
860
861 /*
862 * If the next tile in this direction is connected to
863 * us, and there isn't a barrier in the way, and it
864 * isn't already marked active, then mark it active and
865 * add it to the to-examine list.
866 */
867 if ((x2 != moving_col && y2 != moving_row) &&
868 (tile(state, x1, y1) & d1) &&
869 (tile(state, x2, y2) & d2) &&
870 !(barrier(state, x1, y1) & d1) &&
871 !index(state, active, x2, y2)) {
872 index(state, active, x2, y2) = ACTIVE;
873 add234(todo, new_xyd(x2, y2, 0));
874 }
875 }
876 }
877 /* Now we expect the todo list to have shrunk to zero size. */
878 assert(count234(todo) == 0);
879 freetree234(todo);
880
881 return active;
882 }
883
884 struct game_ui {
885 int cur_x, cur_y;
886 int cur_visible;
887 };
888
889 static game_ui *new_ui(game_state *state)
890 {
891 game_ui *ui = snew(game_ui);
892 ui->cur_x = state->width / 2;
893 ui->cur_y = state->height / 2;
894 ui->cur_visible = FALSE;
895
896 return ui;
897 }
898
899 static void free_ui(game_ui *ui)
900 {
901 sfree(ui);
902 }
903
904 /* ----------------------------------------------------------------------
905 * Process a move.
906 */
907
908 static void slide_row(game_state *state, int dir, int row)
909 {
910 int x = dir > 0 ? -1 : state->width;
911 int tx = x + dir;
912 int n = state->width - 1;
913 unsigned char endtile = state->tiles[T(state, tx, row)];
914 do {
915 x = tx;
916 tx = (x + dir + state->width) % state->width;
917 state->tiles[T(state, x, row)] = state->tiles[T(state, tx, row)];
918 } while (--n > 0);
919 state->tiles[T(state, tx, row)] = endtile;
920 }
921
922 static void slide_col(game_state *state, int dir, int col)
923 {
924 int y = dir > 0 ? -1 : state->height;
925 int ty = y + dir;
926 int n = state->height - 1;
927 unsigned char endtile = state->tiles[T(state, col, ty)];
928 do {
929 y = ty;
930 ty = (y + dir + state->height) % state->height;
931 state->tiles[T(state, col, y)] = state->tiles[T(state, col, ty)];
932 } while (--n > 0);
933 state->tiles[T(state, col, ty)] = endtile;
934 }
935
936 static game_state *make_move(game_state *state, game_ui *ui,
937 int x, int y, int button)
938 {
939 int cx, cy;
940 int n, dx, dy;
941 game_state *ret;
942
943 if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
944 return NULL;
945
946 cx = (x - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
947 cy = (y - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
948
949 if (cy >= 0 && cy < state->height && cy != state->cy)
950 {
951 if (cx == -1) dx = +1;
952 else if (cx == state->width) dx = -1;
953 else return NULL;
954 n = state->width;
955 dy = 0;
956 }
957 else if (cx >= 0 && cx < state->width && cx != state->cx)
958 {
959 if (cy == -1) dy = +1;
960 else if (cy == state->height) dy = -1;
961 else return NULL;
962 n = state->height;
963 dx = 0;
964 }
965 else
966 return NULL;
967
968 /* reverse direction if right hand button is pressed */
969 if (button == RIGHT_BUTTON)
970 {
971 dx = -dx;
972 dy = -dy;
973 }
974
975 ret = dup_game(state);
976 ret->just_used_solve = FALSE;
977
978 if (dx == 0) slide_col(ret, dy, cx);
979 else slide_row(ret, dx, cy);
980
981 ret->move_count++;
982 ret->last_move_row = dx ? cy : -1;
983 ret->last_move_col = dx ? -1 : cx;
984 ret->last_move_dir = dx + dy;
985
986 /*
987 * See if the game has been completed.
988 */
989 if (!ret->completed) {
990 unsigned char *active = compute_active(ret, -1, -1);
991 int x1, y1;
992 int complete = TRUE;
993
994 for (x1 = 0; x1 < ret->width; x1++)
995 for (y1 = 0; y1 < ret->height; y1++)
996 if (!index(ret, active, x1, y1)) {
997 complete = FALSE;
998 goto break_label; /* break out of two loops at once */
999 }
1000 break_label:
1001
1002 sfree(active);
1003
1004 if (complete)
1005 ret->completed = ret->move_count;
1006 }
1007
1008 return ret;
1009 }
1010
1011 /* ----------------------------------------------------------------------
1012 * Routines for drawing the game position on the screen.
1013 */
1014
1015 struct game_drawstate {
1016 int started;
1017 int width, height;
1018 unsigned char *visible;
1019 };
1020
1021 static game_drawstate *game_new_drawstate(game_state *state)
1022 {
1023 game_drawstate *ds = snew(game_drawstate);
1024
1025 ds->started = FALSE;
1026 ds->width = state->width;
1027 ds->height = state->height;
1028 ds->visible = snewn(state->width * state->height, unsigned char);
1029 memset(ds->visible, 0xFF, state->width * state->height);
1030
1031 return ds;
1032 }
1033
1034 static void game_free_drawstate(game_drawstate *ds)
1035 {
1036 sfree(ds->visible);
1037 sfree(ds);
1038 }
1039
1040 static void game_size(game_params *params, int *x, int *y)
1041 {
1042 *x = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
1043 *y = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
1044 }
1045
1046 static float *game_colours(frontend *fe, game_state *state, int *ncolours)
1047 {
1048 float *ret;
1049
1050 ret = snewn(NCOLOURS * 3, float);
1051 *ncolours = NCOLOURS;
1052
1053 /*
1054 * Basic background colour is whatever the front end thinks is
1055 * a sensible default.
1056 */
1057 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1058
1059 /*
1060 * Wires are black.
1061 */
1062 ret[COL_WIRE * 3 + 0] = 0.0F;
1063 ret[COL_WIRE * 3 + 1] = 0.0F;
1064 ret[COL_WIRE * 3 + 2] = 0.0F;
1065
1066 /*
1067 * Powered wires and powered endpoints are cyan.
1068 */
1069 ret[COL_POWERED * 3 + 0] = 0.0F;
1070 ret[COL_POWERED * 3 + 1] = 1.0F;
1071 ret[COL_POWERED * 3 + 2] = 1.0F;
1072
1073 /*
1074 * Barriers are red.
1075 */
1076 ret[COL_BARRIER * 3 + 0] = 1.0F;
1077 ret[COL_BARRIER * 3 + 1] = 0.0F;
1078 ret[COL_BARRIER * 3 + 2] = 0.0F;
1079
1080 /*
1081 * Unpowered endpoints are blue.
1082 */
1083 ret[COL_ENDPOINT * 3 + 0] = 0.0F;
1084 ret[COL_ENDPOINT * 3 + 1] = 0.0F;
1085 ret[COL_ENDPOINT * 3 + 2] = 1.0F;
1086
1087 /*
1088 * Tile borders are a darker grey than the background.
1089 */
1090 ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1091 ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1092 ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
1093
1094 /*
1095 * Flashing tiles are a grey in between those two.
1096 */
1097 ret[COL_FLASHING * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
1098 ret[COL_FLASHING * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
1099 ret[COL_FLASHING * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
1100
1101 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
1102 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F;
1103 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F;
1104 ret[COL_TEXT * 3 + 0] = 0.0;
1105 ret[COL_TEXT * 3 + 1] = 0.0;
1106 ret[COL_TEXT * 3 + 2] = 0.0;
1107
1108 return ret;
1109 }
1110
1111 static void draw_thick_line(frontend *fe, int x1, int y1, int x2, int y2,
1112 int colour)
1113 {
1114 draw_line(fe, x1-1, y1, x2-1, y2, COL_WIRE);
1115 draw_line(fe, x1+1, y1, x2+1, y2, COL_WIRE);
1116 draw_line(fe, x1, y1-1, x2, y2-1, COL_WIRE);
1117 draw_line(fe, x1, y1+1, x2, y2+1, COL_WIRE);
1118 draw_line(fe, x1, y1, x2, y2, colour);
1119 }
1120
1121 static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2,
1122 int colour)
1123 {
1124 int mx = (x1 < x2 ? x1 : x2);
1125 int my = (y1 < y2 ? y1 : y2);
1126 int dx = (x2 + x1 - 2*mx + 1);
1127 int dy = (y2 + y1 - 2*my + 1);
1128
1129 draw_rect(fe, mx, my, dx, dy, colour);
1130 }
1131
1132 static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase)
1133 {
1134 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1135 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1136 int x1, y1, dx, dy, dir2;
1137
1138 dir >>= 4;
1139
1140 dir2 = A(dir);
1141 dx = X(dir) + X(dir2);
1142 dy = Y(dir) + Y(dir2);
1143 x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1144 y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1145
1146 if (phase == 0) {
1147 draw_rect_coords(fe, bx+x1, by+y1,
1148 bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
1149 COL_WIRE);
1150 draw_rect_coords(fe, bx+x1, by+y1,
1151 bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
1152 COL_WIRE);
1153 } else {
1154 draw_rect_coords(fe, bx+x1, by+y1,
1155 bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
1156 COL_BARRIER);
1157 }
1158 }
1159
1160 static void draw_barrier(frontend *fe, int x, int y, int dir, int phase)
1161 {
1162 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1163 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1164 int x1, y1, w, h;
1165
1166 x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0);
1167 y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0);
1168 w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1169 h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1170
1171 if (phase == 0) {
1172 draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
1173 } else {
1174 draw_rect(fe, bx+x1, by+y1, w, h, COL_BARRIER);
1175 }
1176 }
1177
1178 static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile,
1179 float xshift, float yshift)
1180 {
1181 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (xshift * TILE_SIZE);
1182 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (yshift * TILE_SIZE);
1183 float cx, cy, ex, ey;
1184 int dir, col;
1185
1186 /*
1187 * When we draw a single tile, we must draw everything up to
1188 * and including the borders around the tile. This means that
1189 * if the neighbouring tiles have connections to those borders,
1190 * we must draw those connections on the borders themselves.
1191 *
1192 * This would be terribly fiddly if we ever had to draw a tile
1193 * while its neighbour was in mid-rotate, because we'd have to
1194 * arrange to _know_ that the neighbour was being rotated and
1195 * hence had an anomalous effect on the redraw of this tile.
1196 * Fortunately, the drawing algorithm avoids ever calling us in
1197 * this circumstance: we're either drawing lots of straight
1198 * tiles at game start or after a move is complete, or we're
1199 * repeatedly drawing only the rotating tile. So no problem.
1200 */
1201
1202 /*
1203 * So. First blank the tile out completely: draw a big
1204 * rectangle in border colour, and a smaller rectangle in
1205 * background colour to fill it in.
1206 */
1207 draw_rect(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
1208 COL_BORDER);
1209 draw_rect(fe, bx+TILE_BORDER, by+TILE_BORDER,
1210 TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
1211 tile & FLASHING ? COL_FLASHING : COL_BACKGROUND);
1212
1213 /*
1214 * Draw the wires.
1215 */
1216 cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F;
1217 col = (tile & ACTIVE ? COL_POWERED : COL_WIRE);
1218 for (dir = 1; dir < 0x10; dir <<= 1) {
1219 if (tile & dir) {
1220 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1221 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1222 draw_thick_line(fe, bx+(int)cx, by+(int)cy,
1223 bx+(int)(cx+ex), by+(int)(cy+ey),
1224 COL_WIRE);
1225 }
1226 }
1227 for (dir = 1; dir < 0x10; dir <<= 1) {
1228 if (tile & dir) {
1229 ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1230 ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1231 draw_line(fe, bx+(int)cx, by+(int)cy,
1232 bx+(int)(cx+ex), by+(int)(cy+ey), col);
1233 }
1234 }
1235
1236 /*
1237 * Draw the box in the middle. We do this in blue if the tile
1238 * is an unpowered endpoint, in cyan if the tile is a powered
1239 * endpoint, in black if the tile is the centrepiece, and
1240 * otherwise not at all.
1241 */
1242 col = -1;
1243 if (x == state->cx && y == state->cy)
1244 col = COL_WIRE;
1245 else if (COUNT(tile) == 1) {
1246 col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
1247 }
1248 if (col >= 0) {
1249 int i, points[8];
1250
1251 points[0] = +1; points[1] = +1;
1252 points[2] = +1; points[3] = -1;
1253 points[4] = -1; points[5] = -1;
1254 points[6] = -1; points[7] = +1;
1255
1256 for (i = 0; i < 8; i += 2) {
1257 ex = (TILE_SIZE * 0.24F) * points[i];
1258 ey = (TILE_SIZE * 0.24F) * points[i+1];
1259 points[i] = bx+(int)(cx+ex);
1260 points[i+1] = by+(int)(cy+ey);
1261 }
1262
1263 draw_polygon(fe, points, 4, TRUE, col);
1264 draw_polygon(fe, points, 4, FALSE, COL_WIRE);
1265 }
1266
1267 /*
1268 * Draw the points on the border if other tiles are connected
1269 * to us.
1270 */
1271 for (dir = 1; dir < 0x10; dir <<= 1) {
1272 int dx, dy, px, py, lx, ly, vx, vy, ox, oy;
1273
1274 dx = X(dir);
1275 dy = Y(dir);
1276
1277 ox = x + dx;
1278 oy = y + dy;
1279
1280 if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
1281 continue;
1282
1283 if (!(tile(state, ox, oy) & F(dir)))
1284 continue;
1285
1286 px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
1287 py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy);
1288 lx = dx * (TILE_BORDER-1);
1289 ly = dy * (TILE_BORDER-1);
1290 vx = (dy ? 1 : 0);
1291 vy = (dx ? 1 : 0);
1292
1293 if (xshift == 0.0 && yshift == 0.0 && (tile & dir)) {
1294 /*
1295 * If we are fully connected to the other tile, we must
1296 * draw right across the tile border. (We can use our
1297 * own ACTIVE state to determine what colour to do this
1298 * in: if we are fully connected to the other tile then
1299 * the two ACTIVE states will be the same.)
1300 */
1301 draw_rect_coords(fe, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
1302 draw_rect_coords(fe, px, py, px+lx, py+ly,
1303 (tile & ACTIVE) ? COL_POWERED : COL_WIRE);
1304 } else {
1305 /*
1306 * The other tile extends into our border, but isn't
1307 * actually connected to us. Just draw a single black
1308 * dot.
1309 */
1310 draw_rect_coords(fe, px, py, px, py, COL_WIRE);
1311 }
1312 }
1313
1314 draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1315 }
1316
1317 static void draw_tile_barriers(frontend *fe, game_state *state, int x, int y)
1318 {
1319 int phase;
1320 int dir;
1321 int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1322 int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1323 /*
1324 * Draw barrier corners, and then barriers.
1325 */
1326 for (phase = 0; phase < 2; phase++) {
1327 for (dir = 1; dir < 0x10; dir <<= 1)
1328 if (barrier(state, x, y) & (dir << 4))
1329 draw_barrier_corner(fe, x, y, dir << 4, phase);
1330 for (dir = 1; dir < 0x10; dir <<= 1)
1331 if (barrier(state, x, y) & dir)
1332 draw_barrier(fe, x, y, dir, phase);
1333 }
1334
1335 draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1336 }
1337
1338 static void draw_arrow(frontend *fe, int x, int y, int xdx, int xdy)
1339 {
1340 int coords[14];
1341 int ydy = -xdx, ydx = xdy;
1342
1343 x = x * TILE_SIZE + BORDER + WINDOW_OFFSET;
1344 y = y * TILE_SIZE + BORDER + WINDOW_OFFSET;
1345
1346 #define POINT(n, xx, yy) ( \
1347 coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \
1348 coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy)
1349
1350 POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */
1351 POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */
1352 POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */
1353 POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */
1354 POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */
1355 POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
1356 POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
1357
1358 draw_polygon(fe, coords, 7, TRUE, COL_LOWLIGHT);
1359 draw_polygon(fe, coords, 7, FALSE, COL_TEXT);
1360 }
1361
1362 static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
1363 game_state *state, int dir, game_ui *ui, float t, float ft)
1364 {
1365 int x, y, tx, ty, frame;
1366 unsigned char *active;
1367 float xshift = 0.0;
1368 float yshift = 0.0;
1369
1370 /*
1371 * Clear the screen and draw the exterior barrier lines if this
1372 * is our first call.
1373 */
1374 if (!ds->started) {
1375 int phase;
1376
1377 ds->started = TRUE;
1378
1379 draw_rect(fe, 0, 0,
1380 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
1381 BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
1382 COL_BACKGROUND);
1383 draw_update(fe, 0, 0,
1384 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
1385 BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
1386
1387 for (phase = 0; phase < 2; phase++) {
1388
1389 for (x = 0; x < ds->width; x++) {
1390 if (barrier(state, x, 0) & UL)
1391 draw_barrier_corner(fe, x, -1, LD, phase);
1392 if (barrier(state, x, 0) & RU)
1393 draw_barrier_corner(fe, x, -1, DR, phase);
1394 if (barrier(state, x, 0) & U)
1395 draw_barrier(fe, x, -1, D, phase);
1396 if (barrier(state, x, ds->height-1) & DR)
1397 draw_barrier_corner(fe, x, ds->height, RU, phase);
1398 if (barrier(state, x, ds->height-1) & LD)
1399 draw_barrier_corner(fe, x, ds->height, UL, phase);
1400 if (barrier(state, x, ds->height-1) & D)
1401 draw_barrier(fe, x, ds->height, U, phase);
1402 }
1403
1404 for (y = 0; y < ds->height; y++) {
1405 if (barrier(state, 0, y) & UL)
1406 draw_barrier_corner(fe, -1, y, RU, phase);
1407 if (barrier(state, 0, y) & LD)
1408 draw_barrier_corner(fe, -1, y, DR, phase);
1409 if (barrier(state, 0, y) & L)
1410 draw_barrier(fe, -1, y, R, phase);
1411 if (barrier(state, ds->width-1, y) & RU)
1412 draw_barrier_corner(fe, ds->width, y, UL, phase);
1413 if (barrier(state, ds->width-1, y) & DR)
1414 draw_barrier_corner(fe, ds->width, y, LD, phase);
1415 if (barrier(state, ds->width-1, y) & R)
1416 draw_barrier(fe, ds->width, y, L, phase);
1417 }
1418 }
1419
1420 /*
1421 * Arrows for making moves.
1422 */
1423 for (x = 0; x < ds->width; x++) {
1424 if (x == state->cx) continue;
1425 draw_arrow(fe, x, 0, +1, 0);
1426 draw_arrow(fe, x+1, ds->height, -1, 0);
1427 }
1428 for (y = 0; y < ds->height; y++) {
1429 if (y == state->cy) continue;
1430 draw_arrow(fe, ds->width, y, 0, +1);
1431 draw_arrow(fe, 0, y+1, 0, -1);
1432 }
1433 }
1434
1435 /* Check if this is an undo. If so, we will need to run any animation
1436 * backwards.
1437 */
1438 if (oldstate && oldstate->move_count > state->move_count) {
1439 game_state * tmpstate = state;
1440 state = oldstate;
1441 oldstate = tmpstate;
1442 t = ANIM_TIME - t;
1443 }
1444
1445 tx = ty = -1;
1446 if (oldstate && (t < ANIM_TIME)) {
1447 /*
1448 * We're animating a slide, of row/column number
1449 * state->last_move_pos, in direction
1450 * state->last_move_dir
1451 */
1452 xshift = state->last_move_row == -1 ? 0.0 :
1453 (1 - t / ANIM_TIME) * state->last_move_dir;
1454 yshift = state->last_move_col == -1 ? 0.0 :
1455 (1 - t / ANIM_TIME) * state->last_move_dir;
1456 }
1457
1458 frame = -1;
1459 if (ft > 0) {
1460 /*
1461 * We're animating a completion flash. Find which frame
1462 * we're at.
1463 */
1464 frame = (int)(ft / FLASH_FRAME);
1465 }
1466
1467 /*
1468 * Draw any tile which differs from the way it was last drawn.
1469 */
1470 if (xshift != 0.0 || yshift != 0.0) {
1471 active = compute_active(state,
1472 state->last_move_row, state->last_move_col);
1473 } else {
1474 active = compute_active(state, -1, -1);
1475 }
1476
1477 clip(fe,
1478 BORDER + WINDOW_OFFSET, BORDER + WINDOW_OFFSET,
1479 TILE_SIZE * state->width + TILE_BORDER,
1480 TILE_SIZE * state->height + TILE_BORDER);
1481
1482 for (x = 0; x < ds->width; x++)
1483 for (y = 0; y < ds->height; y++) {
1484 unsigned char c = tile(state, x, y) | index(state, active, x, y);
1485
1486 /*
1487 * In a completion flash, we adjust the FLASHING bit
1488 * depending on our distance from the centre point and
1489 * the frame number.
1490 */
1491 if (frame >= 0) {
1492 int xdist, ydist, dist;
1493 xdist = (x < state->cx ? state->cx - x : x - state->cx);
1494 ydist = (y < state->cy ? state->cy - y : y - state->cy);
1495 dist = (xdist > ydist ? xdist : ydist);
1496
1497 if (frame >= dist && frame < dist+4) {
1498 int flash = (frame - dist) & 1;
1499 flash = flash ? FLASHING : 0;
1500 c = (c &~ FLASHING) | flash;
1501 }
1502 }
1503
1504 if (index(state, ds->visible, x, y) != c ||
1505 index(state, ds->visible, x, y) == 0xFF ||
1506 (x == state->last_move_col || y == state->last_move_row))
1507 {
1508 float xs = (y == state->last_move_row ? xshift : 0.0);
1509 float ys = (x == state->last_move_col ? yshift : 0.0);
1510
1511 draw_tile(fe, state, x, y, c, xs, ys);
1512 if (xs < 0 && x == 0)
1513 draw_tile(fe, state, state->width, y, c, xs, ys);
1514 else if (xs > 0 && x == state->width - 1)
1515 draw_tile(fe, state, -1, y, c, xs, ys);
1516 else if (ys < 0 && y == 0)
1517 draw_tile(fe, state, x, state->height, c, xs, ys);
1518 else if (ys > 0 && y == state->height - 1)
1519 draw_tile(fe, state, x, -1, c, xs, ys);
1520
1521 if (x == state->last_move_col || y == state->last_move_row)
1522 index(state, ds->visible, x, y) = 0xFF;
1523 else
1524 index(state, ds->visible, x, y) = c;
1525 }
1526 }
1527
1528 for (x = 0; x < ds->width; x++)
1529 for (y = 0; y < ds->height; y++)
1530 draw_tile_barriers(fe, state, x, y);
1531
1532 unclip(fe);
1533
1534 /*
1535 * Update the status bar.
1536 */
1537 {
1538 char statusbuf[256];
1539 int i, n, a;
1540
1541 n = state->width * state->height;
1542 for (i = a = 0; i < n; i++)
1543 if (active[i])
1544 a++;
1545
1546 if (state->used_solve)
1547 sprintf(statusbuf, "Moves since auto-solve: %d",
1548 state->move_count - state->completed);
1549 else
1550 sprintf(statusbuf, "%sMoves: %d",
1551 (state->completed ? "COMPLETED! " : ""),
1552 (state->completed ? state->completed : state->move_count));
1553
1554 sprintf(statusbuf + strlen(statusbuf), " Active: %d/%d", a, n);
1555
1556 status_bar(fe, statusbuf);
1557 }
1558
1559 sfree(active);
1560 }
1561
1562 static float game_anim_length(game_state *oldstate,
1563 game_state *newstate, int dir)
1564 {
1565 /*
1566 * Don't animate an auto-solve move.
1567 */
1568 if ((dir > 0 && newstate->just_used_solve) ||
1569 (dir < 0 && oldstate->just_used_solve))
1570 return 0.0F;
1571
1572 return ANIM_TIME;
1573 }
1574
1575 static float game_flash_length(game_state *oldstate,
1576 game_state *newstate, int dir)
1577 {
1578 /*
1579 * If the game has just been completed, we display a completion
1580 * flash.
1581 */
1582 if (!oldstate->completed && newstate->completed &&
1583 !oldstate->used_solve && !newstate->used_solve) {
1584 int size;
1585 size = 0;
1586 if (size < newstate->cx+1)
1587 size = newstate->cx+1;
1588 if (size < newstate->cy+1)
1589 size = newstate->cy+1;
1590 if (size < newstate->width - newstate->cx)
1591 size = newstate->width - newstate->cx;
1592 if (size < newstate->height - newstate->cy)
1593 size = newstate->height - newstate->cy;
1594 return FLASH_FRAME * (size+4);
1595 }
1596
1597 return 0.0F;
1598 }
1599
1600 static int game_wants_statusbar(void)
1601 {
1602 return TRUE;
1603 }
1604
1605 #ifdef COMBINED
1606 #define thegame netslide
1607 #endif
1608
1609 const struct game thegame = {
1610 "Netslide", "games.netslide",
1611 default_params,
1612 game_fetch_preset,
1613 decode_params,
1614 encode_params,
1615 free_params,
1616 dup_params,
1617 TRUE, game_configure, custom_params,
1618 validate_params,
1619 new_game_seed,
1620 game_free_aux_info,
1621 validate_seed,
1622 new_game,
1623 dup_game,
1624 free_game,
1625 TRUE, solve_game,
1626 FALSE, game_text_format,
1627 new_ui,
1628 free_ui,
1629 make_move,
1630 game_size,
1631 game_colours,
1632 game_new_drawstate,
1633 game_free_drawstate,
1634 game_redraw,
1635 game_anim_length,
1636 game_flash_length,
1637 game_wants_statusbar,
1638 };