A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/*
2 * net.c: Net game.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <limits.h>
11#ifdef NO_TGMATH_H
12# include <math.h>
13#else
14# include <tgmath.h>
15#endif
16
17#include "puzzles.h"
18#include "tree234.h"
19
20/*
21 * The standard user interface for Net simply has left- and
22 * right-button mouse clicks in a square rotate it one way or the
23 * other. We also provide, by #ifdef, a separate interface based on
24 * rotational dragging motions. I initially developed this for the
25 * Mac on the basis that it might work better than the click
26 * interface with only one mouse button available, but in fact
27 * found it to be quite strange and unintuitive. Apparently it
28 * works better on stylus-driven platforms such as Palm and
29 * PocketPC, though, so we enable it by default there.
30 */
31#ifdef STYLUS_BASED
32#define USE_DRAGGING
33#endif
34
35/* Direction and other bitfields */
36#define R 0x01
37#define U 0x02
38#define L 0x04
39#define D 0x08
40#define LOCKED 0x10
41#define ACTIVE 0x20
42#define RERR (R << 6)
43#define UERR (U << 6)
44#define LERR (L << 6)
45#define DERR (D << 6)
46#define ERR(dir) ((dir) << 6)
47
48/* Rotations: Anticlockwise, Clockwise, Flip, general rotate */
49#define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) )
50#define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) )
51#define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) )
52#define ROT(x, n) ( ((n)&3) == 0 ? (x) : \
53 ((n)&3) == 1 ? A(x) : \
54 ((n)&3) == 2 ? F(x) : C(x) )
55
56/* X and Y displacements */
57#define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 )
58#define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 )
59
60/* Bit count */
61#define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \
62 (((x) & 0x02) >> 1) + ((x) & 0x01) )
63
64#define PREFERRED_TILE_SIZE 32
65#define TILE_SIZE (ds->tilesize)
66#define LINE_THICK ((TILE_SIZE+47)/48)
67#ifdef SMALL_SCREEN
68#define WINDOW_OFFSET 4
69#else
70#define WINDOW_OFFSET 16
71#endif
72
73#define ROTATE_TIME 0.13F
74#define FLASH_FRAME 0.07F
75
76enum {
77 COL_BACKGROUND,
78 COL_LOCKED,
79 COL_BORDER,
80 COL_WIRE,
81 COL_ENDPOINT,
82 COL_POWERED,
83 COL_BARRIER,
84 COL_ERR,
85 NCOLOURS
86};
87
88enum {
89 PREF_UNLOCKED_LOOPS,
90 N_PREF_ITEMS
91};
92
93struct game_params {
94 int width;
95 int height;
96 bool wrapping;
97 bool unique;
98 float barrier_probability;
99};
100
101typedef struct game_immutable_state {
102 int refcount;
103 unsigned char *barriers;
104} game_immutable_state;
105
106struct game_state {
107 int width, height;
108 bool wrapping, completed;
109 int last_rotate_x, last_rotate_y, last_rotate_dir;
110 bool used_solve;
111 unsigned char *tiles;
112 struct game_immutable_state *imm;
113};
114
115#define OFFSETWH(x2,y2,x1,y1,dir,width,height) \
116 ( (x2) = ((x1) + width + X((dir))) % width, \
117 (y2) = ((y1) + height + Y((dir))) % height)
118
119#define OFFSET(x2,y2,x1,y1,dir,state) \
120 OFFSETWH(x2,y2,x1,y1,dir,(state)->width,(state)->height)
121
122#define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
123#define tile(state, x, y) index(state, (state)->tiles, x, y)
124#define barrier(state, x, y) index(state, (state)->imm->barriers, x, y)
125
126struct xyd {
127 int x, y, direction;
128};
129
130static int xyd_cmp(const void *av, const void *bv) {
131 const struct xyd *a = (const struct xyd *)av;
132 const struct xyd *b = (const struct xyd *)bv;
133 if (a->x < b->x)
134 return -1;
135 if (a->x > b->x)
136 return +1;
137 if (a->y < b->y)
138 return -1;
139 if (a->y > b->y)
140 return +1;
141 if (a->direction < b->direction)
142 return -1;
143 if (a->direction > b->direction)
144 return +1;
145 return 0;
146}
147
148static int xyd_cmp_nc(void *av, void *bv) { return xyd_cmp(av, bv); }
149
150static struct xyd *new_xyd(int x, int y, int direction)
151{
152 struct xyd *xyd = snew(struct xyd);
153 xyd->x = x;
154 xyd->y = y;
155 xyd->direction = direction;
156 return xyd;
157}
158
159/* ----------------------------------------------------------------------
160 * Manage game parameters.
161 */
162static game_params *default_params(void)
163{
164 game_params *ret = snew(game_params);
165
166 ret->width = 5;
167 ret->height = 5;
168 ret->wrapping = false;
169 ret->unique = true;
170 ret->barrier_probability = 0.0;
171
172 return ret;
173}
174
175static const struct game_params net_presets[] = {
176 {5, 5, false, true, 0.0},
177 {7, 7, false, true, 0.0},
178 {9, 9, false, true, 0.0},
179 {11, 11, false, true, 0.0},
180#ifndef SMALL_SCREEN
181 {13, 11, false, true, 0.0},
182#endif
183 {5, 5, true, true, 0.0},
184 {7, 7, true, true, 0.0},
185 {9, 9, true, true, 0.0},
186 {11, 11, true, true, 0.0},
187#ifndef SMALL_SCREEN
188 {13, 11, true, true, 0.0},
189#endif
190};
191
192static bool game_fetch_preset(int i, char **name, game_params **params)
193{
194 game_params *ret;
195 char str[80];
196
197 if (i < 0 || i >= lenof(net_presets))
198 return false;
199
200 ret = snew(game_params);
201 *ret = net_presets[i];
202
203 sprintf(str, "%dx%d%s", ret->width, ret->height,
204 ret->wrapping ? " wrapping" : "");
205
206 *name = dupstr(str);
207 *params = ret;
208 return true;
209}
210
211static void free_params(game_params *params)
212{
213 sfree(params);
214}
215
216static game_params *dup_params(const game_params *params)
217{
218 game_params *ret = snew(game_params);
219 *ret = *params; /* structure copy */
220 return ret;
221}
222
223static void decode_params(game_params *ret, char const *string)
224{
225 char const *p = string;
226
227 ret->width = atoi(p);
228 while (*p && isdigit((unsigned char)*p)) p++;
229 if (*p == 'x') {
230 p++;
231 ret->height = atoi(p);
232 while (*p && isdigit((unsigned char)*p)) p++;
233 } else {
234 ret->height = ret->width;
235 }
236
237 while (*p) {
238 if (*p == 'w') {
239 p++;
240 ret->wrapping = true;
241 } else if (*p == 'b') {
242 p++;
243 ret->barrier_probability = (float)atof(p);
244 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
245 } else if (*p == 'a') {
246 p++;
247 ret->unique = false;
248 } else
249 p++; /* skip any other gunk */
250 }
251}
252
253static char *encode_params(const game_params *params, bool full)
254{
255 char ret[400];
256 int len;
257
258 len = sprintf(ret, "%dx%d", params->width, params->height);
259 if (params->wrapping)
260 ret[len++] = 'w';
261 if (full && params->barrier_probability)
262 len += sprintf(ret+len, "b%g", params->barrier_probability);
263 if (full && !params->unique)
264 ret[len++] = 'a';
265 assert(len < lenof(ret));
266 ret[len] = '\0';
267
268 return dupstr(ret);
269}
270
271static config_item *game_configure(const game_params *params)
272{
273 config_item *ret;
274 char buf[80];
275
276 ret = snewn(6, config_item);
277
278 ret[0].name = "Width";
279 ret[0].type = C_STRING;
280 sprintf(buf, "%d", params->width);
281 ret[0].u.string.sval = dupstr(buf);
282
283 ret[1].name = "Height";
284 ret[1].type = C_STRING;
285 sprintf(buf, "%d", params->height);
286 ret[1].u.string.sval = dupstr(buf);
287
288 ret[2].name = "Walls wrap around";
289 ret[2].type = C_BOOLEAN;
290 ret[2].u.boolean.bval = params->wrapping;
291
292 ret[3].name = "Barrier probability";
293 ret[3].type = C_STRING;
294 sprintf(buf, "%g", params->barrier_probability);
295 ret[3].u.string.sval = dupstr(buf);
296
297 ret[4].name = "Ensure unique solution";
298 ret[4].type = C_BOOLEAN;
299 ret[4].u.boolean.bval = params->unique;
300
301 ret[5].name = NULL;
302 ret[5].type = C_END;
303
304 return ret;
305}
306
307static game_params *custom_params(const config_item *cfg)
308{
309 game_params *ret = snew(game_params);
310
311 ret->width = atoi(cfg[0].u.string.sval);
312 ret->height = atoi(cfg[1].u.string.sval);
313 ret->wrapping = cfg[2].u.boolean.bval;
314 ret->barrier_probability = (float)atof(cfg[3].u.string.sval);
315 ret->unique = cfg[4].u.boolean.bval;
316
317 return ret;
318}
319
320static const char *validate_params(const game_params *params, bool full)
321{
322 if (params->width <= 0 || params->height <= 0)
323 return "Width and height must both be greater than zero";
324 if (params->width <= 1 && params->height <= 1)
325 return "At least one of width and height must be greater than one";
326 if (params->width > INT_MAX / params->height)
327 return "Width times height must not be unreasonably large";
328 if (params->barrier_probability < 0)
329 return "Barrier probability may not be negative";
330 if (params->barrier_probability > 1)
331 return "Barrier probability may not be greater than 1";
332
333 /*
334 * Specifying either grid dimension as 2 in a wrapping puzzle
335 * makes it actually impossible to ensure a unique puzzle
336 * solution.
337 *
338 * Proof:
339 *
340 * Without loss of generality, let us assume the puzzle _width_
341 * is 2, so we can conveniently discuss rows without having to
342 * say `rows/columns' all the time. (The height may be 2 as
343 * well, but that doesn't matter.)
344 *
345 * In each row, there are two edges between tiles: the inner
346 * edge (running down the centre of the grid) and the outer
347 * edge (the identified left and right edges of the grid).
348 *
349 * Lemma: In any valid 2xn puzzle there must be at least one
350 * row in which _exactly one_ of the inner edge and outer edge
351 * is connected.
352 *
353 * Proof: No row can have _both_ inner and outer edges
354 * connected, because this would yield a loop. So the only
355 * other way to falsify the lemma is for every row to have
356 * _neither_ the inner nor outer edge connected. But this
357 * means there is no connection at all between the left and
358 * right columns of the puzzle, so there are two disjoint
359 * subgraphs, which is also disallowed. []
360 *
361 * Given such a row, it is always possible to make the
362 * disconnected edge connected and the connected edge
363 * disconnected without changing the state of any other edge.
364 * (This is easily seen by case analysis on the various tiles:
365 * left-pointing and right-pointing endpoints can be exchanged,
366 * likewise T-pieces, and a corner piece can select its
367 * horizontal connectivity independently of its vertical.) This
368 * yields a distinct valid solution.
369 *
370 * Thus, for _every_ row in which exactly one of the inner and
371 * outer edge is connected, there are two valid states for that
372 * row, and hence the total number of solutions of the puzzle
373 * is at least 2^(number of such rows), and in particular is at
374 * least 2 since there must be at least one such row. []
375 */
376 if (full && params->unique && params->wrapping &&
377 (params->width == 2 || params->height == 2))
378 return "No wrapping puzzle with a width or height of 2 can have"
379 " a unique solution";
380
381 return NULL;
382}
383
384/* ----------------------------------------------------------------------
385 * Solver used to assure solution uniqueness during generation.
386 */
387
388/*
389 * Test cases I used while debugging all this were
390 *
391 * ./net --generate 1 13x11w#12300
392 * which expands under the non-unique grid generation rules to
393 * 13x11w:5eaade1bd222664436d5e2965c12656b1129dd825219e3274d558d5eb2dab5da18898e571d5a2987be79746bd95726c597447d6da96188c513add829da7681da954db113d3cd244
394 * and has two ambiguous areas.
395 *
396 * An even better one is
397 * 13x11w#507896411361192
398 * which expands to
399 * 13x11w:b7125b1aec598eb31bd58d82572bc11494e5dee4e8db2bdd29b88d41a16bdd996d2996ddec8c83741a1e8674e78328ba71737b8894a9271b1cd1399453d1952e43951d9b712822e
400 * and has an ambiguous area _and_ a situation where loop avoidance
401 * is a necessary deductive technique.
402 *
403 * Then there's
404 * 48x25w#820543338195187
405 * becoming
406 * 48x25w:255989d14cdd185deaa753a93821a12edc1ab97943ac127e2685d7b8b3c48861b2192416139212b316eddd35de43714ebc7628d753db32e596284d9ec52c5a7dc1b4c811a655117d16dc28921b2b4161352cab1d89d18bc836b8b891d55ea4622a1251861b5bc9a8aa3e5bcd745c95229ca6c3b5e21d5832d397e917325793d7eb442dc351b2db2a52ba8e1651642275842d8871d5534aabc6d5b741aaa2d48ed2a7dbbb3151ddb49d5b9a7ed1ab98ee75d613d656dbba347bc514c84556b43a9bc65a3256ead792488b862a9d2a8a39b4255a4949ed7dbd79443292521265896b4399c95ede89d7c8c797a6a57791a849adea489359a158aa12e5dacce862b8333b7ebea7d344d1a3c53198864b73a9dedde7b663abb1b539e1e8853b1b7edb14a2a17ebaae4dbe63598a2e7e9a2dbdad415bc1d8cb88cbab5a8c82925732cd282e641ea3bd7d2c6e776de9117a26be86deb7c82c89524b122cb9397cd1acd2284e744ea62b9279bae85479ababe315c3ac29c431333395b24e6a1e3c43a2da42d4dce84aadd5b154aea555eaddcbd6e527d228c19388d9b424d94214555a7edbdeebe569d4a56dc51a86bd9963e377bb74752bd5eaa5761ba545e297b62a1bda46ab4aee423ad6c661311783cc18786d4289236563cb4a75ec67d481c14814994464cd1b87396dee63e5ab6e952cc584baa1d4c47cb557ec84dbb63d487c8728118673a166846dd3a4ebc23d6cb9c5827d96b4556e91899db32b517eda815ae271a8911bd745447121dc8d321557bc2a435ebec1bbac35b1a291669451174e6aa2218a4a9c5a6ca31ebc45d84e3a82c121e9ced7d55e9a
407 * which has a spot (far right) where slightly more complex loop
408 * avoidance is required.
409 */
410
411struct todo {
412 bool *marked;
413 int *buffer;
414 int buflen;
415 int head, tail;
416};
417
418static struct todo *todo_new(int maxsize)
419{
420 struct todo *todo = snew(struct todo);
421 todo->marked = snewn(maxsize, bool);
422 memset(todo->marked, 0, maxsize);
423 todo->buflen = maxsize + 1;
424 todo->buffer = snewn(todo->buflen, int);
425 todo->head = todo->tail = 0;
426 return todo;
427}
428
429static void todo_free(struct todo *todo)
430{
431 sfree(todo->marked);
432 sfree(todo->buffer);
433 sfree(todo);
434}
435
436static void todo_add(struct todo *todo, int index)
437{
438 if (todo->marked[index])
439 return; /* already on the list */
440 todo->marked[index] = true;
441 todo->buffer[todo->tail++] = index;
442 if (todo->tail == todo->buflen)
443 todo->tail = 0;
444}
445
446static int todo_get(struct todo *todo) {
447 int ret;
448
449 if (todo->head == todo->tail)
450 return -1; /* list is empty */
451 ret = todo->buffer[todo->head++];
452 if (todo->head == todo->buflen)
453 todo->head = 0;
454 todo->marked[ret] = false;
455
456 return ret;
457}
458
459/*
460 * Return values: -1 means puzzle was proved inconsistent, 0 means we
461 * failed to narrow down to a unique solution, +1 means we solved it
462 * fully.
463 */
464static int net_solver(int w, int h, unsigned char *tiles,
465 unsigned char *barriers, bool wrapping)
466{
467 unsigned char *tilestate;
468 unsigned char *edgestate;
469 int *deadends;
470 DSF *equivalence;
471 struct todo *todo;
472 int i, j, x, y;
473 int area;
474 bool done_something;
475
476 /*
477 * Set up the solver's data structures.
478 */
479
480 /*
481 * tilestate stores the possible orientations of each tile.
482 * There are up to four of these, so we'll index the array in
483 * fours. tilestate[(y * w + x) * 4] and its three successive
484 * members give the possible orientations, clearing to 255 from
485 * the end as things are ruled out.
486 *
487 * In this loop we also count up the area of the grid (which is
488 * not _necessarily_ equal to w*h, because there might be one
489 * or more blank squares present. This will never happen in a
490 * grid generated _by_ this program, but it's worth keeping the
491 * solver as general as possible.)
492 */
493 tilestate = snewn(w * h * 4, unsigned char);
494 area = 0;
495 for (i = 0; i < w*h; i++) {
496 tilestate[i * 4] = tiles[i] & 0xF;
497 for (j = 1; j < 4; j++) {
498 if (tilestate[i * 4 + j - 1] == 255 ||
499 A(tilestate[i * 4 + j - 1]) == tilestate[i * 4])
500 tilestate[i * 4 + j] = 255;
501 else
502 tilestate[i * 4 + j] = A(tilestate[i * 4 + j - 1]);
503 }
504 if (tiles[i] != 0)
505 area++;
506 }
507
508 /*
509 * edgestate stores the known state of each edge. It is 0 for
510 * unknown, 1 for open (connected) and 2 for closed (not
511 * connected).
512 *
513 * In principle we need only worry about each edge once each,
514 * but in fact it's easier to track each edge twice so that we
515 * can reference it from either side conveniently. Also I'm
516 * going to allocate _five_ bytes per tile, rather than the
517 * obvious four, so that I can index edgestate[(y*w+x) * 5 + d]
518 * where d is 1,2,4,8 and they never overlap.
519 */
520 edgestate = snewn((w * h - 1) * 5 + 9, unsigned char);
521 memset(edgestate, 0, (w * h - 1) * 5 + 9);
522
523 /*
524 * deadends tracks which edges have dead ends on them. It is
525 * indexed by tile and direction: deadends[(y*w+x) * 5 + d]
526 * tells you whether heading out of tile (x,y) in direction d
527 * can reach a limited amount of the grid. Values are area+1
528 * (no dead end known) or less than that (can reach _at most_
529 * this many other tiles by heading this way out of this tile).
530 */
531 deadends = snewn((w * h - 1) * 5 + 9, int);
532 for (i = 0; i < (w * h - 1) * 5 + 9; i++)
533 deadends[i] = area+1;
534
535 /*
536 * equivalence tracks which sets of tiles are known to be
537 * connected to one another, so we can avoid creating loops by
538 * linking together tiles which are already linked through
539 * another route.
540 *
541 * This is a disjoint set forest structure: equivalence[i]
542 * contains the index of another member of the equivalence
543 * class containing i, or contains i itself for precisely one
544 * member in each such class. To find a representative member
545 * of the equivalence class containing i, you keep replacing i
546 * with equivalence[i] until it stops changing; then you go
547 * _back_ along the same path and point everything on it
548 * directly at the representative member so as to speed up
549 * future searches. Then you test equivalence between tiles by
550 * finding the representative of each tile and seeing if
551 * they're the same; and you create new equivalence (merge
552 * classes) by finding the representative of each tile and
553 * setting equivalence[one]=the_other.
554 */
555 equivalence = dsf_new(w * h);
556
557 /*
558 * On a non-wrapping grid, we instantly know that all the edges
559 * round the edge are closed.
560 */
561 if (!wrapping) {
562 for (i = 0; i < w; i++) {
563 edgestate[i * 5 + 2] = edgestate[((h-1) * w + i) * 5 + 8] = 2;
564 }
565 for (i = 0; i < h; i++) {
566 edgestate[(i * w + w-1) * 5 + 1] = edgestate[(i * w) * 5 + 4] = 2;
567 }
568 }
569
570 /*
571 * If we have barriers available, we can mark those edges as
572 * closed too.
573 */
574 if (barriers) {
575 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
576 int d;
577 for (d = 1; d <= 8; d += d) {
578 if (barriers[y*w+x] & d) {
579 int x2, y2;
580 /*
581 * In principle the barrier list should already
582 * contain each barrier from each side, but
583 * let's not take chances with our internal
584 * consistency.
585 */
586 OFFSETWH(x2, y2, x, y, d, w, h);
587 edgestate[(y*w+x) * 5 + d] = 2;
588 edgestate[(y2*w+x2) * 5 + F(d)] = 2;
589 }
590 }
591 }
592 }
593
594 /*
595 * Since most deductions made by this solver are local (the
596 * exception is loop avoidance, where joining two tiles
597 * together on one side of the grid can theoretically permit a
598 * fresh deduction on the other), we can address the scaling
599 * problem inherent in iterating repeatedly over the entire
600 * grid by instead working with a to-do list.
601 */
602 todo = todo_new(w * h);
603
604 /*
605 * Main deductive loop.
606 */
607 done_something = true; /* prevent instant termination! */
608 while (1) {
609 int index;
610
611 /*
612 * Take a tile index off the todo list and process it.
613 */
614 index = todo_get(todo);
615 if (index == -1) {
616 /*
617 * If we have run out of immediate things to do, we
618 * have no choice but to scan the whole grid for
619 * longer-range things we've missed. Hence, I now add
620 * every square on the grid back on to the to-do list.
621 * I also set `done_something' to false at this point;
622 * if we later come back here and find it still false,
623 * we will know we've scanned the entire grid without
624 * finding anything new to do, and we can terminate.
625 */
626 if (!done_something)
627 break;
628 for (i = 0; i < w*h; i++)
629 todo_add(todo, i);
630 done_something = false;
631
632 index = todo_get(todo);
633 }
634
635 y = index / w;
636 x = index % w;
637 {
638 int d, ourclass = dsf_canonify(equivalence, y*w+x);
639 int deadendmax[9];
640
641 deadendmax[1] = deadendmax[2] = deadendmax[4] = deadendmax[8] = 0;
642
643 for (i = j = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) {
644 bool valid;
645 int nnondeadends, nondeadends[4], deadendtotal;
646 int nequiv, equiv[5];
647 int val = tilestate[(y*w+x) * 4 + i];
648
649 valid = true;
650 nnondeadends = deadendtotal = 0;
651 equiv[0] = ourclass;
652 nequiv = 1;
653 for (d = 1; d <= 8; d += d) {
654 /*
655 * Immediately rule out this orientation if it
656 * conflicts with any known edge.
657 */
658 if ((edgestate[(y*w+x) * 5 + d] == 1 && !(val & d)) ||
659 (edgestate[(y*w+x) * 5 + d] == 2 && (val & d)))
660 valid = false;
661
662 if (val & d) {
663 /*
664 * Count up the dead-end statistics.
665 */
666 if (deadends[(y*w+x) * 5 + d] <= area) {
667 deadendtotal += deadends[(y*w+x) * 5 + d];
668 } else {
669 nondeadends[nnondeadends++] = d;
670 }
671
672 /*
673 * Ensure we aren't linking to any tiles,
674 * through edges not already known to be
675 * open, which create a loop.
676 */
677 if (edgestate[(y*w+x) * 5 + d] == 0) {
678 int c, k, x2, y2;
679
680 OFFSETWH(x2, y2, x, y, d, w, h);
681 c = dsf_canonify(equivalence, y2*w+x2);
682 for (k = 0; k < nequiv; k++)
683 if (c == equiv[k])
684 break;
685 if (k == nequiv)
686 equiv[nequiv++] = c;
687 else
688 valid = false;
689 }
690 }
691 }
692
693 if (nnondeadends == 0) {
694 /*
695 * If this orientation links together dead-ends
696 * with a total area of less than the entire
697 * grid, it is invalid.
698 *
699 * (We add 1 to deadendtotal because of the
700 * tile itself, of course; one tile linking
701 * dead ends of size 2 and 3 forms a subnetwork
702 * with a total area of 6, not 5.)
703 */
704 if (deadendtotal > 0 && deadendtotal+1 < area)
705 valid = false;
706 } else if (nnondeadends == 1) {
707 /*
708 * If this orientation links together one or
709 * more dead-ends with precisely one
710 * non-dead-end, then we may have to mark that
711 * non-dead-end as a dead end going the other
712 * way. However, it depends on whether all
713 * other orientations share the same property.
714 */
715 deadendtotal++;
716 if (deadendmax[nondeadends[0]] < deadendtotal)
717 deadendmax[nondeadends[0]] = deadendtotal;
718 } else {
719 /*
720 * If this orientation links together two or
721 * more non-dead-ends, then we can rule out the
722 * possibility of putting in new dead-end
723 * markings in those directions.
724 */
725 int k;
726 for (k = 0; k < nnondeadends; k++)
727 deadendmax[nondeadends[k]] = area+1;
728 }
729
730 if (valid)
731 tilestate[(y*w+x) * 4 + j++] = val;
732#ifdef SOLVER_DIAGNOSTICS
733 else
734 printf("ruling out orientation %x at %d,%d\n", val, x, y);
735#endif
736 }
737
738 if (j == 0) {
739 /* If we've ruled out all possible orientations for a
740 * tile, then our puzzle has no solution at all. */
741 return -1;
742 }
743
744 if (j < i) {
745 done_something = true;
746
747 /*
748 * We have ruled out at least one tile orientation.
749 * Make sure the rest are blanked.
750 */
751 while (j < 4)
752 tilestate[(y*w+x) * 4 + j++] = 255;
753 }
754
755 /*
756 * Now go through the tile orientations again and see
757 * if we've deduced anything new about any edges.
758 */
759 {
760 int a, o;
761 a = 0xF; o = 0;
762
763 for (i = 0; i < 4 && tilestate[(y*w+x) * 4 + i] != 255; i++) {
764 a &= tilestate[(y*w+x) * 4 + i];
765 o |= tilestate[(y*w+x) * 4 + i];
766 }
767 for (d = 1; d <= 8; d += d)
768 if (edgestate[(y*w+x) * 5 + d] == 0) {
769 int x2, y2, d2;
770 OFFSETWH(x2, y2, x, y, d, w, h);
771 d2 = F(d);
772 if (a & d) {
773 /* This edge is open in all orientations. */
774#ifdef SOLVER_DIAGNOSTICS
775 printf("marking edge %d,%d:%d open\n", x, y, d);
776#endif
777 edgestate[(y*w+x) * 5 + d] = 1;
778 edgestate[(y2*w+x2) * 5 + d2] = 1;
779 dsf_merge(equivalence, y*w+x, y2*w+x2);
780 done_something = true;
781 todo_add(todo, y2*w+x2);
782 } else if (!(o & d)) {
783 /* This edge is closed in all orientations. */
784#ifdef SOLVER_DIAGNOSTICS
785 printf("marking edge %d,%d:%d closed\n", x, y, d);
786#endif
787 edgestate[(y*w+x) * 5 + d] = 2;
788 edgestate[(y2*w+x2) * 5 + d2] = 2;
789 done_something = true;
790 todo_add(todo, y2*w+x2);
791 }
792 }
793
794 }
795
796 /*
797 * Now check the dead-end markers and see if any of
798 * them has lowered from the real ones.
799 */
800 for (d = 1; d <= 8; d += d) {
801 int x2, y2, d2;
802 OFFSETWH(x2, y2, x, y, d, w, h);
803 d2 = F(d);
804 if (deadendmax[d] > 0 &&
805 deadends[(y2*w+x2) * 5 + d2] > deadendmax[d]) {
806#ifdef SOLVER_DIAGNOSTICS
807 printf("setting dead end value %d,%d:%d to %d\n",
808 x2, y2, d2, deadendmax[d]);
809#endif
810 deadends[(y2*w+x2) * 5 + d2] = deadendmax[d];
811 done_something = true;
812 todo_add(todo, y2*w+x2);
813 }
814 }
815
816 }
817 }
818
819 /*
820 * Mark all completely determined tiles as locked.
821 */
822 j = +1;
823 for (i = 0; i < w*h; i++) {
824 if (tilestate[i * 4 + 1] == 255) {
825 assert(tilestate[i * 4 + 0] != 255);
826 tiles[i] = tilestate[i * 4] | LOCKED;
827 } else {
828 tiles[i] &= ~LOCKED;
829 j = 0;
830 }
831 }
832
833 /*
834 * Free up working space.
835 */
836 todo_free(todo);
837 sfree(tilestate);
838 sfree(edgestate);
839 sfree(deadends);
840 dsf_free(equivalence);
841
842 return j;
843}
844
845/* ----------------------------------------------------------------------
846 * Randomly select a new game description.
847 */
848
849/*
850 * Function to randomly perturb an ambiguous section in a grid, to
851 * attempt to ensure unique solvability.
852 */
853static void perturb(int w, int h, unsigned char *tiles, bool wrapping,
854 random_state *rs, int startx, int starty, int startd)
855{
856 struct xyd *perimeter, *perim2, *loop[2], looppos[2];
857 int nperim, perimsize, nloop[2], loopsize[2];
858 int x, y, d, i;
859
860 /*
861 * We know that the tile at (startx,starty) is part of an
862 * ambiguous section, and we also know that its neighbour in
863 * direction startd is fully specified. We begin by tracing all
864 * the way round the ambiguous area.
865 */
866 nperim = perimsize = 0;
867 perimeter = NULL;
868 x = startx;
869 y = starty;
870 d = startd;
871#ifdef PERTURB_DIAGNOSTICS
872 printf("perturb %d,%d:%d\n", x, y, d);
873#endif
874 do {
875 int x2, y2, d2;
876
877 if (nperim >= perimsize) {
878 perimsize = perimsize * 3 / 2 + 32;
879 perimeter = sresize(perimeter, perimsize, struct xyd);
880 }
881 perimeter[nperim].x = x;
882 perimeter[nperim].y = y;
883 perimeter[nperim].direction = d;
884 nperim++;
885#ifdef PERTURB_DIAGNOSTICS
886 printf("perimeter: %d,%d:%d\n", x, y, d);
887#endif
888
889 /*
890 * First, see if we can simply turn left from where we are
891 * and find another locked square.
892 */
893 d2 = A(d);
894 OFFSETWH(x2, y2, x, y, d2, w, h);
895 if ((!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1)) ||
896 (tiles[y2*w+x2] & LOCKED)) {
897 d = d2;
898 } else {
899 /*
900 * Failing that, step left into the new square and look
901 * in front of us.
902 */
903 x = x2;
904 y = y2;
905 OFFSETWH(x2, y2, x, y, d, w, h);
906 if ((wrapping || (abs(x2-x) <= 1 && abs(y2-y) <= 1)) &&
907 !(tiles[y2*w+x2] & LOCKED)) {
908 /*
909 * And failing _that_, we're going to have to step
910 * forward into _that_ square and look right at the
911 * same locked square as we started with.
912 */
913 x = x2;
914 y = y2;
915 d = C(d);
916 }
917 }
918
919 } while (x != startx || y != starty || d != startd);
920
921 /*
922 * Our technique for perturbing this ambiguous area is to
923 * search round its edge for a join we can make: that is, an
924 * edge on the perimeter which is (a) not currently connected,
925 * and (b) connecting it would not yield a full cross on either
926 * side. Then we make that join, search round the network to
927 * find the loop thus constructed, and sever the loop at a
928 * randomly selected other point.
929 */
930 perim2 = snewn(nperim, struct xyd);
931 memcpy(perim2, perimeter, nperim * sizeof(struct xyd));
932 /* Shuffle the perimeter, so as to search it without directional bias. */
933 shuffle(perim2, nperim, sizeof(*perim2), rs);
934 for (i = 0; i < nperim; i++) {
935 int x2, y2;
936
937 x = perim2[i].x;
938 y = perim2[i].y;
939 d = perim2[i].direction;
940
941 OFFSETWH(x2, y2, x, y, d, w, h);
942 if (!wrapping && (abs(x2-x) > 1 || abs(y2-y) > 1))
943 continue; /* can't link across non-wrapping border */
944 if (tiles[y*w+x] & d)
945 continue; /* already linked in this direction! */
946 if (((tiles[y*w+x] | d) & 15) == 15)
947 continue; /* can't turn this tile into a cross */
948 if (((tiles[y2*w+x2] | F(d)) & 15) == 15)
949 continue; /* can't turn other tile into a cross */
950
951 /*
952 * We've found the point at which we're going to make a new
953 * link.
954 */
955#ifdef PERTURB_DIAGNOSTICS
956 printf("linking %d,%d:%d\n", x, y, d);
957#endif
958 tiles[y*w+x] |= d;
959 tiles[y2*w+x2] |= F(d);
960
961 break;
962 }
963 sfree(perim2);
964
965 if (i == nperim) {
966 sfree(perimeter);
967 return; /* nothing we can do! */
968 }
969
970 /*
971 * Now we've constructed a new link, we need to find the entire
972 * loop of which it is a part.
973 *
974 * In principle, this involves doing a complete search round
975 * the network. However, I anticipate that in the vast majority
976 * of cases the loop will be quite small, so what I'm going to
977 * do is make _two_ searches round the network in parallel, one
978 * keeping its metaphorical hand on the left-hand wall while
979 * the other keeps its hand on the right. As soon as one of
980 * them gets back to its starting point, I abandon the other.
981 */
982 for (i = 0; i < 2; i++) {
983 loopsize[i] = nloop[i] = 0;
984 loop[i] = NULL;
985 looppos[i].x = x;
986 looppos[i].y = y;
987 looppos[i].direction = d;
988 }
989 while (1) {
990 for (i = 0; i < 2; i++) {
991 int x2, y2, j;
992
993 x = looppos[i].x;
994 y = looppos[i].y;
995 d = looppos[i].direction;
996
997 OFFSETWH(x2, y2, x, y, d, w, h);
998
999 /*
1000 * Add this path segment to the loop, unless it exactly
1001 * reverses the previous one on the loop in which case
1002 * we take it away again.
1003 */
1004#ifdef PERTURB_DIAGNOSTICS
1005 printf("looppos[%d] = %d,%d:%d\n", i, x, y, d);
1006#endif
1007 if (nloop[i] > 0 &&
1008 loop[i][nloop[i]-1].x == x2 &&
1009 loop[i][nloop[i]-1].y == y2 &&
1010 loop[i][nloop[i]-1].direction == F(d)) {
1011#ifdef PERTURB_DIAGNOSTICS
1012 printf("removing path segment %d,%d:%d from loop[%d]\n",
1013 x2, y2, F(d), i);
1014#endif
1015 nloop[i]--;
1016 } else {
1017 if (nloop[i] >= loopsize[i]) {
1018 loopsize[i] = loopsize[i] * 3 / 2 + 32;
1019 loop[i] = sresize(loop[i], loopsize[i], struct xyd);
1020 }
1021#ifdef PERTURB_DIAGNOSTICS
1022 printf("adding path segment %d,%d:%d to loop[%d]\n",
1023 x, y, d, i);
1024#endif
1025 loop[i][nloop[i]++] = looppos[i];
1026 }
1027
1028#ifdef PERTURB_DIAGNOSTICS
1029 printf("tile at new location is %x\n", tiles[y2*w+x2] & 0xF);
1030#endif
1031 d = F(d);
1032 for (j = 0; j < 4; j++) {
1033 if (i == 0)
1034 d = A(d);
1035 else
1036 d = C(d);
1037#ifdef PERTURB_DIAGNOSTICS
1038 printf("trying dir %d\n", d);
1039#endif
1040 if (tiles[y2*w+x2] & d) {
1041 looppos[i].x = x2;
1042 looppos[i].y = y2;
1043 looppos[i].direction = d;
1044 break;
1045 }
1046 }
1047
1048 assert(j < 4);
1049 assert(nloop[i] > 0);
1050
1051 if (looppos[i].x == loop[i][0].x &&
1052 looppos[i].y == loop[i][0].y &&
1053 looppos[i].direction == loop[i][0].direction) {
1054#ifdef PERTURB_DIAGNOSTICS
1055 printf("loop %d finished tracking\n", i);
1056#endif
1057
1058 /*
1059 * Having found our loop, we now sever it at a
1060 * randomly chosen point - absolutely any will do -
1061 * which is not the one we joined it at to begin
1062 * with. Conveniently, the one we joined it at is
1063 * loop[i][0], so we just avoid that one.
1064 */
1065 j = random_upto(rs, nloop[i]-1) + 1;
1066 x = loop[i][j].x;
1067 y = loop[i][j].y;
1068 d = loop[i][j].direction;
1069 OFFSETWH(x2, y2, x, y, d, w, h);
1070 tiles[y*w+x] &= ~d;
1071 tiles[y2*w+x2] &= ~F(d);
1072
1073 break;
1074 }
1075 }
1076 if (i < 2)
1077 break;
1078 }
1079 sfree(loop[0]);
1080 sfree(loop[1]);
1081
1082 /*
1083 * Finally, we must mark the entire disputed section as locked,
1084 * to prevent the perturb function being called on it multiple
1085 * times.
1086 *
1087 * To do this, we _sort_ the perimeter of the area. The
1088 * existing xyd_cmp function will arrange things into columns
1089 * for us, in such a way that each column has the edges in
1090 * vertical order. Then we can work down each column and fill
1091 * in all the squares between an up edge and a down edge.
1092 */
1093 qsort(perimeter, nperim, sizeof(struct xyd), xyd_cmp);
1094 x = y = -1;
1095 for (i = 0; i <= nperim; i++) {
1096 if (i == nperim || perimeter[i].x > x) {
1097 /*
1098 * Fill in everything from the last Up edge to the
1099 * bottom of the grid, if necessary.
1100 */
1101 if (x != -1) {
1102 while (y < h) {
1103#ifdef PERTURB_DIAGNOSTICS
1104 printf("resolved: locking tile %d,%d\n", x, y);
1105#endif
1106 tiles[y * w + x] |= LOCKED;
1107 y++;
1108 }
1109 x = y = -1;
1110 }
1111
1112 if (i == nperim)
1113 break;
1114
1115 x = perimeter[i].x;
1116 y = 0;
1117 }
1118
1119 if (perimeter[i].direction == U) {
1120 x = perimeter[i].x;
1121 y = perimeter[i].y;
1122 } else if (perimeter[i].direction == D) {
1123 /*
1124 * Fill in everything from the last Up edge to here.
1125 */
1126 assert(x == perimeter[i].x && y <= perimeter[i].y);
1127 while (y <= perimeter[i].y) {
1128#ifdef PERTURB_DIAGNOSTICS
1129 printf("resolved: locking tile %d,%d\n", x, y);
1130#endif
1131 tiles[y * w + x] |= LOCKED;
1132 y++;
1133 }
1134 x = y = -1;
1135 }
1136 }
1137
1138 sfree(perimeter);
1139}
1140
1141static int *compute_loops_inner(int w, int h, bool wrapping,
1142 const unsigned char *tiles,
1143 const unsigned char *barriers,
1144 bool include_unlocked_squares);
1145
1146static char *new_game_desc(const game_params *params, random_state *rs,
1147 char **aux, bool interactive)
1148{
1149 tree234 *possibilities, *barriertree;
1150 int w, h, x, y, cx, cy, nbarriers;
1151 unsigned char *tiles, *barriers;
1152 char *desc, *p;
1153
1154 w = params->width;
1155 h = params->height;
1156
1157 cx = w / 2;
1158 cy = h / 2;
1159
1160 tiles = snewn(w * h, unsigned char);
1161 barriers = snewn(w * h, unsigned char);
1162
1163 begin_generation:
1164
1165 memset(tiles, 0, w * h);
1166 memset(barriers, 0, w * h);
1167
1168 /*
1169 * Construct the unshuffled grid.
1170 *
1171 * To do this, we simply start at the centre point, repeatedly
1172 * choose a random possibility out of the available ways to
1173 * extend a used square into an unused one, and do it. After
1174 * extending the third line out of a square, we remove the
1175 * fourth from the possibilities list to avoid any full-cross
1176 * squares (which would make the game too easy because they
1177 * only have one orientation).
1178 *
1179 * The slightly worrying thing is the avoidance of full-cross
1180 * squares. Can this cause our unsophisticated construction
1181 * algorithm to paint itself into a corner, by getting into a
1182 * situation where there are some unreached squares and the
1183 * only way to reach any of them is to extend a T-piece into a
1184 * full cross?
1185 *
1186 * Answer: no it can't, and here's a proof.
1187 *
1188 * Any contiguous group of such unreachable squares must be
1189 * surrounded on _all_ sides by T-pieces pointing away from the
1190 * group. (If not, then there is a square which can be extended
1191 * into one of the `unreachable' ones, and so it wasn't
1192 * unreachable after all.) In particular, this implies that
1193 * each contiguous group of unreachable squares must be
1194 * rectangular in shape (any deviation from that yields a
1195 * non-T-piece next to an `unreachable' square).
1196 *
1197 * So we have a rectangle of unreachable squares, with T-pieces
1198 * forming a solid border around the rectangle. The corners of
1199 * that border must be connected (since every tile connects all
1200 * the lines arriving in it), and therefore the border must
1201 * form a closed loop around the rectangle.
1202 *
1203 * But this can't have happened in the first place, since we
1204 * _know_ we've avoided creating closed loops! Hence, no such
1205 * situation can ever arise, and the naive grid construction
1206 * algorithm will guaranteeably result in a complete grid
1207 * containing no unreached squares, no full crosses _and_ no
1208 * closed loops. []
1209 */
1210 possibilities = newtree234(xyd_cmp_nc);
1211
1212 if (cx+1 < w)
1213 add234(possibilities, new_xyd(cx, cy, R));
1214 if (cy-1 >= 0)
1215 add234(possibilities, new_xyd(cx, cy, U));
1216 if (cx-1 >= 0)
1217 add234(possibilities, new_xyd(cx, cy, L));
1218 if (cy+1 < h)
1219 add234(possibilities, new_xyd(cx, cy, D));
1220
1221 while (count234(possibilities) > 0) {
1222 int i;
1223 struct xyd *xyd;
1224 int x1, y1, d1, x2, y2, d2, d;
1225
1226 /*
1227 * Extract a randomly chosen possibility from the list.
1228 */
1229 i = random_upto(rs, count234(possibilities));
1230 xyd = delpos234(possibilities, i);
1231 x1 = xyd->x;
1232 y1 = xyd->y;
1233 d1 = xyd->direction;
1234 sfree(xyd);
1235
1236 OFFSET(x2, y2, x1, y1, d1, params);
1237 d2 = F(d1);
1238#ifdef GENERATION_DIAGNOSTICS
1239 printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n",
1240 x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]);
1241#endif
1242
1243 /*
1244 * Make the connection. (We should be moving to an as yet
1245 * unused tile.)
1246 */
1247 index(params, tiles, x1, y1) |= d1;
1248 assert(index(params, tiles, x2, y2) == 0);
1249 index(params, tiles, x2, y2) |= d2;
1250
1251 /*
1252 * If we have created a T-piece, remove its last
1253 * possibility.
1254 */
1255 if (COUNT(index(params, tiles, x1, y1)) == 3) {
1256 struct xyd xyd1, *xydp;
1257
1258 xyd1.x = x1;
1259 xyd1.y = y1;
1260 xyd1.direction = 0x0F ^ index(params, tiles, x1, y1);
1261
1262 xydp = find234(possibilities, &xyd1, NULL);
1263
1264 if (xydp) {
1265#ifdef GENERATION_DIAGNOSTICS
1266 printf("T-piece; removing (%d,%d,%c)\n",
1267 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
1268#endif
1269 del234(possibilities, xydp);
1270 sfree(xydp);
1271 }
1272 }
1273
1274 /*
1275 * Remove all other possibilities that were pointing at the
1276 * tile we've just moved into.
1277 */
1278 for (d = 1; d < 0x10; d <<= 1) {
1279 int x3, y3, d3;
1280 struct xyd xyd1, *xydp;
1281
1282 OFFSET(x3, y3, x2, y2, d, params);
1283 d3 = F(d);
1284
1285 xyd1.x = x3;
1286 xyd1.y = y3;
1287 xyd1.direction = d3;
1288
1289 xydp = find234(possibilities, &xyd1, NULL);
1290
1291 if (xydp) {
1292#ifdef GENERATION_DIAGNOSTICS
1293 printf("Loop avoidance; removing (%d,%d,%c)\n",
1294 xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
1295#endif
1296 del234(possibilities, xydp);
1297 sfree(xydp);
1298 }
1299 }
1300
1301 /*
1302 * Add new possibilities to the list for moving _out_ of
1303 * the tile we have just moved into.
1304 */
1305 for (d = 1; d < 0x10; d <<= 1) {
1306 int x3, y3;
1307
1308 if (d == d2)
1309 continue; /* we've got this one already */
1310
1311 if (!params->wrapping) {
1312 if (d == U && y2 == 0)
1313 continue;
1314 if (d == D && y2 == h-1)
1315 continue;
1316 if (d == L && x2 == 0)
1317 continue;
1318 if (d == R && x2 == w-1)
1319 continue;
1320 }
1321
1322 OFFSET(x3, y3, x2, y2, d, params);
1323
1324 if (index(params, tiles, x3, y3))
1325 continue; /* this would create a loop */
1326
1327#ifdef GENERATION_DIAGNOSTICS
1328 printf("New frontier; adding (%d,%d,%c)\n",
1329 x2, y2, "0RU3L567D9abcdef"[d]);
1330#endif
1331 add234(possibilities, new_xyd(x2, y2, d));
1332 }
1333 }
1334 /* Having done that, we should have no possibilities remaining. */
1335 assert(count234(possibilities) == 0);
1336 freetree234(possibilities);
1337
1338 if (params->unique) {
1339 int prevn = -1;
1340
1341 /*
1342 * Run the solver to check unique solubility.
1343 */
1344 while (net_solver(w, h, tiles, NULL, params->wrapping) != 1) {
1345 int n = 0;
1346
1347 /*
1348 * We expect (in most cases) that most of the grid will
1349 * be uniquely specified already, and the remaining
1350 * ambiguous sections will be small and separate. So
1351 * our strategy is to find each individual such
1352 * section, and perform a perturbation on the network
1353 * in that area.
1354 */
1355 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
1356 if (x+1 < w && ((tiles[y*w+x] ^ tiles[y*w+x+1]) & LOCKED)) {
1357 n++;
1358 if (tiles[y*w+x] & LOCKED)
1359 perturb(w, h, tiles, params->wrapping, rs, x+1, y, L);
1360 else
1361 perturb(w, h, tiles, params->wrapping, rs, x, y, R);
1362 }
1363 if (y+1 < h && ((tiles[y*w+x] ^ tiles[(y+1)*w+x]) & LOCKED)) {
1364 n++;
1365 if (tiles[y*w+x] & LOCKED)
1366 perturb(w, h, tiles, params->wrapping, rs, x, y+1, U);
1367 else
1368 perturb(w, h, tiles, params->wrapping, rs, x, y, D);
1369 }
1370 }
1371
1372 /*
1373 * Now n counts the number of ambiguous sections we
1374 * have fiddled with. If we haven't managed to decrease
1375 * it from the last time we ran the solver, give up and
1376 * regenerate the entire grid.
1377 */
1378 if (prevn != -1 && prevn <= n)
1379 goto begin_generation; /* (sorry) */
1380
1381 prevn = n;
1382 }
1383
1384 /*
1385 * The solver will have left a lot of LOCKED bits lying
1386 * around in the tiles array. Remove them.
1387 */
1388 for (x = 0; x < w*h; x++)
1389 tiles[x] &= ~LOCKED;
1390 }
1391
1392 /*
1393 * Now compute a list of the possible barrier locations.
1394 */
1395 barriertree = newtree234(xyd_cmp_nc);
1396 for (y = 0; y < h; y++) {
1397 for (x = 0; x < w; x++) {
1398
1399 if (!(index(params, tiles, x, y) & R) &&
1400 (params->wrapping || x < w-1))
1401 add234(barriertree, new_xyd(x, y, R));
1402 if (!(index(params, tiles, x, y) & D) &&
1403 (params->wrapping || y < h-1))
1404 add234(barriertree, new_xyd(x, y, D));
1405 }
1406 }
1407
1408 /*
1409 * Save the unshuffled grid in aux.
1410 */
1411 {
1412 char *solution;
1413 int i;
1414
1415 solution = snewn(w * h + 1, char);
1416 for (i = 0; i < w * h; i++)
1417 solution[i] = "0123456789abcdef"[tiles[i] & 0xF];
1418 solution[w*h] = '\0';
1419
1420 *aux = solution;
1421 }
1422
1423 /*
1424 * Now shuffle the grid.
1425 *
1426 * In order to avoid accidentally generating an already-solved
1427 * grid, we will reshuffle as necessary to ensure that at least
1428 * one edge has a mismatched connection.
1429 *
1430 * This can always be done, since validate_params() enforces a
1431 * grid area of at least 2 and our generator never creates
1432 * either type of rotationally invariant tile (cross and
1433 * blank). Hence there must be at least one edge separating
1434 * distinct tiles, and it must be possible to find orientations
1435 * of those tiles such that one tile is trying to connect
1436 * through that edge and the other is not.
1437 *
1438 * (We could be more subtle, and allow the shuffle to generate
1439 * a grid in which all tiles match up locally and the only
1440 * criterion preventing the grid from being already solved is
1441 * connectedness. However, that would take more effort, and
1442 * it's easier to simply make sure every grid is _obviously_
1443 * not solved.)
1444 *
1445 * We also require that our shuffle produces no loops in the
1446 * initial grid state, because it's a bit rude to light up a 'HEY,
1447 * YOU DID SOMETHING WRONG!' indicator when the user hasn't even
1448 * had a chance to do _anything_ yet. This also is possible just
1449 * by retrying the whole shuffle on failure, because it's clear
1450 * that at least one non-solved shuffle with no loops must exist.
1451 * (Proof: take the _solved_ state of the puzzle, and rotate one
1452 * endpoint.)
1453 */
1454 while (1) {
1455 int mismatches, prev_loopsquares, this_loopsquares, i;
1456 int *loops;
1457
1458 shuffle:
1459 for (y = 0; y < h; y++) {
1460 for (x = 0; x < w; x++) {
1461 int orig = index(params, tiles, x, y);
1462 int rot = random_upto(rs, 4);
1463 index(params, tiles, x, y) = ROT(orig, rot);
1464 }
1465 }
1466
1467 /*
1468 * Check for loops, and try to fix them by reshuffling just
1469 * the squares involved.
1470 */
1471 prev_loopsquares = w*h+1;
1472 while (1) {
1473 loops = compute_loops_inner(w, h, params->wrapping, tiles, NULL,
1474 true);
1475 this_loopsquares = 0;
1476 for (i = 0; i < w*h; i++) {
1477 if (loops[i]) {
1478 int orig = tiles[i];
1479 int rot = random_upto(rs, 4);
1480 tiles[i] = ROT(orig, rot);
1481 this_loopsquares++;
1482 }
1483 }
1484 sfree(loops);
1485 if (this_loopsquares > prev_loopsquares) {
1486 /*
1487 * We're increasing rather than reducing the number of
1488 * loops. Give up and go back to the full shuffle.
1489 */
1490 goto shuffle;
1491 }
1492 if (this_loopsquares == 0)
1493 break;
1494 prev_loopsquares = this_loopsquares;
1495 }
1496
1497 mismatches = 0;
1498 /*
1499 * I can't even be bothered to check for mismatches across
1500 * a wrapping edge, so I'm just going to enforce that there
1501 * must be a mismatch across a non-wrapping edge, which is
1502 * still always possible.
1503 */
1504 for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
1505 if (x+1 < w && ((ROT(index(params, tiles, x, y), 2) ^
1506 index(params, tiles, x+1, y)) & L))
1507 mismatches++;
1508 if (y+1 < h && ((ROT(index(params, tiles, x, y), 2) ^
1509 index(params, tiles, x, y+1)) & U))
1510 mismatches++;
1511 }
1512
1513 if (mismatches == 0)
1514 continue;
1515
1516 /* OK. */
1517 break;
1518 }
1519
1520 /*
1521 * And now choose barrier locations. (We carefully do this
1522 * _after_ shuffling, so that changing the barrier rate in the
1523 * params while keeping the random seed the same will give the
1524 * same shuffled grid and _only_ change the barrier locations.
1525 * Also the way we choose barrier locations, by repeatedly
1526 * choosing one possibility from the list until we have enough,
1527 * is designed to ensure that raising the barrier rate while
1528 * keeping the seed the same will provide a superset of the
1529 * previous barrier set - i.e. if you ask for 10 barriers, and
1530 * then decide that's still too hard and ask for 20, you'll get
1531 * the original 10 plus 10 more, rather than getting 20 new
1532 * ones and the chance of remembering your first 10.)
1533 */
1534 nbarriers = (int)(params->barrier_probability * count234(barriertree));
1535 assert(nbarriers >= 0 && nbarriers <= count234(barriertree));
1536
1537 while (nbarriers > 0) {
1538 int i;
1539 struct xyd *xyd;
1540 int x1, y1, d1, x2, y2, d2;
1541
1542 /*
1543 * Extract a randomly chosen barrier from the list.
1544 */
1545 i = random_upto(rs, count234(barriertree));
1546 xyd = delpos234(barriertree, i);
1547
1548 assert(xyd != NULL);
1549
1550 x1 = xyd->x;
1551 y1 = xyd->y;
1552 d1 = xyd->direction;
1553 sfree(xyd);
1554
1555 OFFSET(x2, y2, x1, y1, d1, params);
1556 d2 = F(d1);
1557
1558 index(params, barriers, x1, y1) |= d1;
1559 index(params, barriers, x2, y2) |= d2;
1560
1561 nbarriers--;
1562 }
1563
1564 /*
1565 * Clean up the rest of the barrier list.
1566 */
1567 {
1568 struct xyd *xyd;
1569
1570 while ( (xyd = delpos234(barriertree, 0)) != NULL)
1571 sfree(xyd);
1572
1573 freetree234(barriertree);
1574 }
1575
1576 /*
1577 * Finally, encode the grid into a string game description.
1578 *
1579 * My syntax is extremely simple: each square is encoded as a
1580 * hex digit in which bit 0 means a connection on the right,
1581 * bit 1 means up, bit 2 left and bit 3 down. (i.e. the same
1582 * encoding as used internally). Each digit is followed by
1583 * optional barrier indicators: `v' means a vertical barrier to
1584 * the right of it, and `h' means a horizontal barrier below
1585 * it.
1586 */
1587 desc = snewn(w * h * 3 + 1, char);
1588 p = desc;
1589 for (y = 0; y < h; y++) {
1590 for (x = 0; x < w; x++) {
1591 *p++ = "0123456789abcdef"[index(params, tiles, x, y)];
1592 if ((params->wrapping || x < w-1) &&
1593 (index(params, barriers, x, y) & R))
1594 *p++ = 'v';
1595 if ((params->wrapping || y < h-1) &&
1596 (index(params, barriers, x, y) & D))
1597 *p++ = 'h';
1598 }
1599 }
1600 assert(p - desc <= w*h*3);
1601 *p = '\0';
1602
1603 sfree(tiles);
1604 sfree(barriers);
1605
1606 return desc;
1607}
1608
1609static const char *validate_desc(const game_params *params, const char *desc)
1610{
1611 int w = params->width, h = params->height;
1612 int i;
1613
1614 for (i = 0; i < w*h; i++) {
1615 if (*desc >= '0' && *desc <= '9')
1616 /* OK */;
1617 else if (*desc >= 'a' && *desc <= 'f')
1618 /* OK */;
1619 else if (*desc >= 'A' && *desc <= 'F')
1620 /* OK */;
1621 else if (!*desc)
1622 return "Game description shorter than expected";
1623 else
1624 return "Game description contained unexpected character";
1625 desc++;
1626 while (*desc == 'h' || *desc == 'v')
1627 desc++;
1628 }
1629 if (*desc)
1630 return "Game description longer than expected";
1631
1632 return NULL;
1633}
1634
1635/* ----------------------------------------------------------------------
1636 * Construct an initial game state, given a description and parameters.
1637 */
1638
1639static game_state *new_game(midend *me, const game_params *params,
1640 const char *desc)
1641{
1642 game_state *state;
1643 int w, h, x, y;
1644
1645 assert(params->width > 0 && params->height > 0);
1646 assert(params->width > 1 || params->height > 1);
1647
1648 /*
1649 * Create a blank game state.
1650 */
1651 state = snew(game_state);
1652 w = state->width = params->width;
1653 h = state->height = params->height;
1654 state->wrapping = params->wrapping;
1655 state->imm = snew(game_immutable_state);
1656 state->imm->refcount = 1;
1657 state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
1658 state->completed = state->used_solve = false;
1659 state->tiles = snewn(state->width * state->height, unsigned char);
1660 memset(state->tiles, 0, state->width * state->height);
1661 state->imm->barriers = snewn(state->width * state->height, unsigned char);
1662 memset(state->imm->barriers, 0, state->width * state->height);
1663
1664 /*
1665 * Parse the game description into the grid.
1666 */
1667 for (y = 0; y < h; y++) {
1668 for (x = 0; x < w; x++) {
1669 if (*desc >= '0' && *desc <= '9')
1670 tile(state, x, y) = *desc - '0';
1671 else if (*desc >= 'a' && *desc <= 'f')
1672 tile(state, x, y) = *desc - 'a' + 10;
1673 else if (*desc >= 'A' && *desc <= 'F')
1674 tile(state, x, y) = *desc - 'A' + 10;
1675 if (*desc)
1676 desc++;
1677 while (*desc == 'h' || *desc == 'v') {
1678 int x2, y2, d1, d2;
1679 if (*desc == 'v')
1680 d1 = R;
1681 else
1682 d1 = D;
1683
1684 OFFSET(x2, y2, x, y, d1, state);
1685 d2 = F(d1);
1686
1687 barrier(state, x, y) |= d1;
1688 barrier(state, x2, y2) |= d2;
1689
1690 desc++;
1691 }
1692 }
1693 }
1694
1695 /*
1696 * Set up border barriers if this is a non-wrapping game.
1697 */
1698 if (!state->wrapping) {
1699 for (x = 0; x < state->width; x++) {
1700 barrier(state, x, 0) |= U;
1701 barrier(state, x, state->height-1) |= D;
1702 }
1703 for (y = 0; y < state->height; y++) {
1704 barrier(state, 0, y) |= L;
1705 barrier(state, state->width-1, y) |= R;
1706 }
1707 } else {
1708 /*
1709 * We check whether this is de-facto a non-wrapping game
1710 * despite the parameters, in case we were passed the
1711 * description of a non-wrapping game. This is so that we
1712 * can change some aspects of the UI behaviour.
1713 */
1714 state->wrapping = false;
1715 for (x = 0; x < state->width; x++)
1716 if (!(barrier(state, x, 0) & U) ||
1717 !(barrier(state, x, state->height-1) & D))
1718 state->wrapping = true;
1719 for (y = 0; y < state->height; y++)
1720 if (!(barrier(state, 0, y) & L) ||
1721 !(barrier(state, state->width-1, y) & R))
1722 state->wrapping = true;
1723 }
1724
1725 return state;
1726}
1727
1728static game_state *dup_game(const game_state *state)
1729{
1730 game_state *ret;
1731
1732 ret = snew(game_state);
1733 ret->imm = state->imm;
1734 ret->imm->refcount++;
1735 ret->width = state->width;
1736 ret->height = state->height;
1737 ret->wrapping = state->wrapping;
1738 ret->completed = state->completed;
1739 ret->used_solve = state->used_solve;
1740 ret->last_rotate_dir = state->last_rotate_dir;
1741 ret->last_rotate_x = state->last_rotate_x;
1742 ret->last_rotate_y = state->last_rotate_y;
1743 ret->tiles = snewn(state->width * state->height, unsigned char);
1744 memcpy(ret->tiles, state->tiles, state->width * state->height);
1745
1746 return ret;
1747}
1748
1749static void free_game(game_state *state)
1750{
1751 if (--state->imm->refcount == 0) {
1752 sfree(state->imm->barriers);
1753 sfree(state->imm);
1754 }
1755 sfree(state->tiles);
1756 sfree(state);
1757}
1758
1759static char *solve_game(const game_state *state, const game_state *currstate,
1760 const char *aux, const char **error)
1761{
1762 unsigned char *tiles;
1763 char *ret;
1764 int retlen, retsize;
1765 int i;
1766
1767 tiles = snewn(state->width * state->height, unsigned char);
1768
1769 if (!aux) {
1770 /*
1771 * Run the internal solver on the provided grid. This might
1772 * not yield a complete solution.
1773 */
1774 int solver_result;
1775
1776 memcpy(tiles, state->tiles, state->width * state->height);
1777 solver_result = net_solver(state->width, state->height, tiles,
1778 state->imm->barriers, state->wrapping);
1779
1780 if (solver_result < 0) {
1781 *error = "No solution exists for this puzzle";
1782 sfree(tiles);
1783 return NULL;
1784 }
1785 } else {
1786 for (i = 0; i < state->width * state->height; i++) {
1787 int c = aux[i];
1788
1789 if (c >= '0' && c <= '9')
1790 tiles[i] = c - '0';
1791 else if (c >= 'a' && c <= 'f')
1792 tiles[i] = c - 'a' + 10;
1793 else if (c >= 'A' && c <= 'F')
1794 tiles[i] = c - 'A' + 10;
1795
1796 tiles[i] |= LOCKED;
1797 }
1798 }
1799
1800 /*
1801 * Now construct a string which can be passed to execute_move()
1802 * to transform the current grid into the solved one.
1803 */
1804 retsize = 256;
1805 ret = snewn(retsize, char);
1806 retlen = 0;
1807 ret[retlen++] = 'S';
1808
1809 for (i = 0; i < state->width * state->height; i++) {
1810 int from = currstate->tiles[i], to = tiles[i];
1811 int ft = from & (R|L|U|D), tt = to & (R|L|U|D);
1812 int x = i % state->width, y = i / state->width;
1813 int chr = '\0';
1814 char buf[80], *p = buf;
1815
1816 if (from == to)
1817 continue; /* nothing needs doing at all */
1818
1819 /*
1820 * To transform this tile into the desired tile: first
1821 * unlock the tile if it's locked, then rotate it if
1822 * necessary, then lock it if necessary.
1823 */
1824 if (from & LOCKED)
1825 p += sprintf(p, ";L%d,%d", x, y);
1826
1827 if (tt == A(ft))
1828 chr = 'A';
1829 else if (tt == C(ft))
1830 chr = 'C';
1831 else if (tt == F(ft))
1832 chr = 'F';
1833 else {
1834 assert(tt == ft);
1835 chr = '\0';
1836 }
1837 if (chr)
1838 p += sprintf(p, ";%c%d,%d", chr, x, y);
1839
1840 if (to & LOCKED)
1841 p += sprintf(p, ";L%d,%d", x, y);
1842
1843 if (p > buf) {
1844 if (retlen + (p - buf) >= retsize) {
1845 retsize = retlen + (p - buf) + 512;
1846 ret = sresize(ret, retsize, char);
1847 }
1848 memcpy(ret+retlen, buf, p - buf);
1849 retlen += p - buf;
1850 }
1851 }
1852
1853 assert(retlen < retsize);
1854 ret[retlen] = '\0';
1855 ret = sresize(ret, retlen+1, char);
1856
1857 sfree(tiles);
1858
1859 return ret;
1860}
1861
1862/* ----------------------------------------------------------------------
1863 * Utility routine.
1864 */
1865
1866/*
1867 * Compute which squares are reachable from the centre square, as a
1868 * quick visual aid to determining how close the game is to
1869 * completion. This is also a simple way to tell if the game _is_
1870 * completed - just call this function and see whether every square
1871 * is marked active.
1872 */
1873static unsigned char *compute_active(const game_state *state, int cx, int cy)
1874{
1875 unsigned char *active;
1876 tree234 *todo;
1877 struct xyd *xyd;
1878
1879 active = snewn(state->width * state->height, unsigned char);
1880 memset(active, 0, state->width * state->height);
1881
1882 assert(0 <= cx && cx < state->width);
1883 assert(0 <= cy && cy < state->height);
1884 /*
1885 * We only store (x,y) pairs in todo, but it's easier to reuse
1886 * xyd_cmp and just store direction 0 every time.
1887 */
1888 todo = newtree234(xyd_cmp_nc);
1889 index(state, active, cx, cy) = ACTIVE;
1890 add234(todo, new_xyd(cx, cy, 0));
1891
1892 while ( (xyd = delpos234(todo, 0)) != NULL) {
1893 int x1, y1, d1, x2, y2, d2;
1894
1895 x1 = xyd->x;
1896 y1 = xyd->y;
1897 sfree(xyd);
1898
1899 for (d1 = 1; d1 < 0x10; d1 <<= 1) {
1900 OFFSET(x2, y2, x1, y1, d1, state);
1901 d2 = F(d1);
1902
1903 /*
1904 * If the next tile in this direction is connected to
1905 * us, and there isn't a barrier in the way, and it
1906 * isn't already marked active, then mark it active and
1907 * add it to the to-examine list.
1908 */
1909 if ((tile(state, x1, y1) & d1) &&
1910 (tile(state, x2, y2) & d2) &&
1911 !(barrier(state, x1, y1) & d1) &&
1912 !index(state, active, x2, y2)) {
1913 index(state, active, x2, y2) = ACTIVE;
1914 add234(todo, new_xyd(x2, y2, 0));
1915 }
1916 }
1917 }
1918 /* Now we expect the todo list to have shrunk to zero size. */
1919 assert(count234(todo) == 0);
1920 freetree234(todo);
1921
1922 return active;
1923}
1924
1925struct net_neighbour_ctx {
1926 int w, h;
1927 const unsigned char *tiles, *barriers;
1928 int i, n, neighbours[4];
1929 bool include_unlocked_squares;
1930};
1931static int net_neighbour(int vertex, void *vctx)
1932{
1933 struct net_neighbour_ctx *ctx = (struct net_neighbour_ctx *)vctx;
1934
1935 if (vertex >= 0) {
1936 int x = vertex % ctx->w, y = vertex / ctx->w;
1937 int tile, dir, x1, y1, v1;
1938
1939 ctx->i = ctx->n = 0;
1940
1941 tile = ctx->tiles[vertex];
1942 if (ctx->barriers)
1943 tile &= ~ctx->barriers[vertex];
1944
1945 for (dir = 1; dir < 0x10; dir <<= 1) {
1946 if (!(tile & dir))
1947 continue;
1948 OFFSETWH(x1, y1, x, y, dir, ctx->w, ctx->h);
1949 v1 = y1 * ctx->w + x1;
1950 if (!ctx->include_unlocked_squares &&
1951 !(tile & ctx->tiles[v1] & LOCKED))
1952 continue;
1953 if (ctx->tiles[v1] & F(dir))
1954 ctx->neighbours[ctx->n++] = v1;
1955 }
1956 }
1957
1958 if (ctx->i < ctx->n)
1959 return ctx->neighbours[ctx->i++];
1960 else
1961 return -1;
1962}
1963
1964static int *compute_loops_inner(int w, int h, bool wrapping,
1965 const unsigned char *tiles,
1966 const unsigned char *barriers,
1967 bool include_unlocked_squares)
1968{
1969 struct net_neighbour_ctx ctx;
1970 struct findloopstate *fls;
1971 int *loops;
1972 int x, y, v;
1973
1974 fls = findloop_new_state(w*h);
1975 ctx.w = w;
1976 ctx.h = h;
1977 ctx.tiles = tiles;
1978 ctx.barriers = barriers;
1979 ctx.include_unlocked_squares = include_unlocked_squares;
1980 findloop_run(fls, w*h, net_neighbour, &ctx);
1981
1982 loops = snewn(w*h, int);
1983
1984 for (y = 0; y < h; y++) {
1985 for (x = 0; x < w; x++) {
1986 int x1, y1, v1, dir;
1987 int flags = 0;
1988
1989 v = y * w + x;
1990 for (dir = 1; dir < 0x10; dir <<= 1) {
1991 if ((tiles[v] & dir) &&
1992 !(barriers && (barriers[y*w+x] & dir))) {
1993 OFFSETWH(x1, y1, x, y, dir, w, h);
1994 v1 = y1 * w + x1;
1995 if (!include_unlocked_squares &&
1996 !(tiles[v] & tiles[v1] & LOCKED))
1997 continue;
1998 if ((tiles[v1] & F(dir)) &&
1999 findloop_is_loop_edge(fls, y*w+x, y1*w+x1))
2000 flags |= ERR(dir);
2001 }
2002 }
2003 loops[y*w+x] = flags;
2004 }
2005 }
2006
2007 findloop_free_state(fls);
2008 return loops;
2009}
2010
2011static int *compute_loops(const game_state *state,
2012 bool include_unlocked_squares)
2013{
2014 return compute_loops_inner(state->width, state->height, state->wrapping,
2015 state->tiles, state->imm->barriers,
2016 include_unlocked_squares);
2017}
2018
2019struct game_ui {
2020 int org_x, org_y; /* origin */
2021 int cx, cy; /* source tile (game coordinates) */
2022 int cur_x, cur_y;
2023 bool cur_visible;
2024 random_state *rs; /* used for jumbling */
2025#ifdef USE_DRAGGING
2026 int dragtilex, dragtiley, dragstartx, dragstarty;
2027 bool dragged;
2028#endif
2029
2030 bool unlocked_loops;
2031};
2032
2033static game_ui *new_ui(const game_state *state)
2034{
2035 void *seed;
2036 int seedsize;
2037 game_ui *ui = snew(game_ui);
2038
2039 ui->unlocked_loops = true;
2040
2041 if (state) {
2042 ui->org_x = ui->org_y = 0;
2043 ui->cur_x = ui->cx = state->width / 2;
2044 ui->cur_y = ui->cy = state->height / 2;
2045 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2046 get_random_seed(&seed, &seedsize);
2047 ui->rs = random_new(seed, seedsize);
2048 sfree(seed);
2049#ifdef USE_DRAGGING
2050 ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1;
2051#endif
2052 } else {
2053 ui->rs = NULL;
2054 }
2055
2056 return ui;
2057}
2058
2059static void free_ui(game_ui *ui)
2060{
2061 if (ui->rs)
2062 random_free(ui->rs);
2063 sfree(ui);
2064}
2065
2066static char *encode_ui(const game_ui *ui)
2067{
2068 char buf[120];
2069 /*
2070 * We preserve the origin and centre-point coordinates over a
2071 * serialise.
2072 */
2073 sprintf(buf, "O%d,%d;C%d,%d", ui->org_x, ui->org_y, ui->cx, ui->cy);
2074 return dupstr(buf);
2075}
2076
2077static void decode_ui(game_ui *ui, const char *encoding,
2078 const game_state *state)
2079{
2080 int org_x, org_y, cx, cy;
2081
2082 if (sscanf(encoding, "O%d,%d;C%d,%d", &org_x, &org_y, &cx, &cy) == 4) {
2083 if (0 <= org_x && org_x < state->width &&
2084 0 <= org_y && org_y < state->height) {
2085 ui->org_x = org_x;
2086 ui->org_y = org_y;
2087 }
2088 if (0 <= cx && cx < state->width &&
2089 0 <= cy && cy < state->height) {
2090 ui->cx = cx;
2091 ui->cy = cy;
2092 }
2093 }
2094}
2095
2096static config_item *get_prefs(game_ui *ui)
2097{
2098 config_item *ret;
2099
2100 ret = snewn(N_PREF_ITEMS+1, config_item);
2101
2102 ret[PREF_UNLOCKED_LOOPS].name =
2103 "Highlight loops involving unlocked squares";
2104 ret[PREF_UNLOCKED_LOOPS].kw = "unlocked-loops";
2105 ret[PREF_UNLOCKED_LOOPS].type = C_BOOLEAN;
2106 ret[PREF_UNLOCKED_LOOPS].u.boolean.bval = ui->unlocked_loops;
2107
2108 ret[N_PREF_ITEMS].name = NULL;
2109 ret[N_PREF_ITEMS].type = C_END;
2110
2111 return ret;
2112}
2113
2114static void set_prefs(game_ui *ui, const config_item *cfg)
2115{
2116 ui->unlocked_loops = cfg[PREF_UNLOCKED_LOOPS].u.boolean.bval;
2117}
2118
2119static void game_changed_state(game_ui *ui, const game_state *oldstate,
2120 const game_state *newstate)
2121{
2122}
2123
2124static const char *current_key_label(const game_ui *ui,
2125 const game_state *state, int button)
2126{
2127 if (tile(state, ui->cur_x, ui->cur_y) & LOCKED) {
2128 if (button == CURSOR_SELECT2) return "Unlock";
2129 } else {
2130 if (button == CURSOR_SELECT) return "Rotate";
2131 if (button == CURSOR_SELECT2) return "Lock";
2132 }
2133 return "";
2134}
2135
2136struct game_drawstate {
2137 int width, height;
2138 int tilesize;
2139 unsigned long *visible, *to_draw;
2140};
2141
2142/* ----------------------------------------------------------------------
2143 * Process a move.
2144 */
2145static char *interpret_move(const game_state *state, game_ui *ui,
2146 const game_drawstate *ds,
2147 int x, int y, int button)
2148{
2149 char *nullret;
2150 int tx = -1, ty = -1, dir = 0;
2151 bool shift = button & MOD_SHFT, ctrl = button & MOD_CTRL;
2152 enum {
2153 NONE, ROTATE_LEFT, ROTATE_180, ROTATE_RIGHT, TOGGLE_LOCK, JUMBLE,
2154 MOVE_ORIGIN, MOVE_SOURCE, MOVE_ORIGIN_AND_SOURCE, MOVE_CURSOR
2155 } action;
2156
2157 button = STRIP_BUTTON_MODIFIERS(button);
2158 nullret = NULL;
2159 action = NONE;
2160
2161 if (button == LEFT_BUTTON ||
2162 button == MIDDLE_BUTTON ||
2163#ifdef USE_DRAGGING
2164 button == LEFT_DRAG ||
2165 button == LEFT_RELEASE ||
2166 button == RIGHT_DRAG ||
2167 button == RIGHT_RELEASE ||
2168#endif
2169 button == RIGHT_BUTTON) {
2170
2171 if (ui->cur_visible) {
2172 ui->cur_visible = false;
2173 nullret = MOVE_UI_UPDATE;
2174 }
2175
2176 /*
2177 * The button must have been clicked on a valid tile.
2178 */
2179 x -= WINDOW_OFFSET + LINE_THICK;
2180 y -= WINDOW_OFFSET + LINE_THICK;
2181 tx = x / TILE_SIZE;
2182 ty = y / TILE_SIZE;
2183 if (x < 0 || y < 0 || tx >= state->width || ty >= state->height) {
2184#ifdef USE_DRAGGING
2185 if (IS_MOUSE_DOWN(button)) {
2186 ui->dragstartx = ui->dragstarty = ui->dragtilex = ui->dragtiley = -1;
2187 return nullret;
2188 }
2189 /*
2190 * else: Despite the mouse moving off the grid, let drags and releases
2191 * continue to manipulate the tile they started from.
2192 */
2193#else
2194 return nullret;
2195#endif
2196 }
2197 /* Transform from physical to game coords */
2198 tx = (tx + ui->org_x) % state->width;
2199 ty = (ty + ui->org_y) % state->height;
2200 if (x % TILE_SIZE >= TILE_SIZE - LINE_THICK ||
2201 y % TILE_SIZE >= TILE_SIZE - LINE_THICK)
2202 return nullret;
2203
2204#ifdef USE_DRAGGING
2205
2206 if (button == MIDDLE_BUTTON
2207#ifdef STYLUS_BASED
2208 || button == RIGHT_BUTTON /* with a stylus, `right-click' locks */
2209#endif
2210 ) {
2211 /*
2212 * Middle button never drags: it only toggles the lock.
2213 */
2214 action = TOGGLE_LOCK;
2215 } else if (button == LEFT_BUTTON
2216#ifndef STYLUS_BASED
2217 || button == RIGHT_BUTTON /* (see above) */
2218#endif
2219 ) {
2220 /*
2221 * Otherwise, we note down the start point for a drag.
2222 */
2223 ui->dragtilex = tx;
2224 ui->dragtiley = ty;
2225 ui->dragstartx = x % TILE_SIZE;
2226 ui->dragstarty = y % TILE_SIZE;
2227 ui->dragged = false;
2228 return nullret; /* no actual action */
2229 } else if (button == LEFT_DRAG
2230#ifndef STYLUS_BASED
2231 || button == RIGHT_DRAG
2232#endif
2233 ) {
2234 if (ui->dragtilex < 0)
2235 return nullret;
2236
2237 /*
2238 * Find the new drag point and see if it necessitates a
2239 * rotation.
2240 */
2241 int x0,y0, xA,yA, xC,yC, xF,yF;
2242 int mx, my;
2243 int d0, dA, dC, dF, dmin;
2244
2245 tx = ui->dragtilex;
2246 ty = ui->dragtiley;
2247
2248 mx = x - (ui->dragtilex * TILE_SIZE);
2249 my = y - (ui->dragtiley * TILE_SIZE);
2250
2251 x0 = ui->dragstartx;
2252 y0 = ui->dragstarty;
2253 xA = ui->dragstarty;
2254 yA = TILE_SIZE-1 - ui->dragstartx;
2255 xF = TILE_SIZE-1 - ui->dragstartx;
2256 yF = TILE_SIZE-1 - ui->dragstarty;
2257 xC = TILE_SIZE-1 - ui->dragstarty;
2258 yC = ui->dragstartx;
2259
2260 d0 = (mx-x0)*(mx-x0) + (my-y0)*(my-y0);
2261 dA = (mx-xA)*(mx-xA) + (my-yA)*(my-yA);
2262 dF = (mx-xF)*(mx-xF) + (my-yF)*(my-yF);
2263 dC = (mx-xC)*(mx-xC) + (my-yC)*(my-yC);
2264
2265 dmin = min(min(d0,dA),min(dF,dC));
2266
2267 if (d0 == dmin) {
2268 return nullret;
2269 } else if (dF == dmin) {
2270 action = ROTATE_180;
2271 ui->dragstartx = xF;
2272 ui->dragstarty = yF;
2273 ui->dragged = true;
2274 } else if (dA == dmin) {
2275 action = ROTATE_LEFT;
2276 ui->dragstartx = xA;
2277 ui->dragstarty = yA;
2278 ui->dragged = true;
2279 } else /* dC == dmin */ {
2280 action = ROTATE_RIGHT;
2281 ui->dragstartx = xC;
2282 ui->dragstarty = yC;
2283 ui->dragged = true;
2284 }
2285 } else if (button == LEFT_RELEASE
2286#ifndef STYLUS_BASED
2287 || button == RIGHT_RELEASE
2288#endif
2289 ) {
2290 if (!ui->dragged && ui->dragtilex >= 0) {
2291 /*
2292 * There was a click but no perceptible drag:
2293 * revert to single-click behaviour.
2294 */
2295 tx = ui->dragtilex;
2296 ty = ui->dragtiley;
2297
2298 if (button == LEFT_RELEASE)
2299 action = ROTATE_LEFT;
2300 else
2301 action = ROTATE_RIGHT;
2302 } else
2303 return nullret; /* no action */
2304 }
2305
2306#else /* USE_DRAGGING */
2307
2308 action = (button == LEFT_BUTTON ? ROTATE_LEFT :
2309 button == RIGHT_BUTTON ? ROTATE_RIGHT : TOGGLE_LOCK);
2310
2311#endif /* USE_DRAGGING */
2312
2313 } else if (IS_CURSOR_MOVE(button)) {
2314 switch (button) {
2315 case CURSOR_UP: dir = U; break;
2316 case CURSOR_DOWN: dir = D; break;
2317 case CURSOR_LEFT: dir = L; break;
2318 case CURSOR_RIGHT: dir = R; break;
2319 default: return nullret;
2320 }
2321 if (shift && ctrl) action = MOVE_ORIGIN_AND_SOURCE;
2322 else if (shift) action = MOVE_ORIGIN;
2323 else if (ctrl) action = MOVE_SOURCE;
2324 else action = MOVE_CURSOR;
2325 } else if (button == 'a' || button == 's' || button == 'd' ||
2326 button == 'A' || button == 'S' || button == 'D' ||
2327 button == 'f' || button == 'F' ||
2328 IS_CURSOR_SELECT(button)) {
2329 tx = ui->cur_x;
2330 ty = ui->cur_y;
2331 if (button == 'a' || button == 'A' || button == CURSOR_SELECT)
2332 action = ROTATE_LEFT;
2333 else if (button == 's' || button == 'S' || button == CURSOR_SELECT2)
2334 action = TOGGLE_LOCK;
2335 else if (button == 'd' || button == 'D')
2336 action = ROTATE_RIGHT;
2337 else if (button == 'f' || button == 'F')
2338 action = ROTATE_180;
2339 ui->cur_visible = true;
2340 } else if (button == 'j' || button == 'J') {
2341 /* XXX should we have some mouse control for this? */
2342 action = JUMBLE;
2343 } else
2344 return nullret;
2345
2346 /*
2347 * The middle button locks or unlocks a tile. (A locked tile
2348 * cannot be turned, and is visually marked as being locked.
2349 * This is a convenience for the player, so that once they are
2350 * sure which way round a tile goes, they can lock it and thus
2351 * avoid forgetting later on that they'd already done that one;
2352 * and the locking also prevents them turning the tile by
2353 * accident. If they change their mind, another middle click
2354 * unlocks it.)
2355 */
2356 if (action == TOGGLE_LOCK) {
2357 char buf[80];
2358 sprintf(buf, "L%d,%d", tx, ty);
2359 return dupstr(buf);
2360 } else if (action == ROTATE_LEFT || action == ROTATE_RIGHT ||
2361 action == ROTATE_180) {
2362 char buf[80];
2363
2364 /*
2365 * The left and right buttons have no effect if clicked on a
2366 * locked tile.
2367 */
2368 if (tile(state, tx, ty) & LOCKED)
2369 return nullret;
2370
2371 /*
2372 * Otherwise, turn the tile one way or the other. Left button
2373 * turns anticlockwise; right button turns clockwise.
2374 */
2375 sprintf(buf, "%c%d,%d", (int)(action == ROTATE_LEFT ? 'A' :
2376 action == ROTATE_RIGHT ? 'C' : 'F'), tx, ty);
2377 return dupstr(buf);
2378 } else if (action == JUMBLE) {
2379 /*
2380 * Jumble all unlocked tiles to random orientations.
2381 */
2382
2383 int jx, jy, maxlen;
2384 char *ret, *p;
2385
2386 /*
2387 * Maximum string length assumes no int can be converted to
2388 * decimal and take more than 11 digits!
2389 */
2390 maxlen = state->width * state->height * 25 + 3;
2391
2392 ret = snewn(maxlen, char);
2393 p = ret;
2394 *p++ = 'J';
2395
2396 for (jy = 0; jy < state->height; jy++) {
2397 for (jx = 0; jx < state->width; jx++) {
2398 if (!(tile(state, jx, jy) & LOCKED)) {
2399 int rot = random_upto(ui->rs, 4);
2400 if (rot) {
2401 p += sprintf(p, ";%c%d,%d", "AFC"[rot-1], jx, jy);
2402 }
2403 }
2404 }
2405 }
2406 *p++ = '\0';
2407 assert(p - ret < maxlen);
2408 ret = sresize(ret, p - ret, char);
2409
2410 return ret;
2411 } else if (action == MOVE_ORIGIN || action == MOVE_SOURCE ||
2412 action == MOVE_ORIGIN_AND_SOURCE || action == MOVE_CURSOR) {
2413 assert(dir != 0);
2414 if (action == MOVE_ORIGIN || action == MOVE_ORIGIN_AND_SOURCE) {
2415 if (state->wrapping) {
2416 OFFSET(ui->org_x, ui->org_y, ui->org_x, ui->org_y, dir, state);
2417 } else return nullret; /* disallowed for non-wrapping grids */
2418 }
2419 if (action == MOVE_SOURCE || action == MOVE_ORIGIN_AND_SOURCE) {
2420 OFFSET(ui->cx, ui->cy, ui->cx, ui->cy, dir, state);
2421 }
2422 if (action == MOVE_CURSOR) {
2423 OFFSET(ui->cur_x, ui->cur_y, ui->cur_x, ui->cur_y, dir, state);
2424 ui->cur_visible = true;
2425 }
2426 return MOVE_UI_UPDATE;
2427 } else {
2428 return NULL;
2429 }
2430}
2431
2432static game_state *execute_move(const game_state *from, const char *move)
2433{
2434 game_state *ret;
2435 int tx = -1, ty = -1, n, orig;
2436 bool noanim;
2437
2438 ret = dup_game(from);
2439
2440 if (move[0] == 'J' || move[0] == 'S') {
2441 if (move[0] == 'S')
2442 ret->used_solve = true;
2443
2444 move++;
2445 if (*move == ';')
2446 move++;
2447 noanim = true;
2448 } else
2449 noanim = false;
2450
2451 ret->last_rotate_dir = 0; /* suppress animation */
2452 ret->last_rotate_x = ret->last_rotate_y = 0;
2453
2454 while (*move) {
2455 if ((move[0] == 'A' || move[0] == 'C' ||
2456 move[0] == 'F' || move[0] == 'L') &&
2457 sscanf(move+1, "%d,%d%n", &tx, &ty, &n) >= 2 &&
2458 tx >= 0 && tx < from->width && ty >= 0 && ty < from->height) {
2459 orig = tile(ret, tx, ty);
2460 if (move[0] == 'A') {
2461 tile(ret, tx, ty) = A(orig);
2462 if (!noanim)
2463 ret->last_rotate_dir = +1;
2464 } else if (move[0] == 'F') {
2465 tile(ret, tx, ty) = F(orig);
2466 if (!noanim)
2467 ret->last_rotate_dir = +2; /* + for sake of argument */
2468 } else if (move[0] == 'C') {
2469 tile(ret, tx, ty) = C(orig);
2470 if (!noanim)
2471 ret->last_rotate_dir = -1;
2472 } else {
2473 assert(move[0] == 'L');
2474 tile(ret, tx, ty) ^= LOCKED;
2475 }
2476
2477 move += 1 + n;
2478 if (*move == ';') move++;
2479 } else {
2480 free_game(ret);
2481 return NULL;
2482 }
2483 }
2484 if (!noanim) {
2485 if (tx == -1 || ty == -1) { free_game(ret); return NULL; }
2486 ret->last_rotate_x = tx;
2487 ret->last_rotate_y = ty;
2488 }
2489
2490 /*
2491 * Check whether the game has been completed.
2492 *
2493 * For this purpose it doesn't matter where the source square is,
2494 * because we can start from anywhere (or, at least, any square
2495 * that's non-empty!), and correctly determine whether the game is
2496 * completed.
2497 */
2498 {
2499 unsigned char *active;
2500 int pos;
2501 bool complete = true;
2502
2503 for (pos = 0; pos < ret->width * ret->height; pos++)
2504 if (ret->tiles[pos] & 0xF)
2505 break;
2506
2507 if (pos < ret->width * ret->height) {
2508 active = compute_active(ret, pos % ret->width, pos / ret->width);
2509
2510 for (pos = 0; pos < ret->width * ret->height; pos++)
2511 if ((ret->tiles[pos] & 0xF) && !active[pos]) {
2512 complete = false;
2513 break;
2514 }
2515
2516 sfree(active);
2517 }
2518
2519 if (complete)
2520 ret->completed = true;
2521 }
2522
2523 return ret;
2524}
2525
2526
2527/* ----------------------------------------------------------------------
2528 * Routines for drawing the game position on the screen.
2529 */
2530
2531static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2532{
2533 game_drawstate *ds = snew(game_drawstate);
2534 int i, ncells;
2535
2536 ds->width = state->width;
2537 ds->height = state->height;
2538 ncells = (state->width+2) * (state->height+2);
2539 ds->visible = snewn(ncells, unsigned long);
2540 ds->to_draw = snewn(ncells, unsigned long);
2541 ds->tilesize = 0; /* undecided yet */
2542 for (i = 0; i < ncells; i++)
2543 ds->visible[i] = -1;
2544
2545 return ds;
2546}
2547
2548#define dsindex(ds, field, x, y) ((ds)->field[((y)+1)*((ds)->width+2)+((x)+1)])
2549#define visible(ds, x, y) dsindex(ds, visible, x, y)
2550#define todraw(ds, x, y) dsindex(ds, to_draw, x, y)
2551
2552static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2553{
2554 sfree(ds->visible);
2555 sfree(ds->to_draw);
2556 sfree(ds);
2557}
2558
2559static void game_compute_size(const game_params *params, int tilesize,
2560 const game_ui *ui, int *x, int *y)
2561{
2562 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2563 struct { int tilesize; } ads, *ds = &ads;
2564 ads.tilesize = tilesize;
2565
2566 *x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + LINE_THICK;
2567 *y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + LINE_THICK;
2568}
2569
2570static void game_set_size(drawing *dr, game_drawstate *ds,
2571 const game_params *params, int tilesize)
2572{
2573 ds->tilesize = tilesize;
2574}
2575
2576static float *game_colours(frontend *fe, int *ncolours)
2577{
2578 float *ret;
2579
2580 ret = snewn(NCOLOURS * 3, float);
2581 *ncolours = NCOLOURS;
2582
2583 /*
2584 * Basic background colour is whatever the front end thinks is
2585 * a sensible default.
2586 */
2587 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2588
2589 /*
2590 * Wires are black.
2591 */
2592 ret[COL_WIRE * 3 + 0] = 0.0F;
2593 ret[COL_WIRE * 3 + 1] = 0.0F;
2594 ret[COL_WIRE * 3 + 2] = 0.0F;
2595
2596 /*
2597 * Powered wires and powered endpoints are cyan.
2598 */
2599 ret[COL_POWERED * 3 + 0] = 0.0F;
2600 ret[COL_POWERED * 3 + 1] = 1.0F;
2601 ret[COL_POWERED * 3 + 2] = 1.0F;
2602
2603 /*
2604 * Barriers are red.
2605 */
2606 ret[COL_BARRIER * 3 + 0] = 1.0F;
2607 ret[COL_BARRIER * 3 + 1] = 0.0F;
2608 ret[COL_BARRIER * 3 + 2] = 0.0F;
2609
2610 /*
2611 * Highlighted errors are red as well.
2612 */
2613 ret[COL_ERR * 3 + 0] = 1.0F;
2614 ret[COL_ERR * 3 + 1] = 0.0F;
2615 ret[COL_ERR * 3 + 2] = 0.0F;
2616
2617 /*
2618 * Unpowered endpoints are blue.
2619 */
2620 ret[COL_ENDPOINT * 3 + 0] = 0.0F;
2621 ret[COL_ENDPOINT * 3 + 1] = 0.0F;
2622 ret[COL_ENDPOINT * 3 + 2] = 1.0F;
2623
2624 /*
2625 * Tile borders are a darker grey than the background.
2626 */
2627 ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
2628 ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
2629 ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
2630
2631 /*
2632 * Locked tiles are a grey in between those two.
2633 */
2634 ret[COL_LOCKED * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
2635 ret[COL_LOCKED * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
2636 ret[COL_LOCKED * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
2637
2638 return ret;
2639}
2640
2641static void rotated_coords(float *ox, float *oy, const float matrix[4],
2642 float cx, float cy, float ix, float iy)
2643{
2644 *ox = matrix[0] * ix + matrix[2] * iy + cx;
2645 *oy = matrix[1] * ix + matrix[3] * iy + cy;
2646}
2647
2648/* Flags describing the visible features of a tile. */
2649#define TILE_BARRIER_SHIFT 0 /* 4 bits: R U L D */
2650#define TILE_BARRIER_CORNER_SHIFT 4 /* 4 bits: RU UL LD DR */
2651#define TILE_KEYBOARD_CURSOR (1<<8) /* 1 bit if cursor is here */
2652#define TILE_WIRE_SHIFT 9 /* 8 bits: RR UU LL DD
2653 * Each pair: 0=no wire, 1=unpowered,
2654 * 2=powered, 3=error highlight */
2655#define TILE_ENDPOINT_SHIFT 17 /* 2 bits: 0=no endpoint, 1=unpowered,
2656 * 2=powered, 3=power-source square */
2657#define TILE_WIRE_ON_EDGE_SHIFT 19 /* 8 bits: RR UU LL DD,
2658 * same encoding as TILE_WIRE_SHIFT */
2659#define TILE_ROTATING (1UL<<27) /* 1 bit if tile is rotating */
2660#define TILE_LOCKED (1UL<<28) /* 1 bit if tile is locked */
2661
2662static void draw_wires(drawing *dr, int cx, int cy, int radius,
2663 unsigned long tile, int bitmap,
2664 int colour, int halfwidth, const float matrix[4])
2665{
2666 float fpoints[12*2];
2667 int points[12*2];
2668 int npoints, d, dsh, i;
2669 bool any_wire_this_colour = false;
2670 float xf, yf;
2671
2672 npoints = 0;
2673 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2674 int wiretype = (tile >> (TILE_WIRE_SHIFT + 2*dsh)) & 3;
2675
2676 fpoints[2*npoints+0] = halfwidth * (X(d) + X(C(d)));
2677 fpoints[2*npoints+1] = halfwidth * (Y(d) + Y(C(d)));
2678 npoints++;
2679
2680 if (bitmap & (1 << wiretype)) {
2681 fpoints[2*npoints+0] = radius * X(d) + halfwidth * X(C(d));
2682 fpoints[2*npoints+1] = radius * Y(d) + halfwidth * Y(C(d));
2683 npoints++;
2684 fpoints[2*npoints+0] = radius * X(d) + halfwidth * X(A(d));
2685 fpoints[2*npoints+1] = radius * Y(d) + halfwidth * Y(A(d));
2686 npoints++;
2687
2688 any_wire_this_colour = true;
2689 }
2690 }
2691
2692 if (!any_wire_this_colour)
2693 return;
2694
2695 for (i = 0; i < npoints; i++) {
2696 rotated_coords(&xf, &yf, matrix, cx, cy, fpoints[2*i], fpoints[2*i+1]);
2697 points[2*i] = 0.5F + xf;
2698 points[2*i+1] = 0.5F + yf;
2699 }
2700
2701 draw_polygon(dr, points, npoints, colour, colour);
2702}
2703
2704static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y,
2705 unsigned long tile, float angle)
2706{
2707 int tx, ty;
2708 int clipx, clipy, clipX, clipY, clipw, cliph;
2709 int border_br = LINE_THICK/2, border_tl = LINE_THICK - border_br;
2710 int barrier_outline_thick = (LINE_THICK+1)/2;
2711 int bg, d, dsh, pass;
2712 int cx, cy, radius;
2713 float matrix[4];
2714
2715 tx = WINDOW_OFFSET + TILE_SIZE * x + border_br;
2716 ty = WINDOW_OFFSET + TILE_SIZE * y + border_br;
2717
2718 /*
2719 * Clip to the tile boundary, with adjustments if we're drawing
2720 * just outside the grid.
2721 */
2722 clipx = tx; clipX = tx + TILE_SIZE;
2723 clipy = ty; clipY = ty + TILE_SIZE;
2724 if (x == -1) {
2725 clipx = clipX - border_br - barrier_outline_thick;
2726 } else if (x == ds->width) {
2727 clipX = clipx + border_tl + barrier_outline_thick;
2728 }
2729 if (y == -1) {
2730 clipy = clipY - border_br - barrier_outline_thick;
2731 } else if (y == ds->height) {
2732 clipY = clipy + border_tl + barrier_outline_thick;
2733 }
2734 clipw = clipX - clipx;
2735 cliph = clipY - clipy;
2736 clip(dr, clipx, clipy, clipw, cliph);
2737
2738 /*
2739 * Clear the clip region.
2740 */
2741 bg = (tile & TILE_LOCKED) ? COL_LOCKED : COL_BACKGROUND;
2742 draw_rect(dr, clipx, clipy, clipw, cliph, bg);
2743
2744 /*
2745 * Draw the grid lines.
2746 */
2747 {
2748 int gridl = (x == -1 ? tx+TILE_SIZE-border_br : tx);
2749 int gridr = (x == ds->width ? tx+border_tl : tx+TILE_SIZE);
2750 int gridu = (y == -1 ? ty+TILE_SIZE-border_br : ty);
2751 int gridd = (y == ds->height ? ty+border_tl : ty+TILE_SIZE);
2752 if (x >= 0)
2753 draw_rect(dr, tx, gridu, border_tl, gridd-gridu, COL_BORDER);
2754 if (y >= 0)
2755 draw_rect(dr, gridl, ty, gridr-gridl, border_tl, COL_BORDER);
2756 if (x < ds->width)
2757 draw_rect(dr, tx+TILE_SIZE-border_br, gridu,
2758 border_br, gridd-gridu, COL_BORDER);
2759 if (y < ds->height)
2760 draw_rect(dr, gridl, ty+TILE_SIZE-border_br,
2761 gridr-gridl, border_br, COL_BORDER);
2762 }
2763
2764 /*
2765 * Draw the keyboard cursor.
2766 */
2767 if (tile & TILE_KEYBOARD_CURSOR) {
2768 int cursorcol = (tile & TILE_LOCKED) ? COL_BACKGROUND : COL_LOCKED;
2769 int inset_outer = TILE_SIZE/8, inset_inner = inset_outer + LINE_THICK;
2770 draw_rect(dr, tx + inset_outer, ty + inset_outer,
2771 TILE_SIZE - 2*inset_outer, TILE_SIZE - 2*inset_outer,
2772 cursorcol);
2773 draw_rect(dr, tx + inset_inner, ty + inset_inner,
2774 TILE_SIZE - 2*inset_inner, TILE_SIZE - 2*inset_inner,
2775 bg);
2776 }
2777
2778 radius = (TILE_SIZE+1)/2;
2779 cx = tx + radius;
2780 cy = ty + radius;
2781 radius++;
2782
2783 /*
2784 * Draw protrusions into this cell's edges of wires in
2785 * neighbouring cells, as given by the TILE_WIRE_ON_EDGE_SHIFT
2786 * flags. We only draw each of these if there _isn't_ a wire of
2787 * our own that's going to overlap it, which means either the
2788 * corresponding TILE_WIRE_SHIFT flag is zero, or else the
2789 * TILE_ROTATING flag is set (so that our main wire won't be drawn
2790 * in quite that place anyway).
2791 */
2792 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2793 int edgetype = ((tile >> (TILE_WIRE_ON_EDGE_SHIFT + 2*dsh)) & 3);
2794 if (edgetype == 0)
2795 continue; /* there isn't a wire on the edge */
2796 if (!(tile & TILE_ROTATING) &&
2797 ((tile >> (TILE_WIRE_SHIFT + 2*dsh)) & 3) != 0)
2798 continue; /* wire on edge would be overdrawn anyway */
2799
2800 for (pass = 0; pass < 2; pass++) {
2801 int x, y, w, h;
2802 int col = (pass == 0 || edgetype == 1 ? COL_WIRE :
2803 edgetype == 2 ? COL_POWERED : COL_ERR);
2804 int halfwidth = pass == 0 ? 2*LINE_THICK-1 : LINE_THICK-1;
2805
2806 if (X(d) < 0) {
2807 x = tx;
2808 w = border_tl;
2809 } else if (X(d) > 0) {
2810 x = tx + TILE_SIZE - border_br;
2811 w = border_br;
2812 } else {
2813 x = cx - halfwidth;
2814 w = 2 * halfwidth + 1;
2815 }
2816
2817 if (Y(d) < 0) {
2818 y = ty;
2819 h = border_tl;
2820 } else if (Y(d) > 0) {
2821 y = ty + TILE_SIZE - border_br;
2822 h = border_br;
2823 } else {
2824 y = cy - halfwidth;
2825 h = 2 * halfwidth + 1;
2826 }
2827
2828 draw_rect(dr, x, y, w, h, col);
2829 }
2830 }
2831
2832 /*
2833 * Set up the rotation matrix for the main cell contents, i.e.
2834 * everything that is centred in the grid square and optionally
2835 * rotated by an arbitrary angle about that centre point.
2836 */
2837 if (tile & TILE_ROTATING) {
2838 matrix[0] = (float)cos(angle * (float)PI / 180.0F);
2839 matrix[2] = (float)sin(angle * (float)PI / 180.0F);
2840 } else {
2841 matrix[0] = 1.0F;
2842 matrix[2] = 0.0F;
2843 }
2844 matrix[3] = matrix[0];
2845 matrix[1] = -matrix[2];
2846
2847 /*
2848 * Draw the wires.
2849 */
2850 draw_wires(dr, cx, cy, radius, tile,
2851 0xE, COL_WIRE, 2*LINE_THICK-1, matrix);
2852 draw_wires(dr, cx, cy, radius, tile,
2853 0x4, COL_POWERED, LINE_THICK-1, matrix);
2854 draw_wires(dr, cx, cy, radius, tile,
2855 0x8, COL_ERR, LINE_THICK-1, matrix);
2856
2857 /*
2858 * Draw the central box.
2859 */
2860 for (pass = 0; pass < 2; pass++) {
2861 int endtype = (tile >> TILE_ENDPOINT_SHIFT) & 3;
2862 if (endtype) {
2863 int i, points[8], col;
2864 float boxr = TILE_SIZE * 0.24F + (pass == 0 ? LINE_THICK-1 : 0);
2865
2866 col = (pass == 0 || endtype == 3 ? COL_WIRE :
2867 endtype == 2 ? COL_POWERED : COL_ENDPOINT);
2868
2869 points[0] = +1; points[1] = +1;
2870 points[2] = +1; points[3] = -1;
2871 points[4] = -1; points[5] = -1;
2872 points[6] = -1; points[7] = +1;
2873
2874 for (i = 0; i < 8; i += 2) {
2875 float x, y;
2876 rotated_coords(&x, &y, matrix, cx, cy,
2877 boxr * points[i], boxr * points[i+1]);
2878 points[i] = x + 0.5F;
2879 points[i+1] = y + 0.5F;
2880 }
2881
2882 draw_polygon(dr, points, 4, col, COL_WIRE);
2883 }
2884 }
2885
2886 /*
2887 * Draw barriers along grid edges.
2888 */
2889 for (pass = 0; pass < 2; pass++) {
2890 int btl = border_tl, bbr = border_br, col = COL_BARRIER;
2891 if (pass == 0) {
2892 btl += barrier_outline_thick;
2893 bbr += barrier_outline_thick;
2894 col = COL_WIRE;
2895 }
2896
2897 if (tile & (L << TILE_BARRIER_SHIFT))
2898 draw_rect(dr, tx, ty, btl, TILE_SIZE, col);
2899 if (tile & (R << TILE_BARRIER_SHIFT))
2900 draw_rect(dr, tx+TILE_SIZE-bbr, ty, bbr, TILE_SIZE, col);
2901 if (tile & (U << TILE_BARRIER_SHIFT))
2902 draw_rect(dr, tx, ty, TILE_SIZE, btl, col);
2903 if (tile & (D << TILE_BARRIER_SHIFT))
2904 draw_rect(dr, tx, ty+TILE_SIZE-bbr, TILE_SIZE, bbr, col);
2905
2906 if (tile & (R << TILE_BARRIER_CORNER_SHIFT))
2907 draw_rect(dr, tx+TILE_SIZE-bbr, ty, bbr, btl, col);
2908 if (tile & (U << TILE_BARRIER_CORNER_SHIFT))
2909 draw_rect(dr, tx, ty, btl, btl, col);
2910 if (tile & (L << TILE_BARRIER_CORNER_SHIFT))
2911 draw_rect(dr, tx, ty+TILE_SIZE-bbr, btl, bbr, col);
2912 if (tile & (D << TILE_BARRIER_CORNER_SHIFT))
2913 draw_rect(dr, tx+TILE_SIZE-bbr, ty+TILE_SIZE-bbr, bbr, bbr, col);
2914 }
2915
2916 /*
2917 * Unclip and draw update, to finish.
2918 */
2919 unclip(dr);
2920 draw_update(dr, clipx, clipy, clipw, cliph);
2921}
2922
2923static void game_redraw(drawing *dr, game_drawstate *ds,
2924 const game_state *oldstate, const game_state *state,
2925 int dir, const game_ui *ui,
2926 float t, float ft)
2927{
2928 int tx, ty, dx, dy, d, dsh, last_rotate_dir, frame;
2929 unsigned char *active;
2930 int *loops;
2931 float angle = 0.0;
2932
2933 tx = ty = -1;
2934 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
2935 state->last_rotate_dir;
2936 if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) {
2937 /*
2938 * We're animating a single tile rotation. Find the turning
2939 * tile.
2940 */
2941 tx = (dir==-1 ? oldstate->last_rotate_x : state->last_rotate_x);
2942 ty = (dir==-1 ? oldstate->last_rotate_y : state->last_rotate_y);
2943 angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
2944 state = oldstate;
2945 }
2946
2947 if (ft > 0) {
2948 /*
2949 * We're animating a completion flash. Find which frame
2950 * we're at.
2951 */
2952 frame = (int)(ft / FLASH_FRAME);
2953 } else {
2954 frame = 0;
2955 }
2956
2957 /*
2958 * Build up a map of what we want every tile to look like. We
2959 * include tiles one square outside the grid, for the outer edges
2960 * of barriers.
2961 */
2962 active = compute_active(state, ui->cx, ui->cy);
2963 loops = compute_loops(state, ui->unlocked_loops);
2964
2965 for (dy = -1; dy < ds->height+1; dy++) {
2966 for (dx = -1; dx < ds->width+1; dx++) {
2967 todraw(ds, dx, dy) = 0;
2968 }
2969 }
2970
2971 for (dy = 0; dy < ds->height; dy++) {
2972 int gy = (dy + ui->org_y) % ds->height;
2973 for (dx = 0; dx < ds->width; dx++) {
2974 int gx = (dx + ui->org_x) % ds->width;
2975 int t = (tile(state, gx, gy) |
2976 index(state, loops, gx, gy) |
2977 index(state, active, gx, gy));
2978
2979 for (d = 1, dsh = 0; d < 16; d *= 2, dsh++) {
2980 if (barrier(state, gx, gy) & d) {
2981 todraw(ds, dx, dy) |=
2982 d << TILE_BARRIER_SHIFT;
2983 todraw(ds, dx + X(d), dy + Y(d)) |=
2984 F(d) << TILE_BARRIER_SHIFT;
2985 todraw(ds, dx + X(A(d)), dy + Y(A(d))) |=
2986 C(d) << TILE_BARRIER_CORNER_SHIFT;
2987 todraw(ds, dx + X(A(d)) + X(d), dy + Y(A(d)) + Y(d)) |=
2988 F(d) << TILE_BARRIER_CORNER_SHIFT;
2989 todraw(ds, dx + X(C(d)), dy + Y(C(d))) |=
2990 d << TILE_BARRIER_CORNER_SHIFT;
2991 todraw(ds, dx + X(C(d)) + X(d), dy + Y(C(d)) + Y(d)) |=
2992 A(d) << TILE_BARRIER_CORNER_SHIFT;
2993 }
2994
2995 if (t & d) {
2996 int edgeval;
2997
2998 /* Highlight as an error any edge in a locked tile that
2999 * is adjacent to a lack-of-edge in another locked tile,
3000 * or to a barrier */
3001 if (t & LOCKED) {
3002 if (barrier(state, gx, gy) & d) {
3003 t |= ERR(d);
3004 } else {
3005 int ox, oy, t2;
3006 OFFSET(ox, oy, gx, gy, d, state);
3007 t2 = tile(state, ox, oy);
3008 if ((t2 & LOCKED) && !(t2 & F(d))) {
3009 t |= ERR(d);
3010 }
3011 }
3012 }
3013
3014 edgeval = (t & ERR(d) ? 3 : t & ACTIVE ? 2 : 1);
3015 todraw(ds, dx, dy) |= edgeval << (TILE_WIRE_SHIFT + dsh*2);
3016 if (!(gx == tx && gy == ty)) {
3017 todraw(ds, dx + X(d), dy + Y(d)) |=
3018 edgeval << (TILE_WIRE_ON_EDGE_SHIFT + (dsh ^ 2)*2);
3019 }
3020 }
3021 }
3022
3023 if (ui->cur_visible && gx == ui->cur_x && gy == ui->cur_y)
3024 todraw(ds, dx, dy) |= TILE_KEYBOARD_CURSOR;
3025
3026 if (gx == tx && gy == ty)
3027 todraw(ds, dx, dy) |= TILE_ROTATING;
3028
3029 if (gx == ui->cx && gy == ui->cy) {
3030 todraw(ds, dx, dy) |= 3 << TILE_ENDPOINT_SHIFT;
3031 } else if ((t & 0xF) != R && (t & 0xF) != U &&
3032 (t & 0xF) != L && (t & 0xF) != D) {
3033 /* this is not an endpoint tile */
3034 } else if (t & ACTIVE) {
3035 todraw(ds, dx, dy) |= 2 << TILE_ENDPOINT_SHIFT;
3036 } else {
3037 todraw(ds, dx, dy) |= 1 << TILE_ENDPOINT_SHIFT;
3038 }
3039
3040 if (t & LOCKED)
3041 todraw(ds, dx, dy) |= TILE_LOCKED;
3042
3043 /*
3044 * In a completion flash, we adjust the LOCKED bit
3045 * depending on our distance from the centre point and
3046 * the frame number.
3047 */
3048 if (frame >= 0) {
3049 int rcx = (ui->cx + ds->width - ui->org_x) % ds->width;
3050 int rcy = (ui->cy + ds->height - ui->org_y) % ds->height;
3051 int xdist, ydist, dist;
3052 xdist = (dx < rcx ? rcx - dx : dx - rcx);
3053 ydist = (dy < rcy ? rcy - dy : dy - rcy);
3054 dist = (xdist > ydist ? xdist : ydist);
3055
3056 if (frame >= dist && frame < dist+4 &&
3057 ((frame - dist) & 1))
3058 todraw(ds, dx, dy) ^= TILE_LOCKED;
3059 }
3060 }
3061 }
3062
3063 /*
3064 * Now draw any tile that differs from the way it was last drawn.
3065 * An exception is that if either the previous _or_ current state
3066 * has the TILE_ROTATING bit set, we must draw it regardless,
3067 * because it will have rotated to a different angle.q
3068 */
3069 for (dy = -1; dy < ds->height+1; dy++) {
3070 for (dx = -1; dx < ds->width+1; dx++) {
3071 int prev = visible(ds, dx, dy);
3072 int curr = todraw(ds, dx, dy);
3073 if (prev != curr || ((prev | curr) & TILE_ROTATING) != 0) {
3074 draw_tile(dr, ds, dx, dy, curr, angle);
3075 visible(ds, dx, dy) = curr;
3076 }
3077 }
3078 }
3079
3080 /*
3081 * Update the status bar.
3082 */
3083 {
3084 char statusbuf[256], *p;
3085 int i, n, n2, a;
3086 bool complete = false;
3087
3088 p = statusbuf;
3089 *p = '\0'; /* ensure even an empty status string is terminated */
3090
3091 if (state->used_solve) {
3092 p += sprintf(p, "Auto-solved. ");
3093 complete = true;
3094 } else if (state->completed) {
3095 p += sprintf(p, "COMPLETED! ");
3096 complete = true;
3097 }
3098
3099 /*
3100 * Omit the 'Active: n/N' counter completely if the source
3101 * tile is a completely empty one, because then the active
3102 * count can't help but read '1'.
3103 */
3104 if (tile(state, ui->cx, ui->cy) & 0xF) {
3105 n = state->width * state->height;
3106 for (i = a = n2 = 0; i < n; i++) {
3107 if (active[i])
3108 a++;
3109 if (state->tiles[i] & 0xF)
3110 n2++;
3111 }
3112
3113 /*
3114 * Also, if we're displaying a completion indicator and
3115 * the game is still in its completed state (i.e. every
3116 * tile is active), we might as well omit this too.
3117 */
3118 if (!complete || a < n2)
3119 p += sprintf(p, "Active: %d/%d", a, n2);
3120 }
3121
3122 status_bar(dr, statusbuf);
3123 }
3124
3125 sfree(active);
3126 sfree(loops);
3127}
3128
3129static float game_anim_length(const game_state *oldstate,
3130 const game_state *newstate, int dir, game_ui *ui)
3131{
3132 int last_rotate_dir;
3133
3134 /*
3135 * Don't animate if last_rotate_dir is zero.
3136 */
3137 last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
3138 newstate->last_rotate_dir;
3139 if (last_rotate_dir)
3140 return ROTATE_TIME;
3141
3142 return 0.0F;
3143}
3144
3145static float game_flash_length(const game_state *oldstate,
3146 const game_state *newstate, int dir, game_ui *ui)
3147{
3148 /*
3149 * If the game has just been completed, we display a completion
3150 * flash.
3151 */
3152 if (!oldstate->completed && newstate->completed &&
3153 !oldstate->used_solve && !newstate->used_solve) {
3154 int size = 0;
3155 if (size < newstate->width)
3156 size = newstate->width;
3157 if (size < newstate->height)
3158 size = newstate->height;
3159 return FLASH_FRAME * (size+4);
3160 }
3161
3162 return 0.0F;
3163}
3164
3165static void game_get_cursor_location(const game_ui *ui,
3166 const game_drawstate *ds,
3167 const game_state *state,
3168 const game_params *params,
3169 int *x, int *y, int *w, int *h)
3170{
3171 if(ui->cur_visible) {
3172 *x = WINDOW_OFFSET + TILE_SIZE * ui->cur_x;
3173 *y = WINDOW_OFFSET + TILE_SIZE * ui->cur_y;
3174
3175 *w = *h = TILE_SIZE;
3176 }
3177}
3178
3179static int game_status(const game_state *state)
3180{
3181 return state->completed ? +1 : 0;
3182}
3183
3184static void game_print_size(const game_params *params, const game_ui *ui,
3185 float *x, float *y)
3186{
3187 int pw, ph;
3188
3189 /*
3190 * I'll use 8mm squares by default.
3191 */
3192 game_compute_size(params, 800, ui, &pw, &ph);
3193 *x = pw / 100.0F;
3194 *y = ph / 100.0F;
3195}
3196
3197static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
3198 bool topleft, int v, bool drawlines, int ink)
3199{
3200 int tx, ty, cx, cy, r, br, k, thick;
3201
3202 tx = WINDOW_OFFSET + TILE_SIZE * x;
3203 ty = WINDOW_OFFSET + TILE_SIZE * y;
3204
3205 /*
3206 * Find our centre point.
3207 */
3208 if (topleft) {
3209 cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6);
3210 cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6);
3211 r = TILE_SIZE / 8;
3212 br = TILE_SIZE / 32;
3213 } else {
3214 cx = tx + TILE_SIZE / 2;
3215 cy = ty + TILE_SIZE / 2;
3216 r = TILE_SIZE / 2;
3217 br = TILE_SIZE / 8;
3218 }
3219 thick = r / 20;
3220
3221 /*
3222 * Draw the square block if we have an endpoint.
3223 */
3224 if (v == 1 || v == 2 || v == 4 || v == 8)
3225 draw_rect(dr, cx - br, cy - br, br*2, br*2, ink);
3226
3227 /*
3228 * Draw each radial line.
3229 */
3230 if (drawlines) {
3231 for (k = 1; k < 16; k *= 2)
3232 if (v & k) {
3233 int x1 = min(cx, cx + (r-thick) * X(k));
3234 int x2 = max(cx, cx + (r-thick) * X(k));
3235 int y1 = min(cy, cy + (r-thick) * Y(k));
3236 int y2 = max(cy, cy + (r-thick) * Y(k));
3237 draw_rect(dr, x1 - thick, y1 - thick,
3238 (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink);
3239 }
3240 }
3241}
3242
3243static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3244 int tilesize)
3245{
3246 int w = state->width, h = state->height;
3247 int ink = print_mono_colour(dr, 0);
3248 int x, y;
3249
3250 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3251 game_drawstate ads, *ds = &ads;
3252 game_set_size(dr, ds, NULL, tilesize);
3253
3254 /*
3255 * Border.
3256 */
3257 print_line_width(dr, TILE_SIZE / (state->wrapping ? 128 : 12));
3258 draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET,
3259 TILE_SIZE * w, TILE_SIZE * h, ink);
3260
3261 /*
3262 * Grid.
3263 */
3264 print_line_width(dr, TILE_SIZE / 128);
3265 for (x = 1; x < w; x++)
3266 draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET,
3267 WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h,
3268 ink);
3269 for (y = 1; y < h; y++)
3270 draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y,
3271 WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y,
3272 ink);
3273
3274 /*
3275 * Barriers.
3276 */
3277 for (y = 0; y <= h; y++)
3278 for (x = 0; x <= w; x++) {
3279 int b = barrier(state, x % w, y % h);
3280 if (x < w && (b & U))
3281 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3282 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3283 TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink);
3284 if (y < h && (b & L))
3285 draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
3286 WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
3287 TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink);
3288 }
3289
3290 /*
3291 * Grid contents.
3292 */
3293 for (y = 0; y < h; y++)
3294 for (x = 0; x < w; x++) {
3295 int vx, v = tile(state, x, y);
3296 int locked = v & LOCKED;
3297
3298 v &= 0xF;
3299
3300 /*
3301 * Rotate into a standard orientation for the top left
3302 * corner diagram.
3303 */
3304 vx = v;
3305 while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 &&
3306 vx != 5)
3307 vx = A(vx);
3308
3309 /*
3310 * Draw the top left corner diagram.
3311 */
3312 draw_diagram(dr, ds, x, y, true, vx, true, ink);
3313
3314 /*
3315 * Draw the real solution diagram, if we're doing so.
3316 */
3317 draw_diagram(dr, ds, x, y, false, v, locked, ink);
3318 }
3319}
3320
3321#ifdef COMBINED
3322#define thegame net
3323#endif
3324
3325const struct game thegame = {
3326 "Net", "games.net", "net",
3327 default_params,
3328 game_fetch_preset, NULL,
3329 decode_params,
3330 encode_params,
3331 free_params,
3332 dup_params,
3333 true, game_configure, custom_params,
3334 validate_params,
3335 new_game_desc,
3336 validate_desc,
3337 new_game,
3338 dup_game,
3339 free_game,
3340 true, solve_game,
3341 false, NULL, NULL, /* can_format_as_text_now, text_format */
3342 get_prefs, set_prefs,
3343 new_ui,
3344 free_ui,
3345 encode_ui,
3346 decode_ui,
3347 NULL, /* game_request_keys */
3348 game_changed_state,
3349 current_key_label,
3350 interpret_move,
3351 execute_move,
3352 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3353 game_colours,
3354 game_new_drawstate,
3355 game_free_drawstate,
3356 game_redraw,
3357 game_anim_length,
3358 game_flash_length,
3359 game_get_cursor_location,
3360 game_status,
3361 true, false, game_print_size, game_print,
3362 true, /* wants_statusbar */
3363 false, NULL, /* timing_state */
3364 0, /* flags */
3365};