A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 3057 lines 95 kB view raw
1/* 2 * pearl.c: Nikoli's `Masyu' puzzle. 3 */ 4 5/* 6 * TODO: 7 * 8 * - The current keyboard cursor mechanism works well on ordinary PC 9 * keyboards, but for platforms with only arrow keys and a select 10 * button or two, we may at some point need a simpler one which can 11 * handle 'x' markings without needing shift keys. For instance, a 12 * cursor with twice the grid resolution, so that it can range 13 * across face centres, edge centres and vertices; 'clicks' on face 14 * centres begin a drag as currently, clicks on edges toggle 15 * markings, and clicks on vertices are ignored (but it would be 16 * too confusing not to let the cursor rest on them). But I'm 17 * pretty sure that would be less pleasant to play on a full 18 * keyboard, so probably a #ifdef would be the thing. 19 * 20 * - Generation is still pretty slow, due to difficulty coming up in 21 * the first place with a loop that makes a soluble puzzle even 22 * with all possible clues filled in. 23 * + A possible alternative strategy to further tuning of the 24 * existing loop generator would be to throw the entire 25 * mechanism out and instead write a different generator from 26 * scratch which evolves the solution along with the puzzle: 27 * place a few clues, nail down a bit of the loop, place another 28 * clue, nail down some more, etc. However, I don't have a 29 * detailed plan for any such mechanism, so it may be a pipe 30 * dream. 31 */ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <assert.h> 37#include <ctype.h> 38#include <limits.h> 39#ifdef NO_TGMATH_H 40# include <math.h> 41#else 42# include <tgmath.h> 43#endif 44 45#include "puzzles.h" 46#include "grid.h" 47#include "loopgen.h" 48 49#ifdef STANDALONE_SOLVER 50#define SOLVER_DIAGNOSTICS 51static bool solver_show_working = false; 52#endif 53 54#define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0) 55 56#define NOCLUE 0 57#define CORNER 1 58#define STRAIGHT 2 59 60#define R 1 61#define U 2 62#define L 4 63#define D 8 64 65#define DX(d) ( ((d)==R) - ((d)==L) ) 66#define DY(d) ( ((d)==D) - ((d)==U) ) 67 68#define F(d) (((d << 2) | (d >> 2)) & 0xF) 69#define C(d) (((d << 3) | (d >> 1)) & 0xF) 70#define A(d) (((d << 1) | (d >> 3)) & 0xF) 71 72#define LR (L | R) 73#define RL (R | L) 74#define UD (U | D) 75#define DU (D | U) 76#define LU (L | U) 77#define UL (U | L) 78#define LD (L | D) 79#define DL (D | L) 80#define RU (R | U) 81#define UR (U | R) 82#define RD (R | D) 83#define DR (D | R) 84#define BLANK 0 85#define UNKNOWN 15 86 87#define bLR (1 << LR) 88#define bRL (1 << RL) 89#define bUD (1 << UD) 90#define bDU (1 << DU) 91#define bLU (1 << LU) 92#define bUL (1 << UL) 93#define bLD (1 << LD) 94#define bDL (1 << DL) 95#define bRU (1 << RU) 96#define bUR (1 << UR) 97#define bRD (1 << RD) 98#define bDR (1 << DR) 99#define bBLANK (1 << BLANK) 100 101enum { 102 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, 103 COL_CURSOR_BACKGROUND = COL_LOWLIGHT, 104 COL_BLACK, COL_WHITE, 105 COL_ERROR, COL_GRID, COL_FLASH, 106 COL_DRAGON, COL_DRAGOFF, 107 NCOLOURS 108}; 109 110enum { 111 PREF_APPEARANCE, 112 N_PREF_ITEMS 113}; 114 115/* Macro ickery copied from slant.c */ 116#define DIFFLIST(A) \ 117 A(EASY,Easy,e) \ 118 A(TRICKY,Tricky,t) 119#define ENUM(upper,title,lower) DIFF_ ## upper, 120#define TITLE(upper,title,lower) #title, 121#define ENCODE(upper,title,lower) #lower 122#define CONFIG(upper,title,lower) ":" #title 123enum { DIFFLIST(ENUM) DIFFCOUNT }; 124static char const *const pearl_diffnames[] = { DIFFLIST(TITLE) "(count)" }; 125static char const pearl_diffchars[] = DIFFLIST(ENCODE); 126#define DIFFCONFIG DIFFLIST(CONFIG) 127 128struct game_params { 129 int w, h; 130 int difficulty; 131 bool nosolve; /* XXX remove me! */ 132}; 133 134struct shared_state { 135 int w, h, sz; 136 char *clues; /* size w*h */ 137 int refcnt; 138}; 139 140#define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->shared->w && \ 141 (gy) >= 0 && (gy) < (state)->shared->h) 142struct game_state { 143 struct shared_state *shared; 144 char *lines; /* size w*h: lines placed */ 145 char *errors; /* size w*h: errors detected */ 146 char *marks; /* size w*h: 'no line here' marks placed. */ 147 bool completed, used_solve; 148}; 149 150#define DEFAULT_PRESET 3 151 152static const struct game_params pearl_presets[] = { 153 {6, 6, DIFF_EASY}, 154 {6, 6, DIFF_TRICKY}, 155 {8, 8, DIFF_EASY}, 156 {8, 8, DIFF_TRICKY}, 157 {10, 10, DIFF_EASY}, 158 {10, 10, DIFF_TRICKY}, 159 {12, 8, DIFF_EASY}, 160 {12, 8, DIFF_TRICKY}, 161}; 162 163static game_params *default_params(void) 164{ 165 game_params *ret = snew(game_params); 166 167 *ret = pearl_presets[DEFAULT_PRESET]; 168 ret->nosolve = false; 169 170 return ret; 171} 172 173static bool game_fetch_preset(int i, char **name, game_params **params) 174{ 175 game_params *ret; 176 char buf[64]; 177 178 if (i < 0 || i >= lenof(pearl_presets)) return false; 179 180 ret = default_params(); 181 *ret = pearl_presets[i]; /* struct copy */ 182 *params = ret; 183 184 sprintf(buf, "%dx%d %s", 185 pearl_presets[i].w, pearl_presets[i].h, 186 pearl_diffnames[pearl_presets[i].difficulty]); 187 *name = dupstr(buf); 188 189 return true; 190} 191 192static void free_params(game_params *params) 193{ 194 sfree(params); 195} 196 197static game_params *dup_params(const game_params *params) 198{ 199 game_params *ret = snew(game_params); 200 *ret = *params; /* structure copy */ 201 return ret; 202} 203 204static void decode_params(game_params *ret, char const *string) 205{ 206 ret->w = ret->h = atoi(string); 207 while (*string && isdigit((unsigned char) *string)) ++string; 208 if (*string == 'x') { 209 string++; 210 ret->h = atoi(string); 211 while (*string && isdigit((unsigned char)*string)) string++; 212 } 213 214 ret->difficulty = DIFF_EASY; 215 if (*string == 'd') { 216 int i; 217 string++; 218 for (i = 0; i < DIFFCOUNT; i++) 219 if (*string == pearl_diffchars[i]) 220 ret->difficulty = i; 221 if (*string) string++; 222 } 223 224 ret->nosolve = false; 225 if (*string == 'n') { 226 ret->nosolve = true; 227 string++; 228 } 229} 230 231static char *encode_params(const game_params *params, bool full) 232{ 233 char buf[256]; 234 sprintf(buf, "%dx%d", params->w, params->h); 235 if (full) 236 sprintf(buf + strlen(buf), "d%c%s", 237 pearl_diffchars[params->difficulty], 238 params->nosolve ? "n" : ""); 239 return dupstr(buf); 240} 241 242static config_item *game_configure(const game_params *params) 243{ 244 config_item *ret; 245 char buf[64]; 246 247 ret = snewn(5, config_item); 248 249 ret[0].name = "Width"; 250 ret[0].type = C_STRING; 251 sprintf(buf, "%d", params->w); 252 ret[0].u.string.sval = dupstr(buf); 253 254 ret[1].name = "Height"; 255 ret[1].type = C_STRING; 256 sprintf(buf, "%d", params->h); 257 ret[1].u.string.sval = dupstr(buf); 258 259 ret[2].name = "Difficulty"; 260 ret[2].type = C_CHOICES; 261 ret[2].u.choices.choicenames = DIFFCONFIG; 262 ret[2].u.choices.selected = params->difficulty; 263 264 ret[3].name = "Allow unsoluble"; 265 ret[3].type = C_BOOLEAN; 266 ret[3].u.boolean.bval = params->nosolve; 267 268 ret[4].name = NULL; 269 ret[4].type = C_END; 270 271 return ret; 272} 273 274static game_params *custom_params(const config_item *cfg) 275{ 276 game_params *ret = snew(game_params); 277 278 ret->w = atoi(cfg[0].u.string.sval); 279 ret->h = atoi(cfg[1].u.string.sval); 280 ret->difficulty = cfg[2].u.choices.selected; 281 ret->nosolve = cfg[3].u.boolean.bval; 282 283 return ret; 284} 285 286static const char *validate_params(const game_params *params, bool full) 287{ 288 if (params->w < 5) return "Width must be at least five"; 289 if (params->h < 5) return "Height must be at least five"; 290 if (params->w > INT_MAX / params->h) 291 return "Width times height must not be unreasonably large"; 292 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT) 293 return "Unknown difficulty level"; 294 if (params->difficulty >= DIFF_TRICKY && params->w + params->h < 11) 295 return "Width or height must be at least six for Tricky"; 296 297 return NULL; 298} 299 300/* ---------------------------------------------------------------------- 301 * Solver. 302 */ 303 304static int pearl_solve(int w, int h, char *clues, char *result, 305 int difficulty, bool partial) 306{ 307 int W = 2*w+1, H = 2*h+1; 308 short *workspace; 309 DSF *dsf; 310 int *dsfsize; 311 int x, y, b, d; 312 int ret = -1; 313 314 /* 315 * workspace[(2*y+1)*W+(2*x+1)] indicates the possible nature 316 * of the square (x,y), as a logical OR of bitfields. 317 * 318 * workspace[(2*y)*W+(2*x+1)], for x odd and y even, indicates 319 * whether the horizontal edge between (x,y) and (x+1,y) is 320 * connected (1), disconnected (2) or unknown (3). 321 * 322 * workspace[(2*y+1)*W+(2*x)], indicates the same about the 323 * vertical edge between (x,y) and (x,y+1). 324 * 325 * Initially, every square is considered capable of being in 326 * any of the seven possible states (two straights, four 327 * corners and empty), except those corresponding to clue 328 * squares which are more restricted. 329 * 330 * Initially, all edges are unknown, except the ones around the 331 * grid border which are known to be disconnected. 332 */ 333 workspace = snewn(W*H, short); 334 for (x = 0; x < W*H; x++) 335 workspace[x] = 0; 336 /* Square states */ 337 for (y = 0; y < h; y++) 338 for (x = 0; x < w; x++) 339 switch (clues[y*w+x]) { 340 case CORNER: 341 workspace[(2*y+1)*W+(2*x+1)] = bLU|bLD|bRU|bRD; 342 break; 343 case STRAIGHT: 344 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD; 345 break; 346 default: 347 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD|bLU|bLD|bRU|bRD|bBLANK; 348 break; 349 } 350 /* Horizontal edges */ 351 for (y = 0; y <= h; y++) 352 for (x = 0; x < w; x++) 353 workspace[(2*y)*W+(2*x+1)] = (y==0 || y==h ? 2 : 3); 354 /* Vertical edges */ 355 for (y = 0; y < h; y++) 356 for (x = 0; x <= w; x++) 357 workspace[(2*y+1)*W+(2*x)] = (x==0 || x==w ? 2 : 3); 358 359 /* 360 * We maintain a dsf of connected squares, together with a 361 * count of the size of each equivalence class. 362 */ 363 dsf = dsf_new(w*h); 364 dsfsize = snewn(w*h, int); 365 366 /* 367 * Now repeatedly try to find something we can do. 368 */ 369 while (1) { 370 bool done_something = false; 371 372#ifdef SOLVER_DIAGNOSTICS 373 if (solver_show_working) { 374 for (y = 0; y < H; y++) { 375 for (x = 0; x < W; x++) 376 printf("%*x", (x&1) ? 5 : 2, workspace[y*W+x]); 377 printf("\n"); 378 } 379 } 380#endif 381 382 /* 383 * Go through the square state words, and discard any 384 * square state which is inconsistent with known facts 385 * about the edges around the square. 386 */ 387 for (y = 0; y < h; y++) 388 for (x = 0; x < w; x++) { 389 for (b = 0; b < 0xD; b++) 390 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) { 391 /* 392 * If any edge of this square is known to 393 * be connected when state b would require 394 * it disconnected, or vice versa, discard 395 * the state. 396 */ 397 for (d = 1; d <= 8; d += d) { 398 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d); 399 if (workspace[ey*W+ex] == 400 ((b & d) ? 2 : 1)) { 401 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<b); 402#ifdef SOLVER_DIAGNOSTICS 403 if (solver_show_working) 404 printf("edge (%d,%d)-(%d,%d) rules out " 405 "state %d for square (%d,%d)\n", 406 ex/2, ey/2, (ex+1)/2, (ey+1)/2, 407 b, x, y); 408#endif 409 done_something = true; 410 break; 411 } 412 } 413 } 414 415 /* 416 * Consistency check: each square must have at 417 * least one state left! 418 */ 419 if (!workspace[(2*y+1)*W+(2*x+1)]) { 420#ifdef SOLVER_DIAGNOSTICS 421 if (solver_show_working) 422 printf("edge check at (%d,%d): inconsistency\n", x, y); 423#endif 424 ret = 0; 425 goto cleanup; 426 } 427 } 428 429 /* 430 * Now go through the states array again, and nail down any 431 * unknown edge if one of its neighbouring squares makes it 432 * known. 433 */ 434 for (y = 0; y < h; y++) 435 for (x = 0; x < w; x++) { 436 int edgeor = 0, edgeand = 15; 437 438 for (b = 0; b < 0xD; b++) 439 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) { 440 edgeor |= b; 441 edgeand &= b; 442 } 443 444 /* 445 * Now any bit clear in edgeor marks a disconnected 446 * edge, and any bit set in edgeand marks a 447 * connected edge. 448 */ 449 450 /* First check consistency: neither bit is both! */ 451 if (edgeand & ~edgeor) { 452#ifdef SOLVER_DIAGNOSTICS 453 if (solver_show_working) 454 printf("square check at (%d,%d): inconsistency\n", 455 x, y); 456#endif 457 ret = 0; 458 goto cleanup; 459 } 460 461 for (d = 1; d <= 8; d += d) { 462 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d); 463 464 if (!(edgeor & d) && workspace[ey*W+ex] == 3) { 465 workspace[ey*W+ex] = 2; 466 done_something = true; 467#ifdef SOLVER_DIAGNOSTICS 468 if (solver_show_working) 469 printf("possible states of square (%d,%d) force " 470 "edge (%d,%d)-(%d,%d) to be disconnected\n", 471 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2); 472#endif 473 } else if ((edgeand & d) && workspace[ey*W+ex] == 3) { 474 workspace[ey*W+ex] = 1; 475 done_something = true; 476#ifdef SOLVER_DIAGNOSTICS 477 if (solver_show_working) 478 printf("possible states of square (%d,%d) force " 479 "edge (%d,%d)-(%d,%d) to be connected\n", 480 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2); 481#endif 482 } 483 } 484 } 485 486 if (done_something) 487 continue; 488 489 /* 490 * Now for longer-range clue-based deductions (using the 491 * rules that a corner clue must connect to two straight 492 * squares, and a straight clue must connect to at least 493 * one corner square). 494 */ 495 for (y = 0; y < h; y++) 496 for (x = 0; x < w; x++) 497 switch (clues[y*w+x]) { 498 case CORNER: 499 for (d = 1; d <= 8; d += d) { 500 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d); 501 int fx = ex + DX(d), fy = ey + DY(d); 502 int type = d | F(d); 503 504 if (workspace[ey*W+ex] == 1) { 505 /* 506 * If a corner clue is connected on any 507 * edge, then we can immediately nail 508 * down the square beyond that edge as 509 * being a straight in the appropriate 510 * direction. 511 */ 512 if (workspace[fy*W+fx] != (1<<type)) { 513 workspace[fy*W+fx] = (1<<type); 514 done_something = true; 515#ifdef SOLVER_DIAGNOSTICS 516 if (solver_show_working) 517 printf("corner clue at (%d,%d) forces " 518 "square (%d,%d) into state %d\n", 519 x, y, fx/2, fy/2, type); 520#endif 521 522 } 523 } else if (workspace[ey*W+ex] == 3) { 524 /* 525 * Conversely, if a corner clue is 526 * separated by an unknown edge from a 527 * square which _cannot_ be a straight 528 * in the appropriate direction, we can 529 * mark that edge as disconnected. 530 */ 531 if (!(workspace[fy*W+fx] & (1<<type))) { 532 workspace[ey*W+ex] = 2; 533 done_something = true; 534#ifdef SOLVER_DIAGNOSTICS 535 if (solver_show_working) 536 printf("corner clue at (%d,%d), plus " 537 "square (%d,%d) not being state %d, " 538 "disconnects edge (%d,%d)-(%d,%d)\n", 539 x, y, fx/2, fy/2, type, 540 ex/2, ey/2, (ex+1)/2, (ey+1)/2); 541#endif 542 543 } 544 } 545 } 546 547 break; 548 case STRAIGHT: 549 /* 550 * If a straight clue is between two squares 551 * neither of which is capable of being a 552 * corner connected to it, then the straight 553 * clue cannot point in that direction. 554 */ 555 for (d = 1; d <= 2; d += d) { 556 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d); 557 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d); 558 int type = d | F(d); 559 560 if (!(workspace[(2*y+1)*W+(2*x+1)] & (1<<type))) 561 continue; 562 563 if (!(workspace[fy*W+fx] & ((1<<(F(d)|A(d))) | 564 (1<<(F(d)|C(d))))) && 565 !(workspace[gy*W+gx] & ((1<<( d |A(d))) | 566 (1<<( d |C(d)))))) { 567 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<type); 568 done_something = true; 569#ifdef SOLVER_DIAGNOSTICS 570 if (solver_show_working) 571 printf("straight clue at (%d,%d) cannot " 572 "corner at (%d,%d) or (%d,%d) so is " 573 "not state %d\n", 574 x, y, fx/2, fy/2, gx/2, gy/2, type); 575#endif 576 } 577 578 } 579 580 /* 581 * If a straight clue with known direction is 582 * connected on one side to a known straight, 583 * then on the other side it must be a corner. 584 */ 585 for (d = 1; d <= 8; d += d) { 586 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d); 587 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d); 588 int type = d | F(d); 589 590 if (workspace[(2*y+1)*W+(2*x+1)] != (1<<type)) 591 continue; 592 593 if (!(workspace[fy*W+fx] &~ (bLR|bUD)) && 594 (workspace[gy*W+gx] &~ (bLU|bLD|bRU|bRD))) { 595 workspace[gy*W+gx] &= (bLU|bLD|bRU|bRD); 596 done_something = true; 597#ifdef SOLVER_DIAGNOSTICS 598 if (solver_show_working) 599 printf("straight clue at (%d,%d) connecting " 600 "to straight at (%d,%d) makes (%d,%d) " 601 "a corner\n", 602 x, y, fx/2, fy/2, gx/2, gy/2); 603#endif 604 } 605 606 } 607 break; 608 } 609 610 if (done_something) 611 continue; 612 613 /* 614 * Now detect shortcut loops. 615 */ 616 617 { 618 int nonblanks, loopclass; 619 620 dsf_reinit(dsf); 621 for (x = 0; x < w*h; x++) 622 dsfsize[x] = 1; 623 624 /* 625 * First go through the edge entries and update the dsf 626 * of which squares are connected to which others. We 627 * also track the number of squares in each equivalence 628 * class, and count the overall number of 629 * known-non-blank squares. 630 * 631 * In the process of doing this, we must notice if a 632 * loop has already been formed. If it has, we blank 633 * out any square which isn't part of that loop 634 * (failing a consistency check if any such square does 635 * not have BLANK as one of its remaining options) and 636 * exit the deduction loop with success. 637 */ 638 nonblanks = 0; 639 loopclass = -1; 640 for (y = 1; y < H-1; y++) 641 for (x = 1; x < W-1; x++) 642 if ((y ^ x) & 1) { 643 /* 644 * (x,y) are the workspace coordinates of 645 * an edge field. Compute the normal-space 646 * coordinates of the squares it connects. 647 */ 648 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax; 649 int bx = x/2, by = y/2, bc = by*w+bx; 650 651 /* 652 * If the edge is connected, do the dsf 653 * thing. 654 */ 655 if (workspace[y*W+x] == 1) { 656 int ae, be; 657 658 ae = dsf_canonify(dsf, ac); 659 be = dsf_canonify(dsf, bc); 660 661 if (ae == be) { 662 /* 663 * We have a loop! 664 */ 665 if (loopclass != -1) { 666 /* 667 * In fact, we have two 668 * separate loops, which is 669 * doom. 670 */ 671#ifdef SOLVER_DIAGNOSTICS 672 if (solver_show_working) 673 printf("two loops found in grid!\n"); 674#endif 675 ret = 0; 676 goto cleanup; 677 } 678 loopclass = ae; 679 } else { 680 /* 681 * Merge the two equivalence 682 * classes. 683 */ 684 int size = dsfsize[ae] + dsfsize[be]; 685 dsf_merge(dsf, ac, bc); 686 ae = dsf_canonify(dsf, ac); 687 dsfsize[ae] = size; 688 } 689 } 690 } else if ((y & x) & 1) { 691 /* 692 * (x,y) are the workspace coordinates of a 693 * square field. If the square is 694 * definitely not blank, count it. 695 */ 696 if (!(workspace[y*W+x] & bBLANK)) 697 nonblanks++; 698 } 699 700 /* 701 * If we discovered an existing loop above, we must now 702 * blank every square not part of it, and exit the main 703 * deduction loop. 704 */ 705 if (loopclass != -1) { 706#ifdef SOLVER_DIAGNOSTICS 707 if (solver_show_working) 708 printf("loop found in grid!\n"); 709#endif 710 for (y = 0; y < h; y++) 711 for (x = 0; x < w; x++) 712 if (dsf_canonify(dsf, y*w+x) != loopclass) { 713 if (workspace[(y*2+1)*W+(x*2+1)] & bBLANK) { 714 workspace[(y*2+1)*W+(x*2+1)] = bBLANK; 715 } else { 716 /* 717 * This square is not part of the 718 * loop, but is known non-blank. We 719 * have goofed. 720 */ 721#ifdef SOLVER_DIAGNOSTICS 722 if (solver_show_working) 723 printf("non-blank square (%d,%d) found " 724 "outside loop!\n", x, y); 725#endif 726 ret = 0; 727 goto cleanup; 728 } 729 } 730 /* 731 * And we're done. 732 */ 733 ret = 1; 734 break; 735 } 736 737 /* Further deductions are considered 'tricky'. */ 738 if (difficulty == DIFF_EASY) goto done_deductions; 739 740 /* 741 * Now go through the workspace again and mark any edge 742 * which would cause a shortcut loop (i.e. would 743 * connect together two squares in the same equivalence 744 * class, and that equivalence class does not contain 745 * _all_ the known-non-blank squares currently in the 746 * grid) as disconnected. Also, mark any _square state_ 747 * which would cause a shortcut loop as disconnected. 748 */ 749 for (y = 1; y < H-1; y++) 750 for (x = 1; x < W-1; x++) 751 if ((y ^ x) & 1) { 752 /* 753 * (x,y) are the workspace coordinates of 754 * an edge field. Compute the normal-space 755 * coordinates of the squares it connects. 756 */ 757 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax; 758 int bx = x/2, by = y/2, bc = by*w+bx; 759 760 /* 761 * If the edge is currently unknown, and 762 * sits between two squares in the same 763 * equivalence class, and the size of that 764 * class is less than nonblanks, then 765 * connecting this edge would be a shortcut 766 * loop and so we must not do so. 767 */ 768 if (workspace[y*W+x] == 3) { 769 int ae, be; 770 771 ae = dsf_canonify(dsf, ac); 772 be = dsf_canonify(dsf, bc); 773 774 if (ae == be) { 775 /* 776 * We have a loop. Is it a shortcut? 777 */ 778 if (dsfsize[ae] < nonblanks) { 779 /* 780 * Yes! Mark this edge disconnected. 781 */ 782 workspace[y*W+x] = 2; 783 done_something = true; 784#ifdef SOLVER_DIAGNOSTICS 785 if (solver_show_working) 786 printf("edge (%d,%d)-(%d,%d) would " 787 "create a shortcut loop, hence " 788 "must be disconnected\n", 789 x/2, y/2, (x+1)/2, (y+1)/2); 790#endif 791 } 792 } 793 } 794 } else if ((y & x) & 1) { 795 /* 796 * (x,y) are the workspace coordinates of a 797 * square field. Go through its possible 798 * (non-blank) states and see if any gives 799 * rise to a shortcut loop. 800 * 801 * This is slightly fiddly, because we have 802 * to check whether this square is already 803 * part of the same equivalence class as 804 * the things it's joining. 805 */ 806 int ae = dsf_canonify(dsf, (y/2)*w+(x/2)); 807 808 for (b = 2; b < 0xD; b++) 809 if (workspace[y*W+x] & (1<<b)) { 810 /* 811 * Find the equivalence classes of 812 * the two squares this one would 813 * connect if it were in this 814 * state. 815 */ 816 int e = -1; 817 int connections = 0; 818 819 for (d = 1; d <= 8; d += d) if (b & d) { 820 int xx = x/2 + DX(d), yy = y/2 + DY(d); 821 int ee = dsf_canonify(dsf, yy*w+xx); 822 823 if (e == -1) 824 e = ee; 825 else if (e != ee) 826 e = -2; 827 if (workspace[(y+DY(d))*W+(x+DX(d))] == 1) 828 connections++; 829 } 830 831 if (e >= 0 && connections < 2) { 832 /* 833 * This square state would form 834 * a loop on equivalence class 835 * e. Measure the size of that 836 * loop, and see if it's a 837 * shortcut. 838 */ 839 int loopsize = dsfsize[e]; 840 if (e != ae) 841 loopsize++;/* add the square itself */ 842 if (loopsize < nonblanks) { 843 /* 844 * It is! Mark this square 845 * state invalid. 846 */ 847 workspace[y*W+x] &= ~(1<<b); 848 done_something = true; 849#ifdef SOLVER_DIAGNOSTICS 850 if (solver_show_working) 851 printf("square (%d,%d) would " 852 "create a shortcut loop in " 853 "state %d, hence cannot " 854 "be\n", x/2, y/2, b); 855#endif 856 } 857 } 858 } 859 } 860 } 861 862done_deductions: 863 864 if (done_something) 865 continue; 866 867 /* 868 * If we reach here, there is nothing left we can do. 869 * Return 2 for ambiguous puzzle. 870 */ 871 ret = 2; 872 break; 873 } 874 875cleanup: 876 877 /* 878 * If ret = 1 then we've successfully achieved a solution. This 879 * means that we expect every square to be nailed down to 880 * exactly one possibility. If this is the case, or if the caller 881 * asked for a partial solution anyway, transcribe those 882 * possibilities into the result array. 883 */ 884 if (ret == 1 || partial) { 885 for (y = 0; y < h; y++) { 886 for (x = 0; x < w; x++) { 887 for (b = 0; b < 0xD; b++) 888 if (workspace[(2*y+1)*W+(2*x+1)] == (1<<b)) { 889 result[y*w+x] = b; 890 break; 891 } 892 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */ 893 } 894 } 895 896 /* 897 * Ensure we haven't left the _data structure_ inconsistent, 898 * regardless of the consistency of the _puzzle_. In 899 * particular, we should never have marked one square as 900 * linked to its neighbour if the neighbour is not 901 * reciprocally linked back to the original square. 902 * 903 * This can happen if we get part way through solving an 904 * impossible puzzle and then give up trying to make further 905 * progress. So here we fix it up to avoid confusing the rest 906 * of the game. 907 */ 908 for (y = 0; y < h; y++) { 909 for (x = 0; x < w; x++) { 910 for (d = 1; d <= 8; d += d) { 911 int nx = x + DX(d), ny = y + DY(d); 912 int rlink; 913 if (0 <= nx && nx < w && 0 <= ny && ny < h) 914 rlink = result[ny*w+nx] & F(d); 915 else 916 rlink = 0; /* off-board squares don't link back */ 917 918 /* If other square doesn't link to us, don't link to it */ 919 if (!rlink) 920 result[y*w+x] &= ~d; 921 } 922 } 923 } 924 } 925 926 sfree(dsfsize); 927 dsf_free(dsf); 928 sfree(workspace); 929 assert(ret >= 0); 930 return ret; 931} 932 933/* ---------------------------------------------------------------------- 934 * Loop generator. 935 */ 936 937/* 938 * We use the loop generator code from loopy, hard-coding to a square 939 * grid of the appropriate size. Knowing the grid layout and the tile 940 * size we can shrink that to our small grid and then make our line 941 * layout from the face colour info. 942 * 943 * We provide a bias function to the loop generator which tries to 944 * bias in favour of loops with more scope for Pearl black clues. This 945 * seems to improve the success rate of the puzzle generator, in that 946 * such loops have a better chance of being soluble with all valid 947 * clues put in. 948 */ 949 950struct pearl_loopgen_bias_ctx { 951 /* 952 * Our bias function counts the number of 'black clue' corners 953 * (i.e. corners adjacent to two straights) in both the 954 * BLACK/nonBLACK and WHITE/nonWHITE boundaries. In order to do 955 * this, we must: 956 * 957 * - track the edges that are part of each of those loops 958 * - track the types of vertex in each loop (corner, straight, 959 * none) 960 * - track the current black-clue status of each vertex in each 961 * loop. 962 * 963 * Each of these chunks of data is updated incrementally from the 964 * previous one, to avoid slowdown due to the bias function 965 * rescanning the whole grid every time it's called. 966 * 967 * So we need a lot of separate arrays, plus a tdq for each one, 968 * and we must repeat it all twice for the BLACK and WHITE 969 * boundaries. 970 */ 971 struct pearl_loopgen_bias_ctx_boundary { 972 int colour; /* FACE_WHITE or FACE_BLACK */ 973 974 bool *edges; /* is each edge part of the loop? */ 975 tdq *edges_todo; 976 977 char *vertextypes; /* bits 0-3 == outgoing edge bitmap; 978 * bit 4 set iff corner clue. 979 * Hence, 0 means non-vertex; 980 * nonzero but bit 4 zero = straight. */ 981 int *neighbour[2]; /* indices of neighbour vertices in loop */ 982 tdq *vertextypes_todo; 983 984 char *blackclues; /* is each vertex a black clue site? */ 985 tdq *blackclues_todo; 986 } boundaries[2]; /* boundaries[0]=WHITE, [1]=BLACK */ 987 988 char *faces; /* remember last-seen colour of each face */ 989 tdq *faces_todo; 990 991 int score; 992 993 grid *g; 994}; 995static int pearl_loopgen_bias(void *vctx, char *board, int face) 996{ 997 struct pearl_loopgen_bias_ctx *ctx = (struct pearl_loopgen_bias_ctx *)vctx; 998 grid *g = ctx->g; 999 int oldface, newface; 1000 int i, j, k; 1001 1002 tdq_add(ctx->faces_todo, face); 1003 while ((j = tdq_remove(ctx->faces_todo)) >= 0) { 1004 oldface = ctx->faces[j]; 1005 ctx->faces[j] = newface = board[j]; 1006 for (i = 0; i < 2; i++) { 1007 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i]; 1008 int c = b->colour; 1009 1010 /* 1011 * If the face has changed either from or to colour c, we need 1012 * to reprocess the edges for this boundary. 1013 */ 1014 if (oldface == c || newface == c) { 1015 grid_face *f = g->faces[face]; 1016 for (k = 0; k < f->order; k++) 1017 tdq_add(b->edges_todo, f->edges[k]->index); 1018 } 1019 } 1020 } 1021 1022 for (i = 0; i < 2; i++) { 1023 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i]; 1024 int c = b->colour; 1025 1026 /* 1027 * Go through the to-do list of edges. For each edge, decide 1028 * anew whether it's part of this boundary or not. Any edge 1029 * that changes state has to have both its endpoints put on 1030 * the vertextypes_todo list. 1031 */ 1032 while ((j = tdq_remove(b->edges_todo)) >= 0) { 1033 grid_edge *e = g->edges[j]; 1034 int fc1 = e->face1 ? board[e->face1->index] : FACE_BLACK; 1035 int fc2 = e->face2 ? board[e->face2->index] : FACE_BLACK; 1036 bool oldedge = b->edges[j]; 1037 bool newedge = (fc1==c) ^ (fc2==c); 1038 if (oldedge != newedge) { 1039 b->edges[j] = newedge; 1040 tdq_add(b->vertextypes_todo, e->dot1->index); 1041 tdq_add(b->vertextypes_todo, e->dot2->index); 1042 } 1043 } 1044 1045 /* 1046 * Go through the to-do list of vertices whose types need 1047 * refreshing. For each one, decide whether it's a corner, a 1048 * straight, or a vertex not in the loop, and in the former 1049 * two cases also work out the indices of its neighbour 1050 * vertices along the loop. Any vertex that changes state must 1051 * be put back on the to-do list for deciding if it's a black 1052 * clue site, and so must its two new neighbours _and_ its two 1053 * old neighbours. 1054 */ 1055 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) { 1056 grid_dot *d = g->dots[j]; 1057 int neighbours[2], type = 0, n = 0; 1058 1059 for (k = 0; k < d->order; k++) { 1060 grid_edge *e = d->edges[k]; 1061 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1); 1062 /* dir == 0,1,2,3 for an edge going L,U,R,D */ 1063 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y); 1064 int ei = e->index; 1065 if (b->edges[ei]) { 1066 type |= 1 << dir; 1067 neighbours[n] = d2->index; 1068 n++; 1069 } 1070 } 1071 1072 /* 1073 * Decide if it's a corner, and set the corner flag if so. 1074 */ 1075 if (type != 0 && type != 0x5 && type != 0xA) 1076 type |= 0x10; 1077 1078 if (type != b->vertextypes[j]) { 1079 /* 1080 * Recompute old neighbours, if any. 1081 */ 1082 if (b->vertextypes[j]) { 1083 tdq_add(b->blackclues_todo, b->neighbour[0][j]); 1084 tdq_add(b->blackclues_todo, b->neighbour[1][j]); 1085 } 1086 /* 1087 * Recompute this vertex. 1088 */ 1089 tdq_add(b->blackclues_todo, j); 1090 b->vertextypes[j] = type; 1091 /* 1092 * Recompute new neighbours, if any. 1093 */ 1094 if (b->vertextypes[j]) { 1095 b->neighbour[0][j] = neighbours[0]; 1096 b->neighbour[1][j] = neighbours[1]; 1097 tdq_add(b->blackclues_todo, b->neighbour[0][j]); 1098 tdq_add(b->blackclues_todo, b->neighbour[1][j]); 1099 } 1100 } 1101 } 1102 1103 /* 1104 * Go through the list of vertices which we must check to see 1105 * if they're black clue sites. Each one is a black clue site 1106 * iff it is a corner and its loop neighbours are non-corners. 1107 * Adjust the running total of black clues we've counted. 1108 */ 1109 while ((j = tdq_remove(b->blackclues_todo)) >= 0) { 1110 ctx->score -= b->blackclues[j]; 1111 b->blackclues[j] = ((b->vertextypes[j] & 0x10) && 1112 !((b->vertextypes[b->neighbour[0][j]] | 1113 b->vertextypes[b->neighbour[1][j]]) 1114 & 0x10)); 1115 ctx->score += b->blackclues[j]; 1116 } 1117 } 1118 1119 return ctx->score; 1120} 1121 1122static void pearl_loopgen(int w, int h, char *lines, random_state *rs, grid *g) 1123{ 1124 char *board = snewn(g->num_faces, char); 1125 int i, s = g->tilesize; 1126 struct pearl_loopgen_bias_ctx biasctx; 1127 1128 memset(lines, 0, w*h); 1129 1130 /* 1131 * Initialise the context for the bias function. Initially we fill 1132 * all the to-do lists, so that the first call will scan 1133 * everything; thereafter the lists stay empty so we make 1134 * incremental changes. 1135 */ 1136 biasctx.g = g; 1137 biasctx.faces = snewn(g->num_faces, char); 1138 biasctx.faces_todo = tdq_new(g->num_faces); 1139 tdq_fill(biasctx.faces_todo); 1140 biasctx.score = 0; 1141 memset(biasctx.faces, FACE_GREY, g->num_faces); 1142 for (i = 0; i < 2; i++) { 1143 biasctx.boundaries[i].edges = snewn(g->num_edges, bool); 1144 memset(biasctx.boundaries[i].edges, 0, g->num_edges * sizeof(bool)); 1145 biasctx.boundaries[i].edges_todo = tdq_new(g->num_edges); 1146 tdq_fill(biasctx.boundaries[i].edges_todo); 1147 biasctx.boundaries[i].vertextypes = snewn(g->num_dots, char); 1148 memset(biasctx.boundaries[i].vertextypes, 0, g->num_dots); 1149 biasctx.boundaries[i].neighbour[0] = snewn(g->num_dots, int); 1150 biasctx.boundaries[i].neighbour[1] = snewn(g->num_dots, int); 1151 biasctx.boundaries[i].vertextypes_todo = tdq_new(g->num_dots); 1152 tdq_fill(biasctx.boundaries[i].vertextypes_todo); 1153 biasctx.boundaries[i].blackclues = snewn(g->num_dots, char); 1154 memset(biasctx.boundaries[i].blackclues, 0, g->num_dots); 1155 biasctx.boundaries[i].blackclues_todo = tdq_new(g->num_dots); 1156 tdq_fill(biasctx.boundaries[i].blackclues_todo); 1157 } 1158 biasctx.boundaries[0].colour = FACE_WHITE; 1159 biasctx.boundaries[1].colour = FACE_BLACK; 1160 generate_loop(g, board, rs, pearl_loopgen_bias, &biasctx); 1161 sfree(biasctx.faces); 1162 tdq_free(biasctx.faces_todo); 1163 for (i = 0; i < 2; i++) { 1164 sfree(biasctx.boundaries[i].edges); 1165 tdq_free(biasctx.boundaries[i].edges_todo); 1166 sfree(biasctx.boundaries[i].vertextypes); 1167 sfree(biasctx.boundaries[i].neighbour[0]); 1168 sfree(biasctx.boundaries[i].neighbour[1]); 1169 tdq_free(biasctx.boundaries[i].vertextypes_todo); 1170 sfree(biasctx.boundaries[i].blackclues); 1171 tdq_free(biasctx.boundaries[i].blackclues_todo); 1172 } 1173 1174 for (i = 0; i < g->num_edges; i++) { 1175 grid_edge *e = g->edges[i]; 1176 enum face_colour c1 = FACE_COLOUR(e->face1); 1177 enum face_colour c2 = FACE_COLOUR(e->face2); 1178 assert(c1 != FACE_GREY); 1179 assert(c2 != FACE_GREY); 1180 if (c1 != c2) { 1181 /* This grid edge is on the loop: lay line along it */ 1182 int x1 = e->dot1->x/s, y1 = e->dot1->y/s; 1183 int x2 = e->dot2->x/s, y2 = e->dot2->y/s; 1184 1185 /* (x1,y1) and (x2,y2) are now in our grid coords (0-w,0-h). */ 1186 if (x1 == x2) { 1187 if (y1 > y2) SWAP(y1,y2); 1188 1189 assert(y1+1 == y2); 1190 lines[y1*w+x1] |= D; 1191 lines[y2*w+x1] |= U; 1192 } else if (y1 == y2) { 1193 if (x1 > x2) SWAP(x1,x2); 1194 1195 assert(x1+1 == x2); 1196 lines[y1*w+x1] |= R; 1197 lines[y1*w+x2] |= L; 1198 } else 1199 assert(!"grid with diagonal coords?!"); 1200 } 1201 } 1202 1203 sfree(board); 1204 1205#if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS 1206 printf("as returned:\n"); 1207 for (y = 0; y < h; y++) { 1208 for (x = 0; x < w; x++) { 1209 int type = lines[y*w+x]; 1210 char s[5], *p = s; 1211 if (type & L) *p++ = 'L'; 1212 if (type & R) *p++ = 'R'; 1213 if (type & U) *p++ = 'U'; 1214 if (type & D) *p++ = 'D'; 1215 *p = '\0'; 1216 printf("%3s", s); 1217 } 1218 printf("\n"); 1219 } 1220 printf("\n"); 1221#endif 1222} 1223 1224static int new_clues(const game_params *params, random_state *rs, 1225 char *clues, char *grid_out) 1226{ 1227 int w = params->w, h = params->h, diff = params->difficulty; 1228 int ngen = 0, x, y, d, ret, i; 1229 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL); 1230 1231 /* 1232 * Difficulty exception: 5x5 Tricky is not generable (the 1233 * generator will spin forever trying) and so we fudge it to Easy. 1234 */ 1235 if (w == 5 && h == 5 && diff > DIFF_EASY) 1236 diff = DIFF_EASY; 1237 1238 while (1) { 1239 ngen++; 1240 pearl_loopgen(w, h, grid_out, rs, g); 1241 1242#ifdef GENERATION_DIAGNOSTICS 1243 printf("grid array:\n"); 1244 for (y = 0; y < h; y++) { 1245 for (x = 0; x < w; x++) { 1246 int type = grid_out[y*w+x]; 1247 char s[5], *p = s; 1248 if (type & L) *p++ = 'L'; 1249 if (type & R) *p++ = 'R'; 1250 if (type & U) *p++ = 'U'; 1251 if (type & D) *p++ = 'D'; 1252 *p = '\0'; 1253 printf("%2s ", s); 1254 } 1255 printf("\n"); 1256 } 1257 printf("\n"); 1258#endif 1259 1260 /* 1261 * Set up the maximal clue array. 1262 */ 1263 for (y = 0; y < h; y++) 1264 for (x = 0; x < w; x++) { 1265 int type = grid_out[y*w+x]; 1266 1267 clues[y*w+x] = NOCLUE; 1268 1269 if ((bLR|bUD) & (1 << type)) { 1270 /* 1271 * This is a straight; see if it's a viable 1272 * candidate for a straight clue. It qualifies if 1273 * at least one of the squares it connects to is a 1274 * corner. 1275 */ 1276 for (d = 1; d <= 8; d += d) if (type & d) { 1277 int xx = x + DX(d), yy = y + DY(d); 1278 assert(xx >= 0 && xx < w && yy >= 0 && yy < h); 1279 if ((bLU|bLD|bRU|bRD) & (1 << grid_out[yy*w+xx])) 1280 break; 1281 } 1282 if (d <= 8) /* we found one */ 1283 clues[y*w+x] = STRAIGHT; 1284 } else if ((bLU|bLD|bRU|bRD) & (1 << type)) { 1285 /* 1286 * This is a corner; see if it's a viable candidate 1287 * for a corner clue. It qualifies if all the 1288 * squares it connects to are straights. 1289 */ 1290 for (d = 1; d <= 8; d += d) if (type & d) { 1291 int xx = x + DX(d), yy = y + DY(d); 1292 assert(xx >= 0 && xx < w && yy >= 0 && yy < h); 1293 if (!((bLR|bUD) & (1 << grid_out[yy*w+xx]))) 1294 break; 1295 } 1296 if (d > 8) /* we didn't find a counterexample */ 1297 clues[y*w+x] = CORNER; 1298 } 1299 } 1300 1301#ifdef GENERATION_DIAGNOSTICS 1302 printf("clue array:\n"); 1303 for (y = 0; y < h; y++) { 1304 for (x = 0; x < w; x++) { 1305 printf("%c", " *O"[(unsigned char)clues[y*w+x]]); 1306 } 1307 printf("\n"); 1308 } 1309 printf("\n"); 1310#endif 1311 1312 if (!params->nosolve) { 1313 int *cluespace, *straights, *corners; 1314 int nstraights, ncorners, nstraightpos, ncornerpos; 1315 1316 /* 1317 * See if we can solve the puzzle just like this. 1318 */ 1319 ret = pearl_solve(w, h, clues, grid_out, diff, false); 1320 assert(ret > 0); /* shouldn't be inconsistent! */ 1321 if (ret != 1) 1322 continue; /* go round and try again */ 1323 1324 /* 1325 * Check this puzzle isn't too easy. 1326 */ 1327 if (diff > DIFF_EASY) { 1328 ret = pearl_solve(w, h, clues, grid_out, diff-1, false); 1329 assert(ret > 0); 1330 if (ret == 1) 1331 continue; /* too easy: try again */ 1332 } 1333 1334 /* 1335 * Now shuffle the grid points and gradually remove the 1336 * clues to find a minimal set which still leaves the 1337 * puzzle soluble. 1338 * 1339 * We preferentially attempt to remove whichever type of 1340 * clue is currently most numerous, to combat a general 1341 * tendency of plain random generation to bias in favour 1342 * of many white clues and few black. 1343 * 1344 * 'nstraights' and 'ncorners' count the number of clues 1345 * of each type currently remaining in the grid; 1346 * 'nstraightpos' and 'ncornerpos' count the clues of each 1347 * type we have left to try to remove. (Clues which we 1348 * have tried and failed to remove are counted by the 1349 * former but not the latter.) 1350 */ 1351 cluespace = snewn(w*h, int); 1352 straights = cluespace; 1353 nstraightpos = 0; 1354 for (i = 0; i < w*h; i++) 1355 if (clues[i] == STRAIGHT) 1356 straights[nstraightpos++] = i; 1357 corners = straights + nstraightpos; 1358 ncornerpos = 0; 1359 for (i = 0; i < w*h; i++) 1360 if (clues[i] == STRAIGHT) 1361 corners[ncornerpos++] = i; 1362 nstraights = nstraightpos; 1363 ncorners = ncornerpos; 1364 1365 shuffle(straights, nstraightpos, sizeof(*straights), rs); 1366 shuffle(corners, ncornerpos, sizeof(*corners), rs); 1367 while (nstraightpos > 0 || ncornerpos > 0) { 1368 int cluepos; 1369 int clue; 1370 1371 /* 1372 * Decide which clue to try to remove next. If both 1373 * types are available, we choose whichever kind is 1374 * currently overrepresented; otherwise we take 1375 * whatever we can get. 1376 */ 1377 if (nstraightpos > 0 && ncornerpos > 0) { 1378 if (nstraights >= ncorners) 1379 cluepos = straights[--nstraightpos]; 1380 else 1381 cluepos = straights[--ncornerpos]; 1382 } else { 1383 if (nstraightpos > 0) 1384 cluepos = straights[--nstraightpos]; 1385 else 1386 cluepos = straights[--ncornerpos]; 1387 } 1388 1389 y = cluepos / w; 1390 x = cluepos % w; 1391 1392 clue = clues[y*w+x]; 1393 clues[y*w+x] = 0; /* try removing this clue */ 1394 1395 ret = pearl_solve(w, h, clues, grid_out, diff, false); 1396 assert(ret > 0); 1397 if (ret != 1) 1398 clues[y*w+x] = clue; /* oops, put it back again */ 1399 } 1400 sfree(cluespace); 1401 } 1402 1403#ifdef FINISHED_PUZZLE 1404 printf("clue array:\n"); 1405 for (y = 0; y < h; y++) { 1406 for (x = 0; x < w; x++) { 1407 printf("%c", " *O"[(unsigned char)clues[y*w+x]]); 1408 } 1409 printf("\n"); 1410 } 1411 printf("\n"); 1412#endif 1413 1414 break; /* got it */ 1415 } 1416 grid_free(g); 1417 1418 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h)); 1419 1420 return ngen; 1421} 1422 1423static char *new_game_desc(const game_params *params, random_state *rs, 1424 char **aux, bool interactive) 1425{ 1426 char *grid, *clues; 1427 char *desc; 1428 int w = params->w, h = params->h, i, j; 1429 1430 grid = snewn(w*h, char); 1431 clues = snewn(w*h, char); 1432 1433 new_clues(params, rs, clues, grid); 1434 1435 desc = snewn(w * h + 1, char); 1436 for (i = j = 0; i < w*h; i++) { 1437 if (clues[i] == NOCLUE && j > 0 && 1438 desc[j-1] >= 'a' && desc[j-1] < 'z') 1439 desc[j-1]++; 1440 else if (clues[i] == NOCLUE) 1441 desc[j++] = 'a'; 1442 else if (clues[i] == CORNER) 1443 desc[j++] = 'B'; 1444 else if (clues[i] == STRAIGHT) 1445 desc[j++] = 'W'; 1446 } 1447 desc[j] = '\0'; 1448 1449 *aux = snewn(w*h+1, char); 1450 for (i = 0; i < w*h; i++) 1451 (*aux)[i] = (grid[i] < 10) ? (grid[i] + '0') : (grid[i] + 'A' - 10); 1452 (*aux)[w*h] = '\0'; 1453 1454 sfree(grid); 1455 sfree(clues); 1456 1457 return desc; 1458} 1459 1460static const char *validate_desc(const game_params *params, const char *desc) 1461{ 1462 int i, sizesofar; 1463 const int totalsize = params->w * params->h; 1464 1465 sizesofar = 0; 1466 for (i = 0; desc[i]; i++) { 1467 if (desc[i] >= 'a' && desc[i] <= 'z') 1468 sizesofar += desc[i] - 'a' + 1; 1469 else if (desc[i] == 'B' || desc[i] == 'W') 1470 sizesofar++; 1471 else 1472 return "unrecognised character in string"; 1473 } 1474 1475 if (sizesofar > totalsize) 1476 return "string too long"; 1477 else if (sizesofar < totalsize) 1478 return "string too short"; 1479 1480 return NULL; 1481} 1482 1483static void clear_solution(game_state *state) 1484{ 1485 int i, sz = state->shared->w * state->shared->h; 1486 for (i = 0; i < sz; i++) 1487 state->lines[i] = state->errors[i] = state->marks[i] = BLANK; 1488} 1489 1490static game_state *new_game(midend *me, const game_params *params, 1491 const char *desc) 1492{ 1493 game_state *state = snew(game_state); 1494 int i, j, sz = params->w*params->h; 1495 1496 state->completed = false; 1497 state->used_solve = false; 1498 state->shared = snew(struct shared_state); 1499 1500 state->shared->w = params->w; 1501 state->shared->h = params->h; 1502 state->shared->sz = sz; 1503 state->shared->refcnt = 1; 1504 state->shared->clues = snewn(sz, char); 1505 for (i = j = 0; desc[i]; i++) { 1506 assert(j < sz); 1507 if (desc[i] >= 'a' && desc[i] <= 'z') { 1508 int n = desc[i] - 'a' + 1; 1509 assert(j + n <= sz); 1510 while (n-- > 0) 1511 state->shared->clues[j++] = NOCLUE; 1512 } else if (desc[i] == 'B') { 1513 state->shared->clues[j++] = CORNER; 1514 } else if (desc[i] == 'W') { 1515 state->shared->clues[j++] = STRAIGHT; 1516 } 1517 } 1518 1519 state->lines = snewn(sz, char); 1520 state->errors = snewn(sz, char); 1521 state->marks = snewn(sz, char); 1522 clear_solution(state); 1523 1524 return state; 1525} 1526 1527static game_state *dup_game(const game_state *state) 1528{ 1529 game_state *ret = snew(game_state); 1530 int sz = state->shared->sz, i; 1531 1532 ret->shared = state->shared; 1533 ret->completed = state->completed; 1534 ret->used_solve = state->used_solve; 1535 ++ret->shared->refcnt; 1536 1537 ret->lines = snewn(sz, char); 1538 ret->errors = snewn(sz, char); 1539 ret->marks = snewn(sz, char); 1540 for (i = 0; i < sz; i++) { 1541 ret->lines[i] = state->lines[i]; 1542 ret->errors[i] = state->errors[i]; 1543 ret->marks[i] = state->marks[i]; 1544 } 1545 1546 return ret; 1547} 1548 1549static void free_game(game_state *state) 1550{ 1551 assert(state); 1552 if (--state->shared->refcnt == 0) { 1553 sfree(state->shared->clues); 1554 sfree(state->shared); 1555 } 1556 sfree(state->lines); 1557 sfree(state->errors); 1558 sfree(state->marks); 1559 sfree(state); 1560} 1561 1562static char nbits[16] = { 0, 1, 1, 2, 1563 1, 2, 2, 3, 1564 1, 2, 2, 3, 1565 2, 3, 3, 4 }; 1566#define NBITS(l) ( ((l) < 0 || (l) > 15) ? 4 : nbits[l] ) 1567 1568#define ERROR_CLUE 16 1569 1570/* Returns false if the state is invalid. */ 1571static bool dsf_update_completion(game_state *state, int ax, int ay, char dir, 1572 DSF *dsf) 1573{ 1574 int w = state->shared->w /*, h = state->shared->h */; 1575 int ac = ay*w+ax, bx, by, bc; 1576 1577 if (!(state->lines[ac] & dir)) return true; /* no link */ 1578 bx = ax + DX(dir); by = ay + DY(dir); 1579 1580 if (!INGRID(state, bx, by)) 1581 return false; /* should not have a link off grid */ 1582 1583 bc = by*w+bx; 1584 if (!(state->lines[bc] & F(dir))) 1585 return false; /* should have reciprocal link */ 1586 if (!(state->lines[bc] & F(dir))) return true; 1587 1588 dsf_merge(dsf, ac, bc); 1589 return true; 1590} 1591 1592/* Returns false if the state is invalid. */ 1593static bool check_completion(game_state *state, bool mark) 1594{ 1595 int w = state->shared->w, h = state->shared->h, x, y, i, d; 1596 bool had_error = false; 1597 DSF *dsf; 1598 int *component_state; 1599 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize; 1600 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY }; 1601 1602 if (mark) { 1603 for (i = 0; i < w*h; i++) { 1604 state->errors[i] = 0; 1605 } 1606 } 1607 1608#define ERROR(x,y,e) do { had_error = true; if (mark) state->errors[(y)*w+(x)] |= (e); } while(0) 1609 1610 /* 1611 * Analyse the solution into loops, paths and stranger things. 1612 * Basic strategy here is all the same as in Loopy - see the big 1613 * comment in loopy.c's check_completion() - and for exactly the 1614 * same reasons, since Loopy and Pearl have basically the same 1615 * form of expected solution. 1616 */ 1617 dsf = dsf_new(w*h); 1618 1619 /* Build the dsf. */ 1620 for (x = 0; x < w; x++) { 1621 for (y = 0; y < h; y++) { 1622 if (!dsf_update_completion(state, x, y, R, dsf) || 1623 !dsf_update_completion(state, x, y, D, dsf)) { 1624 dsf_free(dsf); 1625 return false; 1626 } 1627 } 1628 } 1629 1630 /* Initialise a state variable for each connected component. */ 1631 component_state = snewn(w*h, int); 1632 for (i = 0; i < w*h; i++) { 1633 if (dsf_canonify(dsf, i) == i) 1634 component_state[i] = COMP_LOOP; 1635 else 1636 component_state[i] = COMP_NONE; 1637 } 1638 1639 /* 1640 * Classify components, and mark errors where a square has more 1641 * than two line segments. 1642 */ 1643 for (x = 0; x < w; x++) { 1644 for (y = 0; y < h; y++) { 1645 int type = state->lines[y*w+x]; 1646 int degree = NBITS(type); 1647 int comp = dsf_canonify(dsf, y*w+x); 1648 if (degree > 2) { 1649 ERROR(x,y,type); 1650 component_state[comp] = COMP_SILLY; 1651 } else if (degree == 0) { 1652 component_state[comp] = COMP_EMPTY; 1653 } else if (degree == 1) { 1654 if (component_state[comp] != COMP_SILLY) 1655 component_state[comp] = COMP_PATH; 1656 } 1657 } 1658 } 1659 1660 /* Count the components, and find the largest sensible one. */ 1661 nsilly = nloop = npath = 0; 1662 total_pathsize = 0; 1663 largest_comp = largest_size = -1; 1664 for (i = 0; i < w*h; i++) { 1665 if (component_state[i] == COMP_SILLY) { 1666 nsilly++; 1667 } else if (component_state[i] == COMP_PATH) { 1668 total_pathsize += dsf_size(dsf, i); 1669 npath = 1; 1670 } else if (component_state[i] == COMP_LOOP) { 1671 int this_size; 1672 1673 nloop++; 1674 1675 if ((this_size = dsf_size(dsf, i)) > largest_size) { 1676 largest_comp = i; 1677 largest_size = this_size; 1678 } 1679 } 1680 } 1681 if (largest_size < total_pathsize) { 1682 largest_comp = -1; /* means the paths */ 1683 largest_size = total_pathsize; 1684 } 1685 1686 if (nloop > 0 && nloop + npath > 1) { 1687 /* 1688 * If there are at least two sensible components including at 1689 * least one loop, highlight every sensible component that is 1690 * not the largest one. 1691 */ 1692 for (i = 0; i < w*h; i++) { 1693 int comp = dsf_canonify(dsf, i); 1694 if ((component_state[comp] == COMP_PATH && 1695 -1 != largest_comp) || 1696 (component_state[comp] == COMP_LOOP && 1697 comp != largest_comp)) 1698 ERROR(i%w, i/w, state->lines[i]); 1699 } 1700 } 1701 1702 /* Now we've finished with the dsf and component states. The only 1703 * thing we'll need to remember later on is whether all edges were 1704 * part of a single loop, for which our counter variables 1705 * nsilly,nloop,npath are enough. */ 1706 sfree(component_state); 1707 dsf_free(dsf); 1708 1709 /* 1710 * Check that no clues are contradicted. This code is similar to 1711 * the code that sets up the maximal clue array for any given 1712 * loop. 1713 */ 1714 for (x = 0; x < w; x++) { 1715 for (y = 0; y < h; y++) { 1716 int type = state->lines[y*w+x]; 1717 if (state->shared->clues[y*w+x] == CORNER) { 1718 /* Supposed to be a corner: will find a contradiction if 1719 * it actually contains a straight line, or if it touches any 1720 * corners. */ 1721 if ((bLR|bUD) & (1 << type)) { 1722 ERROR(x,y,ERROR_CLUE); /* actually straight */ 1723 } 1724 for (d = 1; d <= 8; d += d) if (type & d) { 1725 int xx = x + DX(d), yy = y + DY(d); 1726 if (!INGRID(state, xx, yy)) { 1727 ERROR(x,y,d); /* leads off grid */ 1728 } else { 1729 if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) { 1730 ERROR(x,y,ERROR_CLUE); /* touches corner */ 1731 } 1732 } 1733 } 1734 } else if (state->shared->clues[y*w+x] == STRAIGHT) { 1735 /* Supposed to be straight: will find a contradiction if 1736 * it actually contains a corner, or if it only touches 1737 * straight lines. */ 1738 if ((bLU|bLD|bRU|bRD) & (1 << type)) { 1739 ERROR(x,y,ERROR_CLUE); /* actually a corner */ 1740 } 1741 i = 0; 1742 for (d = 1; d <= 8; d += d) if (type & d) { 1743 int xx = x + DX(d), yy = y + DY(d); 1744 if (!INGRID(state, xx, yy)) { 1745 ERROR(x,y,d); /* leads off grid */ 1746 } else { 1747 if ((bLR|bUD) & (1 << state->lines[yy*w+xx])) 1748 i++; /* a straight */ 1749 } 1750 } 1751 if (i >= 2 && NBITS(type) >= 2) { 1752 ERROR(x,y,ERROR_CLUE); /* everything touched is straight */ 1753 } 1754 } 1755 } 1756 } 1757 1758 if (nloop == 1 && nsilly == 0 && npath == 0) { 1759 /* 1760 * If there's exactly one loop (so that the puzzle is at least 1761 * potentially complete), we need to ensure it hasn't left any 1762 * clue out completely. 1763 */ 1764 for (x = 0; x < w; x++) { 1765 for (y = 0; y < h; y++) { 1766 if (state->lines[y*w+x] == BLANK) { 1767 if (state->shared->clues[y*w+x] != NOCLUE) { 1768 /* the loop doesn't include this clue square! */ 1769 ERROR(x, y, ERROR_CLUE); 1770 } 1771 } 1772 } 1773 } 1774 1775 /* 1776 * But if not, then we're done! 1777 */ 1778 if (!had_error) 1779 state->completed = true; 1780 } 1781 return true; 1782} 1783 1784/* completion check: 1785 * 1786 * - no clues must be contradicted (highlight clue itself in error if so) 1787 * - if there is a closed loop it must include every line segment laid 1788 * - if there's a smaller closed loop then highlight whole loop as error 1789 * - no square must have more than 2 lines radiating from centre point 1790 * (highlight all lines in that square as error if so) 1791 */ 1792 1793static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines) 1794{ 1795 int w = state->shared->w, h = state->shared->h, i; 1796 char *move = snewn(w*h*40, char), *p = move; 1797 1798 *p++ = 'S'; 1799 for (i = 0; i < w*h; i++) { 1800 if (old_lines[i] != new_lines[i]) { 1801 p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w); 1802 } 1803 } 1804 *p++ = '\0'; 1805 move = sresize(move, p - move, char); 1806 1807 return move; 1808} 1809 1810static char *solve_game(const game_state *state, const game_state *currstate, 1811 const char *aux, const char **error) 1812{ 1813 game_state *solved = dup_game(state); 1814 int i, ret, sz = state->shared->sz; 1815 char *move; 1816 1817 if (aux) { 1818 for (i = 0; i < sz; i++) { 1819 if (aux[i] >= '0' && aux[i] <= '9') 1820 solved->lines[i] = aux[i] - '0'; 1821 else if (aux[i] >= 'A' && aux[i] <= 'F') 1822 solved->lines[i] = aux[i] - 'A' + 10; 1823 else { 1824 *error = "invalid char in aux"; 1825 move = NULL; 1826 goto done; 1827 } 1828 } 1829 ret = 1; 1830 } else { 1831 /* Try to solve with present (half-solved) state first: if there's no 1832 * solution from there go back to original state. */ 1833 ret = pearl_solve(currstate->shared->w, currstate->shared->h, 1834 currstate->shared->clues, solved->lines, 1835 DIFFCOUNT, false); 1836 if (ret < 1) 1837 ret = pearl_solve(state->shared->w, state->shared->h, 1838 state->shared->clues, solved->lines, 1839 DIFFCOUNT, false); 1840 1841 } 1842 1843 if (ret < 1) { 1844 *error = "Unable to find solution"; 1845 move = NULL; 1846 } else { 1847 move = solve_for_diff(solved, currstate->lines, solved->lines); 1848 } 1849 1850done: 1851 free_game(solved); 1852 return move; 1853} 1854 1855static bool game_can_format_as_text_now(const game_params *params) 1856{ 1857 return true; 1858} 1859 1860static char *game_text_format(const game_state *state) 1861{ 1862 int w = state->shared->w, h = state->shared->h, cw = 4, ch = 2; 1863 int gw = cw*(w-1) + 2, gh = ch*(h-1) + 1, len = gw * gh, r, c, j; 1864 char *board = snewn(len + 1, char); 1865 1866 assert(board); 1867 memset(board, ' ', len); 1868 1869 for (r = 0; r < h; ++r) { 1870 for (c = 0; c < w; ++c) { 1871 int i = r*w + c, cell = r*ch*gw + c*cw; 1872 board[cell] = "+BW"[(unsigned char)state->shared->clues[i]]; 1873 if (c < w - 1 && (state->lines[i] & R || state->lines[i+1] & L)) 1874 memset(board + cell + 1, '-', cw - 1); 1875 if (r < h - 1 && (state->lines[i] & D || state->lines[i+w] & U)) 1876 for (j = 1; j < ch; ++j) board[cell + j*gw] = '|'; 1877 if (c < w - 1 && (state->marks[i] & R || state->marks[i+1] & L)) 1878 board[cell + cw/2] = 'x'; 1879 if (r < h - 1 && (state->marks[i] & D || state->marks[i+w] & U)) 1880 board[cell + (ch/2 * gw)] = 'x'; 1881 } 1882 1883 for (j = 0; j < (r == h - 1 ? 1 : ch); ++j) 1884 board[r*ch*gw + (gw - 1) + j*gw] = '\n'; 1885 } 1886 1887 board[len] = '\0'; 1888 return board; 1889} 1890 1891struct game_ui { 1892 int *dragcoords; /* list of (y*w+x) coords in drag so far */ 1893 int ndragcoords; /* number of entries in dragcoords. 1894 * 0 = click but no drag yet. -1 = no drag at all */ 1895 int clickx, clicky; /* pixel position of initial click */ 1896 1897 int curx, cury; /* grid position of keyboard cursor */ 1898 bool cursor_active; /* true iff cursor is shown */ 1899 1900 /* 1901 * User preference: general visual style of the GUI. GUI_MASYU is 1902 * how this puzzle is traditionally presented, with clue dots in 1903 * the middle of grid squares, and the solution loop connecting 1904 * square-centres. GUI_LOOPY shifts the grid by half a square in 1905 * each direction, so that the clue dots are at _vertices_ of the 1906 * grid and the solution loop follows the grid edges, which you 1907 * could argue is more logical. 1908 */ 1909 enum { GUI_MASYU, GUI_LOOPY } gui_style; 1910}; 1911 1912static void legacy_prefs_override(struct game_ui *ui_out) 1913{ 1914 static bool initialised = false; 1915 static int gui_style = -1; 1916 1917 if (!initialised) { 1918 initialised = true; 1919 1920 switch (getenv_bool("PEARL_GUI_LOOPY", -1)) { 1921 case 0: 1922 gui_style = GUI_MASYU; 1923 break; 1924 case 1: 1925 gui_style = GUI_LOOPY; 1926 break; 1927 } 1928 } 1929 1930 if (gui_style != -1) 1931 ui_out->gui_style = gui_style; 1932} 1933 1934static game_ui *new_ui(const game_state *state) 1935{ 1936 game_ui *ui = snew(game_ui); 1937 1938 ui->ndragcoords = -1; 1939 ui->dragcoords = state ? snewn(state->shared->sz, int) : NULL; 1940 ui->cursor_active = getenv_bool("PUZZLES_SHOW_CURSOR", false); 1941 ui->curx = ui->cury = 0; 1942 1943 ui->gui_style = GUI_MASYU; 1944 legacy_prefs_override(ui); 1945 1946 return ui; 1947} 1948 1949static void free_ui(game_ui *ui) 1950{ 1951 sfree(ui->dragcoords); 1952 sfree(ui); 1953} 1954 1955static config_item *get_prefs(game_ui *ui) 1956{ 1957 config_item *ret; 1958 1959 ret = snewn(N_PREF_ITEMS+1, config_item); 1960 1961 ret[PREF_APPEARANCE].name = "Puzzle appearance"; 1962 ret[PREF_APPEARANCE].kw = "appearance"; 1963 ret[PREF_APPEARANCE].type = C_CHOICES; 1964 ret[PREF_APPEARANCE].u.choices.choicenames = ":Traditional:Loopy-style"; 1965 ret[PREF_APPEARANCE].u.choices.choicekws = ":traditional:loopy"; 1966 ret[PREF_APPEARANCE].u.choices.selected = ui->gui_style; 1967 1968 ret[N_PREF_ITEMS].name = NULL; 1969 ret[N_PREF_ITEMS].type = C_END; 1970 1971 return ret; 1972} 1973 1974static void set_prefs(game_ui *ui, const config_item *cfg) 1975{ 1976 ui->gui_style = cfg[PREF_APPEARANCE].u.choices.selected; 1977} 1978 1979static void game_changed_state(game_ui *ui, const game_state *oldstate, 1980 const game_state *newstate) 1981{ 1982} 1983 1984static const char *current_key_label(const game_ui *ui, 1985 const game_state *state, int button) 1986{ 1987 if (IS_CURSOR_SELECT(button) && ui->cursor_active) { 1988 if (button == CURSOR_SELECT) { 1989 if (ui->ndragcoords == -1) return "Start"; 1990 return "Stop"; 1991 } 1992 if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) 1993 return "Cancel"; 1994 } 1995 return ""; 1996} 1997 1998#define PREFERRED_TILE_SIZE 31 1999#define HALFSZ (ds->halfsz) 2000#define TILE_SIZE (ds->halfsz*2 + 1) 2001 2002#define BORDER ((ui->gui_style == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2)) 2003 2004#define BORDER_WIDTH (max(TILE_SIZE / 32, 1)) 2005 2006#define COORD(x) ( (x) * TILE_SIZE + BORDER ) 2007#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 ) 2008#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) ) 2009 2010#define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */ 2011#define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */ 2012#define DS_MSHIFT 12 /* shift for no-line mark */ 2013 2014#define DS_ERROR_CLUE (1 << 20) 2015#define DS_FLASH (1 << 21) 2016#define DS_CURSOR (1 << 22) 2017 2018struct game_drawstate { 2019 int halfsz; 2020 bool started; 2021 2022 int w, h, sz; 2023 unsigned int *lflags; /* size w*h */ 2024 2025 char *draglines; /* size w*h; lines flipped by current drag */ 2026}; 2027 2028/* 2029 * Routine shared between multiple callers to work out the intended 2030 * effect of a drag path on the grid. 2031 * 2032 * Call it in a loop, like this: 2033 * 2034 * bool clearing = true; 2035 * for (i = 0; i < ui->ndragcoords - 1; i++) { 2036 * int sx, sy, dx, dy, dir, oldstate, newstate; 2037 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, 2038 * &dir, &oldstate, &newstate); 2039 * 2040 * [do whatever is needed to handle the fact that the drag 2041 * wants the edge from sx,sy to dx,dy (heading in direction 2042 * 'dir' at the sx,sy end) to be changed from state oldstate 2043 * to state newstate, each of which equals either 0 or dir] 2044 * } 2045 */ 2046static void interpret_ui_drag(const game_state *state, const game_ui *ui, 2047 bool *clearing, int i, int *sx, int *sy, 2048 int *dx, int *dy, int *dir, 2049 int *oldstate, int *newstate) 2050{ 2051 int w = state->shared->w; 2052 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1]; 2053 *sy = sp/w; 2054 *sx = sp%w; 2055 *dy = dp/w; 2056 *dx = dp%w; 2057 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L); 2058 *oldstate = state->lines[sp] & *dir; 2059 if (*oldstate) { 2060 /* 2061 * The edge we've dragged over was previously 2062 * present. Set it to absent, unless we've already 2063 * stopped doing that. 2064 */ 2065 *newstate = *clearing ? 0 : *dir; 2066 } else { 2067 /* 2068 * The edge we've dragged over was previously 2069 * absent. Set it to present, and cancel the 2070 * 'clearing' flag so that all subsequent edges in 2071 * the drag are set rather than cleared. 2072 */ 2073 *newstate = *dir; 2074 *clearing = false; 2075 } 2076} 2077 2078static void update_ui_drag(const game_state *state, game_ui *ui, 2079 int gx, int gy) 2080{ 2081 int /* sz = state->shared->sz, */ w = state->shared->w; 2082 int i, ox, oy, pos; 2083 int lastpos; 2084 2085 if (!INGRID(state, gx, gy)) 2086 return; /* square is outside grid */ 2087 2088 if (ui->ndragcoords < 0) 2089 return; /* drag not in progress anyway */ 2090 2091 pos = gy * w + gx; 2092 2093 lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0]; 2094 if (pos == lastpos) 2095 return; /* same square as last visited one */ 2096 2097 /* Drag confirmed, if it wasn't already. */ 2098 if (ui->ndragcoords == 0) 2099 ui->ndragcoords = 1; 2100 2101 /* 2102 * Dragging the mouse into a square that's already been visited by 2103 * the drag path so far has the effect of truncating the path back 2104 * to that square, so a player can back out part of an uncommitted 2105 * drag without having to let go of the mouse. 2106 * 2107 * An exception is that you're allowed to drag round in a loop 2108 * back to the very start of the drag, provided that doesn't 2109 * create a vertex of the wrong degree. This allows a player who's 2110 * after an extra challenge to draw the entire loop in a single 2111 * drag, without it cancelling itself just before release. 2112 */ 2113 for (i = 1; i < ui->ndragcoords; i++) 2114 if (pos == ui->dragcoords[i]) { 2115 ui->ndragcoords = i+1; 2116 return; 2117 } 2118 2119 if (pos == ui->dragcoords[0]) { 2120 /* More complex check for a loop-shaped drag, which has to go 2121 * through interpret_ui_drag to decide on the final degree of 2122 * the start/end vertex. */ 2123 ui->dragcoords[ui->ndragcoords] = pos; 2124 bool clearing = true; 2125 int lines = state->lines[pos] & (L|R|U|D); 2126 for (i = 0; i < ui->ndragcoords; i++) { 2127 int sx, sy, dx, dy, dir, oldstate, newstate; 2128 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, 2129 &dir, &oldstate, &newstate); 2130 if (sx == gx && sy == gy) 2131 lines ^= (oldstate ^ newstate); 2132 if (dx == gx && dy == gy) 2133 lines ^= (F(oldstate) ^ F(newstate)); 2134 } 2135 if (NBITS(lines) > 2) { 2136 /* Bad vertex degree: fall back to the backtracking behaviour. */ 2137 ui->ndragcoords = 1; 2138 return; 2139 } 2140 } 2141 2142 /* 2143 * Otherwise, dragging the mouse into a square that's a rook-move 2144 * away from the last one on the path extends the path. 2145 */ 2146 oy = ui->dragcoords[ui->ndragcoords-1] / w; 2147 ox = ui->dragcoords[ui->ndragcoords-1] % w; 2148 if (ox == gx || oy == gy) { 2149 int dx = (gx < ox ? -1 : gx > ox ? +1 : 0); 2150 int dy = (gy < oy ? -1 : gy > oy ? +1 : 0); 2151 int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L); 2152 while (ox != gx || oy != gy) { 2153 /* 2154 * If the drag attempts to cross a 'no line here' mark, 2155 * stop there. We physically don't allow the user to drag 2156 * over those marks. 2157 */ 2158 if (state->marks[oy*w+ox] & dir) 2159 break; 2160 ox += dx; 2161 oy += dy; 2162 ui->dragcoords[ui->ndragcoords++] = oy * w + ox; 2163 } 2164 } 2165 2166 /* 2167 * Failing that, we do nothing at all: if the user has dragged 2168 * diagonally across the board, they'll just have to return the 2169 * mouse to the last known position and do whatever they meant to 2170 * do again, more slowly and clearly. 2171 */ 2172} 2173 2174static char *mark_in_direction(const game_state *state, int x, int y, int dir, 2175 bool primary, char *buf) 2176{ 2177 int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */; 2178 int x2 = x + DX(dir); 2179 int y2 = y + DY(dir); 2180 int dir2 = F(dir); 2181 2182 char ch = primary ? 'F' : 'M', *other; 2183 2184 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return MOVE_UI_UPDATE; 2185 2186 /* disallow laying a mark over a line, or vice versa. */ 2187 other = primary ? state->marks : state->lines; 2188 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return MOVE_UI_UPDATE; 2189 2190 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2); 2191 return dupstr(buf); 2192} 2193 2194#define KEY_DIRECTION(btn) (\ 2195 (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\ 2196 (btn) == CURSOR_LEFT ? L : R) 2197 2198static char *interpret_move(const game_state *state, game_ui *ui, 2199 const game_drawstate *ds, 2200 int x, int y, int button) 2201{ 2202 int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */; 2203 int gx = FROMCOORD(x), gy = FROMCOORD(y), i; 2204 bool release = false; 2205 char tmpbuf[80]; 2206 2207 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 2208 button = STRIP_BUTTON_MODIFIERS(button); 2209 2210 if (IS_MOUSE_DOWN(button)) { 2211 ui->cursor_active = false; 2212 2213 if (!INGRID(state, gx, gy)) { 2214 ui->ndragcoords = -1; 2215 return MOVE_UI_UPDATE; 2216 } 2217 2218 ui->clickx = x; ui->clicky = y; 2219 ui->dragcoords[0] = gy * w + gx; 2220 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */ 2221 2222 return MOVE_UI_UPDATE; 2223 } 2224 2225 if (button == LEFT_DRAG && ui->ndragcoords >= 0) { 2226 update_ui_drag(state, ui, gx, gy); 2227 return MOVE_UI_UPDATE; 2228 } 2229 2230 if (IS_MOUSE_RELEASE(button)) release = true; 2231 2232 if (IS_CURSOR_MOVE(button)) { 2233 if (!ui->cursor_active) { 2234 ui->cursor_active = true; 2235 } else if (control || shift) { 2236 char *move; 2237 if (ui->ndragcoords > 0) return MOVE_NO_EFFECT; 2238 ui->ndragcoords = -1; 2239 move = mark_in_direction(state, ui->curx, ui->cury, 2240 KEY_DIRECTION(button), control, tmpbuf); 2241 if (control && !shift && *move) 2242 move_cursor(button, &ui->curx, &ui->cury, w, h, false, NULL); 2243 return move; 2244 } else { 2245 move_cursor(button, &ui->curx, &ui->cury, w, h, false, NULL); 2246 if (ui->ndragcoords >= 0) 2247 update_ui_drag(state, ui, ui->curx, ui->cury); 2248 } 2249 return MOVE_UI_UPDATE; 2250 } 2251 2252 if (IS_CURSOR_SELECT(button)) { 2253 if (!ui->cursor_active) { 2254 ui->cursor_active = true; 2255 return MOVE_UI_UPDATE; 2256 } else if (button == CURSOR_SELECT) { 2257 if (ui->ndragcoords == -1) { 2258 ui->ndragcoords = 0; 2259 ui->dragcoords[0] = ui->cury * w + ui->curx; 2260 ui->clickx = CENTERED_COORD(ui->curx); 2261 ui->clicky = CENTERED_COORD(ui->cury); 2262 return MOVE_UI_UPDATE; 2263 } else release = true; 2264 } else if (button == CURSOR_SELECT2) { 2265 if (ui->ndragcoords >= 0) { 2266 ui->ndragcoords = -1; 2267 return MOVE_UI_UPDATE; 2268 } 2269 return MOVE_NO_EFFECT; 2270 } 2271 } 2272 2273 if (button == 27 || button == '\b') { 2274 if (ui->ndragcoords >= 0) { 2275 ui->ndragcoords = -1; 2276 return MOVE_UI_UPDATE; 2277 } 2278 return MOVE_NO_EFFECT; 2279 } 2280 2281 if (release) { 2282 if (ui->ndragcoords > 0) { 2283 /* End of a drag: process the cached line data. */ 2284 int buflen = 0, bufsize = 256, tmplen; 2285 char *buf = NULL; 2286 const char *sep = ""; 2287 bool clearing = true; 2288 2289 for (i = 0; i < ui->ndragcoords - 1; i++) { 2290 int sx, sy, dx, dy, dir, oldstate, newstate; 2291 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, 2292 &dir, &oldstate, &newstate); 2293 2294 if (oldstate != newstate) { 2295 if (!buf) buf = snewn(bufsize, char); 2296 tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep, 2297 dir, sx, sy, F(dir), dx, dy); 2298 if (buflen + tmplen >= bufsize) { 2299 bufsize = (buflen + tmplen) * 5 / 4 + 256; 2300 buf = sresize(buf, bufsize, char); 2301 } 2302 strcpy(buf + buflen, tmpbuf); 2303 buflen += tmplen; 2304 sep = ";"; 2305 } 2306 } 2307 2308 ui->ndragcoords = -1; 2309 2310 return buf ? buf : MOVE_UI_UPDATE; 2311 } else if (ui->ndragcoords == 0) { 2312 /* Click (or tiny drag). Work out which edge we were 2313 * closest to. */ 2314 int cx, cy; 2315 2316 ui->ndragcoords = -1; 2317 2318 /* 2319 * We process clicks based on the mouse-down location, 2320 * because that's more natural for a user to carefully 2321 * control than the mouse-up. 2322 */ 2323 x = ui->clickx; 2324 y = ui->clicky; 2325 2326 gx = FROMCOORD(x); 2327 gy = FROMCOORD(y); 2328 cx = CENTERED_COORD(gx); 2329 cy = CENTERED_COORD(gy); 2330 2331 if (!INGRID(state, gx, gy)) return MOVE_UI_UPDATE; 2332 2333 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) { 2334 /* TODO closer to centre of grid: process as a cell click not an edge click. */ 2335 2336 return MOVE_UI_UPDATE; 2337 } else { 2338 int direction; 2339 if (abs(x-cx) < abs(y-cy)) { 2340 /* Closest to top/bottom edge. */ 2341 direction = (y < cy) ? U : D; 2342 } else { 2343 /* Closest to left/right edge. */ 2344 direction = (x < cx) ? L : R; 2345 } 2346 return mark_in_direction(state, gx, gy, direction, 2347 (button == LEFT_RELEASE), tmpbuf); 2348 } 2349 } 2350 } 2351 2352 if (button == 'H' || button == 'h') 2353 return dupstr("H"); 2354 2355 return MOVE_UNUSED; 2356} 2357 2358static game_state *execute_move(const game_state *state, const char *move) 2359{ 2360 int w = state->shared->w, h = state->shared->h; 2361 char c; 2362 int x, y, l, n; 2363 game_state *ret = dup_game(state); 2364 2365 debug(("move: %s\n", move)); 2366 2367 while (*move) { 2368 c = *move; 2369 if (c == 'S') { 2370 ret->used_solve = true; 2371 move++; 2372 } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') { 2373 /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */ 2374 move++; 2375 if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3) 2376 goto badmove; 2377 if (!INGRID(state, x, y)) goto badmove; 2378 if (l < 0 || l > 15) goto badmove; 2379 2380 if (c == 'L') 2381 ret->lines[y*w + x] |= (char)l; 2382 else if (c == 'N') 2383 ret->lines[y*w + x] &= ~((char)l); 2384 else if (c == 'R') { 2385 ret->lines[y*w + x] = (char)l; 2386 ret->marks[y*w + x] &= ~((char)l); /* erase marks too */ 2387 } else if (c == 'F') 2388 ret->lines[y*w + x] ^= (char)l; 2389 else if (c == 'M') 2390 ret->marks[y*w + x] ^= (char)l; 2391 2392 /* 2393 * If we ended up trying to lay a line _over_ a mark, 2394 * that's a failed move: interpret_move() should have 2395 * ensured we never received a move string like that in 2396 * the first place. 2397 */ 2398 if ((ret->lines[y*w + x] & (char)l) && 2399 (ret->marks[y*w + x] & (char)l)) 2400 goto badmove; 2401 2402 move += n; 2403 } else if (strcmp(move, "H") == 0) { 2404 pearl_solve(ret->shared->w, ret->shared->h, 2405 ret->shared->clues, ret->lines, DIFFCOUNT, true); 2406 for (n = 0; n < w*h; n++) 2407 ret->marks[n] &= ~ret->lines[n]; /* erase marks too */ 2408 move++; 2409 } else { 2410 goto badmove; 2411 } 2412 if (*move == ';') 2413 move++; 2414 else if (*move) 2415 goto badmove; 2416 } 2417 2418 if (!check_completion(ret, true)) goto badmove; 2419 2420 return ret; 2421 2422badmove: 2423 free_game(ret); 2424 return NULL; 2425} 2426 2427/* ---------------------------------------------------------------------- 2428 * Drawing routines. 2429 */ 2430 2431#define FLASH_TIME 0.5F 2432 2433static void game_compute_size(const game_params *params, int tilesize, 2434 const game_ui *ui, int *x, int *y) 2435{ 2436 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2437 struct { int halfsz; } ads, *ds = &ads; 2438 ads.halfsz = (tilesize-1)/2; 2439 2440 *x = (params->w) * TILE_SIZE + 2 * BORDER; 2441 *y = (params->h) * TILE_SIZE + 2 * BORDER; 2442} 2443 2444static void game_set_size(drawing *dr, game_drawstate *ds, 2445 const game_params *params, int tilesize) 2446{ 2447 ds->halfsz = (tilesize-1)/2; 2448} 2449 2450static float *game_colours(frontend *fe, int *ncolours) 2451{ 2452 float *ret = snewn(3 * NCOLOURS, float); 2453 int i; 2454 2455 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); 2456 2457 for (i = 0; i < 3; i++) { 2458 ret[COL_BLACK * 3 + i] = 0.0F; 2459 ret[COL_WHITE * 3 + i] = 1.0F; 2460 ret[COL_GRID * 3 + i] = 0.4F; 2461 } 2462 2463 ret[COL_ERROR * 3 + 0] = 1.0F; 2464 ret[COL_ERROR * 3 + 1] = 0.0F; 2465 ret[COL_ERROR * 3 + 2] = 0.0F; 2466 2467 ret[COL_DRAGON * 3 + 0] = 0.0F; 2468 ret[COL_DRAGON * 3 + 1] = 0.0F; 2469 ret[COL_DRAGON * 3 + 2] = 1.0F; 2470 2471 ret[COL_DRAGOFF * 3 + 0] = 0.8F; 2472 ret[COL_DRAGOFF * 3 + 1] = 0.8F; 2473 ret[COL_DRAGOFF * 3 + 2] = 1.0F; 2474 2475 ret[COL_FLASH * 3 + 0] = 1.0F; 2476 ret[COL_FLASH * 3 + 1] = 1.0F; 2477 ret[COL_FLASH * 3 + 2] = 1.0F; 2478 2479 *ncolours = NCOLOURS; 2480 2481 return ret; 2482} 2483 2484static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 2485{ 2486 struct game_drawstate *ds = snew(struct game_drawstate); 2487 int i; 2488 2489 ds->halfsz = 0; 2490 ds->started = false; 2491 2492 ds->w = state->shared->w; 2493 ds->h = state->shared->h; 2494 ds->sz = state->shared->sz; 2495 ds->lflags = snewn(ds->sz, unsigned int); 2496 for (i = 0; i < ds->sz; i++) 2497 ds->lflags[i] = 0; 2498 2499 ds->draglines = snewn(ds->sz, char); 2500 2501 return ds; 2502} 2503 2504static void game_free_drawstate(drawing *dr, game_drawstate *ds) 2505{ 2506 sfree(ds->draglines); 2507 sfree(ds->lflags); 2508 sfree(ds); 2509} 2510 2511static void draw_lines_specific(drawing *dr, game_drawstate *ds, 2512 const game_ui *ui, int x, int y, 2513 unsigned int lflags, unsigned int shift, int c) 2514{ 2515 int ox = COORD(x), oy = COORD(y); 2516 int t2 = HALFSZ, t16 = HALFSZ/4; 2517 int cx = ox + t2, cy = oy + t2; 2518 int d; 2519 2520 /* Draw each of the four directions, where laid (or error, or drag, etc.) */ 2521 for (d = 1; d < 16; d *= 2) { 2522 int xoff = t2 * DX(d), yoff = t2 * DY(d); 2523 int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d))); 2524 2525 if ((lflags >> shift) & d) { 2526 int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge; 2527 int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge; 2528 2529 if (c == COL_DRAGOFF && !(lflags & d)) 2530 continue; 2531 if (c == COL_DRAGON && (lflags & d)) 2532 continue; 2533 2534 draw_rect(dr, lx, ly, 2535 abs(xoff)+2*xnudge+1, 2536 abs(yoff)+2*ynudge+1, c); 2537 /* end cap */ 2538 draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c); 2539 } 2540 } 2541} 2542 2543static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui, 2544 int x, int y, unsigned int lflags, char clue) 2545{ 2546 int ox = COORD(x), oy = COORD(y); 2547 int t2 = HALFSZ, t16 = HALFSZ/4; 2548 int cx = ox + t2, cy = oy + t2; 2549 int d; 2550 2551 assert(dr); 2552 2553 /* Clip to the grid square. */ 2554 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE); 2555 2556 /* Clear the square. */ 2557 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, 2558 (lflags & DS_CURSOR) ? 2559 COL_CURSOR_BACKGROUND : COL_BACKGROUND); 2560 2561 2562 if (ui->gui_style == GUI_LOOPY) { 2563 /* Draw small dot, underneath any lines. */ 2564 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID); 2565 } else { 2566 /* Draw outline of grid square */ 2567 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID); 2568 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID); 2569 } 2570 2571 /* Draw grid: either thin gridlines, or no-line marks. 2572 * We draw these first because the thick laid lines should be on top. */ 2573 for (d = 1; d < 16; d *= 2) { 2574 int xoff = t2 * DX(d), yoff = t2 * DY(d); 2575 2576 if ((x == 0 && d == L) || 2577 (y == 0 && d == U) || 2578 (x == ds->w-1 && d == R) || 2579 (y == ds->h-1 && d == D)) 2580 continue; /* no gridlines out to the border. */ 2581 2582 if ((lflags >> DS_MSHIFT) & d) { 2583 /* either a no-line mark ... */ 2584 int mx = cx + xoff, my = cy + yoff, msz = t16; 2585 2586 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK); 2587 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK); 2588 } else { 2589 if (ui->gui_style == GUI_LOOPY) { 2590 /* draw grid lines connecting centre of cells */ 2591 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID); 2592 } 2593 } 2594 } 2595 2596 /* Draw each of the four directions, where laid (or error, or drag, etc.) 2597 * Order is important here, specifically for the eventual colours of the 2598 * exposed end caps. */ 2599 draw_lines_specific(dr, ds, ui, x, y, lflags, 0, 2600 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK)); 2601 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_ESHIFT, COL_ERROR); 2602 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGOFF); 2603 draw_lines_specific(dr, ds, ui, x, y, lflags, DS_DSHIFT, COL_DRAGON); 2604 2605 /* Draw a clue, if present */ 2606 if (clue != NOCLUE) { 2607 int c = (lflags & DS_FLASH) ? COL_FLASH : 2608 (clue == STRAIGHT) ? COL_WHITE : COL_BLACK; 2609 2610 if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */ 2611 draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR); 2612 2613 draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK); 2614 } 2615 2616 unclip(dr); 2617 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE); 2618} 2619 2620static void game_redraw(drawing *dr, game_drawstate *ds, 2621 const game_state *oldstate, const game_state *state, 2622 int dir, const game_ui *ui, 2623 float animtime, float flashtime) 2624{ 2625 int w = state->shared->w, h = state->shared->h, sz = state->shared->sz; 2626 int x, y, flashing = 0; 2627 bool force = false; 2628 2629 if (!ds->started) { 2630 if (ui->gui_style == GUI_MASYU) { 2631 /* 2632 * Black rectangle which is the main grid. 2633 */ 2634 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, 2635 w*TILE_SIZE + 2*BORDER_WIDTH + 1, 2636 h*TILE_SIZE + 2*BORDER_WIDTH + 1, 2637 COL_GRID); 2638 } 2639 2640 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER); 2641 2642 ds->started = true; 2643 force = true; 2644 } 2645 2646 if (flashtime > 0 && 2647 (flashtime <= FLASH_TIME/3 || 2648 flashtime >= FLASH_TIME*2/3)) 2649 flashing = DS_FLASH; 2650 2651 memset(ds->draglines, 0, sz); 2652 if (ui->ndragcoords > 0) { 2653 int i; 2654 bool clearing = true; 2655 for (i = 0; i < ui->ndragcoords - 1; i++) { 2656 int sx, sy, dx, dy, dir, oldstate, newstate; 2657 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy, 2658 &dir, &oldstate, &newstate); 2659 ds->draglines[sy*w+sx] ^= (oldstate ^ newstate); 2660 ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate)); 2661 } 2662 } 2663 2664 for (x = 0; x < w; x++) { 2665 for (y = 0; y < h; y++) { 2666 unsigned int f = (unsigned int)state->lines[y*w+x]; 2667 unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D)); 2668 2669 f |= eline << DS_ESHIFT; 2670 f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT; 2671 f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT; 2672 2673 if (state->errors[y*w+x] & ERROR_CLUE) 2674 f |= DS_ERROR_CLUE; 2675 2676 f |= flashing; 2677 2678 if (ui->cursor_active && x == ui->curx && y == ui->cury) 2679 f |= DS_CURSOR; 2680 2681 if (f != ds->lflags[y*w+x] || force) { 2682 ds->lflags[y*w+x] = f; 2683 draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]); 2684 } 2685 } 2686 } 2687} 2688 2689static float game_anim_length(const game_state *oldstate, 2690 const game_state *newstate, int dir, game_ui *ui) 2691{ 2692 return 0.0F; 2693} 2694 2695static float game_flash_length(const game_state *oldstate, 2696 const game_state *newstate, int dir, game_ui *ui) 2697{ 2698 if (!oldstate->completed && newstate->completed && 2699 !oldstate->used_solve && !newstate->used_solve) 2700 return FLASH_TIME; 2701 else 2702 return 0.0F; 2703} 2704 2705static void game_get_cursor_location(const game_ui *ui, 2706 const game_drawstate *ds, 2707 const game_state *state, 2708 const game_params *params, 2709 int *x, int *y, int *w, int *h) 2710{ 2711 if(ui->cursor_active) { 2712 *x = COORD(ui->curx); 2713 *y = COORD(ui->cury); 2714 *w = *h = TILE_SIZE; 2715 } 2716} 2717 2718static int game_status(const game_state *state) 2719{ 2720 return state->completed ? +1 : 0; 2721} 2722 2723static void game_print_size(const game_params *params, const game_ui *ui, 2724 float *x, float *y) 2725{ 2726 int pw, ph; 2727 2728 /* 2729 * I'll use 6mm squares by default. 2730 */ 2731 game_compute_size(params, 600, ui, &pw, &ph); 2732 *x = pw / 100.0F; 2733 *y = ph / 100.0F; 2734} 2735 2736static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 2737 int tilesize) 2738{ 2739 int w = state->shared->w, h = state->shared->h, x, y; 2740 int black = print_mono_colour(dr, 0); 2741 int white = print_mono_colour(dr, 1); 2742 2743 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2744 game_drawstate *ds = game_new_drawstate(dr, state); 2745 game_set_size(dr, ds, NULL, tilesize); 2746 2747 if (ui->gui_style == GUI_MASYU) { 2748 /* Draw grid outlines (black). */ 2749 for (x = 0; x <= w; x++) 2750 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black); 2751 for (y = 0; y <= h; y++) 2752 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black); 2753 } else { 2754 /* Draw small dots, and dotted lines connecting them. For 2755 * added clarity, try to start and end the dotted lines a 2756 * little way away from the dots. */ 2757 print_line_width(dr, TILE_SIZE / 40); 2758 print_line_dotted(dr, true); 2759 for (x = 0; x < w; x++) { 2760 for (y = 0; y < h; y++) { 2761 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ; 2762 draw_circle(dr, cx, cy, tilesize/10, black, black); 2763 if (x+1 < w) 2764 draw_line(dr, cx+tilesize/5, cy, 2765 cx+tilesize-tilesize/5, cy, black); 2766 if (y+1 < h) 2767 draw_line(dr, cx, cy+tilesize/5, 2768 cx, cy+tilesize-tilesize/5, black); 2769 } 2770 } 2771 print_line_dotted(dr, false); 2772 } 2773 2774 for (x = 0; x < w; x++) { 2775 for (y = 0; y < h; y++) { 2776 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ; 2777 int clue = state->shared->clues[y*w+x]; 2778 2779 draw_lines_specific(dr, ds, ui, x, y, 2780 state->lines[y*w+x], 0, black); 2781 2782 if (clue != NOCLUE) { 2783 int c = (clue == CORNER) ? black : white; 2784 draw_circle(dr, cx, cy, TILE_SIZE/4, c, black); 2785 } 2786 } 2787 } 2788 2789 game_free_drawstate(dr, ds); 2790} 2791 2792#ifdef COMBINED 2793#define thegame pearl 2794#endif 2795 2796const struct game thegame = { 2797 "Pearl", "games.pearl", "pearl", 2798 default_params, 2799 game_fetch_preset, NULL, 2800 decode_params, 2801 encode_params, 2802 free_params, 2803 dup_params, 2804 true, game_configure, custom_params, 2805 validate_params, 2806 new_game_desc, 2807 validate_desc, 2808 new_game, 2809 dup_game, 2810 free_game, 2811 true, solve_game, 2812 true, game_can_format_as_text_now, game_text_format, 2813 get_prefs, set_prefs, 2814 new_ui, 2815 free_ui, 2816 NULL, /* encode_ui */ 2817 NULL, /* decode_ui */ 2818 NULL, /* game_request_keys */ 2819 game_changed_state, 2820 current_key_label, 2821 interpret_move, 2822 execute_move, 2823 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2824 game_colours, 2825 game_new_drawstate, 2826 game_free_drawstate, 2827 game_redraw, 2828 game_anim_length, 2829 game_flash_length, 2830 game_get_cursor_location, 2831 game_status, 2832 true, false, game_print_size, game_print, 2833 false, /* wants_statusbar */ 2834 false, NULL, /* timing_state */ 2835 0, /* flags */ 2836}; 2837 2838#ifdef STANDALONE_SOAK_TEST 2839 2840#include <time.h> 2841#include <stdarg.h> 2842 2843static const char *quis = NULL; 2844 2845static void usage(FILE *out) { 2846 fprintf(out, "usage: %s <params>\n", quis); 2847} 2848 2849static void pnum(int n, int ntot, const char *desc) 2850{ 2851 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc); 2852} 2853 2854static void start_soak(game_params *p, random_state *rs, int nsecs) 2855{ 2856 time_t tt_start, tt_now, tt_last; 2857 int n = 0, nsolved = 0, nimpossible = 0, ret; 2858 char *grid, *clues; 2859 2860 tt_start = tt_last = time(NULL); 2861 2862 /* Currently this generates puzzles of any difficulty (trying to solve it 2863 * on the maximum difficulty level and not checking it's not too easy). */ 2864 printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h); 2865 if (nsecs > 0) printf(" for %d seconds", nsecs); 2866 printf(".\n"); 2867 2868 p->nosolve = true; 2869 2870 grid = snewn(p->w*p->h, char); 2871 clues = snewn(p->w*p->h, char); 2872 2873 while (1) { 2874 n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */ 2875 2876 ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, false); 2877 if (ret <= 0) nimpossible++; 2878 if (ret == 1) nsolved++; 2879 2880 tt_now = time(NULL); 2881 if (tt_now > tt_last) { 2882 tt_last = tt_now; 2883 2884 printf("%d total, %3.1f/s, ", 2885 n, (double)n / ((double)tt_now - tt_start)); 2886 pnum(nsolved, n, "solved"); printf(", "); 2887 printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start)); 2888 if (nimpossible > 0) 2889 pnum(nimpossible, n, "impossible"); 2890 printf("\n"); 2891 } 2892 if (nsecs > 0 && (tt_now - tt_start) > nsecs) { 2893 printf("\n"); 2894 break; 2895 } 2896 } 2897 2898 sfree(grid); 2899 sfree(clues); 2900} 2901 2902int main(int argc, char *argv[]) 2903{ 2904 game_params *p = NULL; 2905 random_state *rs = NULL; 2906 time_t seed = time(NULL); 2907 char *id = NULL; 2908 const char *err; 2909 2910 setvbuf(stdout, NULL, _IONBF, 0); 2911 2912 quis = argv[0]; 2913 2914 while (--argc > 0) { 2915 char *p = (char*)(*++argv); 2916 if (!strcmp(p, "-e") || !strcmp(p, "--seed")) { 2917 seed = atoi(*++argv); 2918 argc--; 2919 } else if (*p == '-') { 2920 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); 2921 usage(stderr); 2922 exit(1); 2923 } else { 2924 id = p; 2925 } 2926 } 2927 2928 rs = random_new((void*)&seed, sizeof(time_t)); 2929 p = default_params(); 2930 2931 if (id) { 2932 if (strchr(id, ':')) { 2933 fprintf(stderr, "soak takes params only.\n"); 2934 goto done; 2935 } 2936 2937 decode_params(p, id); 2938 err = validate_params(p, true); 2939 if (err) { 2940 fprintf(stderr, "%s: %s", argv[0], err); 2941 goto done; 2942 } 2943 2944 start_soak(p, rs, 0); /* run forever */ 2945 } else { 2946 int i; 2947 2948 for (i = 5; i <= 12; i++) { 2949 p->w = p->h = i; 2950 start_soak(p, rs, 5); 2951 } 2952 } 2953 2954done: 2955 free_params(p); 2956 random_free(rs); 2957 2958 return 0; 2959} 2960 2961#endif /* STANDALONE_SOAK_TEST */ 2962 2963#ifdef STANDALONE_SOLVER 2964 2965#include <stdarg.h> 2966 2967int main(int argc, char **argv) 2968{ 2969 game_params *p; 2970 game_state *s; 2971 char *id = NULL, *desc; 2972 const char *err; 2973 bool grade = false; 2974 int ret, diff; 2975 bool really_show_working = false; 2976 2977 while (--argc > 0) { 2978 char *p = *++argv; 2979 if (!strcmp(p, "-v")) { 2980 really_show_working = true; 2981 } else if (!strcmp(p, "-g")) { 2982 grade = true; 2983 } else if (*p == '-') { 2984 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p); 2985 return 1; 2986 } else { 2987 id = p; 2988 } 2989 } 2990 2991 if (!id) { 2992 fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]); 2993 return 1; 2994 } 2995 2996 desc = strchr(id, ':'); 2997 if (!desc) { 2998 fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]); 2999 return 1; 3000 } 3001 *desc++ = '\0'; 3002 3003 p = default_params(); 3004 decode_params(p, id); 3005 err = validate_desc(p, desc); 3006 if (err) { 3007 fprintf(stderr, "%s: %s\n", argv[0], err); 3008 return 1; 3009 } 3010 s = new_game(NULL, p, desc); 3011 3012 /* 3013 * When solving an Easy puzzle, we don't want to bother the 3014 * user with Hard-level deductions. For this reason, we grade 3015 * the puzzle internally before doing anything else. 3016 */ 3017 ret = -1; /* placate optimiser */ 3018 solver_show_working = 0; 3019 for (diff = 0; diff < DIFFCOUNT; diff++) { 3020 clear_solution(s); 3021 ret = pearl_solve(p->w, p->h, s->shared->clues, s->lines, diff, false); 3022 if (ret < 2) 3023 break; 3024 } 3025 3026 if (grade) { 3027 if (ret == 0) 3028 printf("Difficulty rating: impossible (no solution exists)\n"); 3029 else if (diff < DIFFCOUNT) 3030 printf("Difficulty rating: %s\n", pearl_diffnames[ret]); 3031 else 3032 printf("Difficulty rating: ambiguous\n"); 3033 } else { 3034 solver_show_working = really_show_working ? 1 : 0; 3035 clear_solution(s); 3036 ret = pearl_solve(p->w, p->h, s->shared->clues, s->lines, 3037 diff, false); 3038 if (ret == 0) { 3039 printf("Puzzle is inconsistent\n"); 3040 } else if (ret == 2) { 3041 printf("Unable to find a unique solution\n"); 3042 } else { 3043 char *text = game_text_format(s); 3044 fputs(text, stdout); 3045 sfree(text); 3046 } 3047 } 3048 3049 free_game(s); 3050 free_params(p); 3051 3052 return 0; 3053} 3054 3055#endif 3056 3057/* vim: set shiftwidth=4 tabstop=8: */