A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 3365 lines 101 kB view raw
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};