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