A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 3024 lines 95 kB view raw
1/* 2 * rect.c: Puzzle from nikoli.co.jp. You have a square grid with 3 * numbers in some squares; you must divide the square grid up into 4 * variously sized rectangles, such that every rectangle contains 5 * exactly one numbered square and the area of each rectangle is 6 * equal to the number contained in it. 7 */ 8 9/* 10 * TODO: 11 * 12 * - Improve singleton removal. 13 * + It would be nice to limit the size of the generated 14 * rectangles in accordance with existing constraints such as 15 * the maximum rectangle size and the one about not 16 * generating a rectangle the full width or height of the 17 * grid. 18 * + This could be achieved by making a less random choice 19 * about which of the available options to use. 20 * + Alternatively, we could create our rectangle and then 21 * split it up. 22 */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <assert.h> 28#include <ctype.h> 29#ifdef NO_TGMATH_H 30# include <math.h> 31#else 32# include <tgmath.h> 33#endif 34 35#include "puzzles.h" 36 37enum { 38 COL_BACKGROUND, 39 COL_CORRECT, 40 COL_LINE, 41 COL_TEXT, 42 COL_GRID, 43 COL_DRAG, COL_DRAGERASE, 44 COL_CURSOR, 45 NCOLOURS 46}; 47 48struct game_params { 49 int w, h; 50 float expandfactor; 51 bool unique; 52}; 53 54#define INDEX(state, x, y) (((y) * (state)->w) + (x)) 55#define index(state, a, x, y) ((a) [ INDEX(state,x,y) ]) 56#define grid(state,x,y) index(state, (state)->grid, x, y) 57#define vedge(state,x,y) index(state, (state)->vedge, x, y) 58#define hedge(state,x,y) index(state, (state)->hedge, x, y) 59 60#define CRANGE(state,x,y,dx,dy) ( (x) >= dx && (x) < (state)->w && \ 61 (y) >= dy && (y) < (state)->h ) 62#define RANGE(state,x,y) CRANGE(state,x,y,0,0) 63#define HRANGE(state,x,y) CRANGE(state,x,y,0,1) 64#define VRANGE(state,x,y) CRANGE(state,x,y,1,0) 65 66#define PREFERRED_TILE_SIZE 24 67#define TILE_SIZE (ds->tilesize) 68#ifdef SMALL_SCREEN 69#define BORDER (2) 70#else 71#define BORDER (TILE_SIZE * 3 / 4) 72#endif 73 74#define CORNER_TOLERANCE 0.15F 75#define CENTRE_TOLERANCE 0.15F 76 77#define FLASH_TIME 0.13F 78 79#define COORD(x) ( (x) * TILE_SIZE + BORDER ) 80#define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE ) 81 82struct game_state { 83 int w, h; 84 int *grid; /* contains the numbers */ 85 unsigned char *vedge; /* (w+1) x h */ 86 unsigned char *hedge; /* w x (h+1) */ 87 bool completed, cheated; 88 unsigned char *correct; 89}; 90 91static game_params *default_params(void) 92{ 93 game_params *ret = snew(game_params); 94 95 ret->w = ret->h = 7; 96 ret->expandfactor = 0.0F; 97 ret->unique = true; 98 99 return ret; 100} 101 102static bool game_fetch_preset(int i, char **name, game_params **params) 103{ 104 game_params *ret; 105 int w, h; 106 char buf[80]; 107 108 switch (i) { 109 case 0: w = 7, h = 7; break; 110 case 1: w = 9, h = 9; break; 111 case 2: w = 11, h = 11; break; 112 case 3: w = 13, h = 13; break; 113 case 4: w = 15, h = 15; break; 114#ifndef SMALL_SCREEN 115 case 5: w = 17, h = 17; break; 116 case 6: w = 19, h = 19; break; 117#endif 118 default: return false; 119 } 120 121 sprintf(buf, "%dx%d", w, h); 122 *name = dupstr(buf); 123 *params = ret = snew(game_params); 124 ret->w = w; 125 ret->h = h; 126 ret->expandfactor = 0.0F; 127 ret->unique = true; 128 return true; 129} 130 131static void free_params(game_params *params) 132{ 133 sfree(params); 134} 135 136static game_params *dup_params(const game_params *params) 137{ 138 game_params *ret = snew(game_params); 139 *ret = *params; /* structure copy */ 140 return ret; 141} 142 143static void decode_params(game_params *ret, char const *string) 144{ 145 ret->w = ret->h = atoi(string); 146 while (*string && isdigit((unsigned char)*string)) string++; 147 if (*string == 'x') { 148 string++; 149 ret->h = atoi(string); 150 while (*string && isdigit((unsigned char)*string)) string++; 151 } 152 if (*string == 'e') { 153 string++; 154 ret->expandfactor = (float)atof(string); 155 while (*string && 156 (*string == '.' || isdigit((unsigned char)*string))) string++; 157 } 158 if (*string == 'a') { 159 string++; 160 ret->unique = false; 161 } 162} 163 164static char *encode_params(const game_params *params, bool full) 165{ 166 char data[256]; 167 168 sprintf(data, "%dx%d", params->w, params->h); 169 if (full && params->expandfactor) 170 sprintf(data + strlen(data), "e%g", params->expandfactor); 171 if (full && !params->unique) 172 strcat(data, "a"); 173 174 return dupstr(data); 175} 176 177static config_item *game_configure(const game_params *params) 178{ 179 config_item *ret; 180 char buf[80]; 181 182 ret = snewn(5, config_item); 183 184 ret[0].name = "Width"; 185 ret[0].type = C_STRING; 186 sprintf(buf, "%d", params->w); 187 ret[0].u.string.sval = dupstr(buf); 188 189 ret[1].name = "Height"; 190 ret[1].type = C_STRING; 191 sprintf(buf, "%d", params->h); 192 ret[1].u.string.sval = dupstr(buf); 193 194 ret[2].name = "Expansion factor"; 195 ret[2].type = C_STRING; 196 sprintf(buf, "%g", params->expandfactor); 197 ret[2].u.string.sval = dupstr(buf); 198 199 ret[3].name = "Ensure unique solution"; 200 ret[3].type = C_BOOLEAN; 201 ret[3].u.boolean.bval = params->unique; 202 203 ret[4].name = NULL; 204 ret[4].type = C_END; 205 206 return ret; 207} 208 209static game_params *custom_params(const config_item *cfg) 210{ 211 game_params *ret = snew(game_params); 212 213 ret->w = atoi(cfg[0].u.string.sval); 214 ret->h = atoi(cfg[1].u.string.sval); 215 ret->expandfactor = (float)atof(cfg[2].u.string.sval); 216 ret->unique = cfg[3].u.boolean.bval; 217 218 return ret; 219} 220 221static const char *validate_params(const game_params *params, bool full) 222{ 223 if (params->w <= 0 || params->h <= 0) 224 return "Width and height must both be greater than zero"; 225 if (params->w > INT_MAX / params->h) 226 return "Width times height must not be unreasonably large"; 227 if (params->w*params->h < 2) 228 return "Grid area must be greater than one"; 229 if (params->expandfactor < 0.0F) 230 return "Expansion factor may not be negative"; 231 return NULL; 232} 233 234struct point { 235 int x, y; 236}; 237 238struct rect { 239 int x, y; 240 int w, h; 241}; 242 243struct rectlist { 244 struct rect *rects; 245 int n; 246}; 247 248struct numberdata { 249 int area; 250 int npoints; 251 struct point *points; 252}; 253 254/* ---------------------------------------------------------------------- 255 * Solver for Rectangles games. 256 * 257 * This solver is souped up beyond the needs of actually _solving_ 258 * a puzzle. It is also designed to cope with uncertainty about 259 * where the numbers have been placed. This is because I run it on 260 * my generated grids _before_ placing the numbers, and have it 261 * tell me where I need to place the numbers to ensure a unique 262 * solution. 263 */ 264 265static void remove_rect_placement(int w, int h, 266 struct rectlist *rectpositions, 267 int *overlaps, 268 int rectnum, int placement) 269{ 270 int x, y, xx, yy; 271 272#ifdef SOLVER_DIAGNOSTICS 273 printf("ruling out rect %d placement at %d,%d w=%d h=%d\n", rectnum, 274 rectpositions[rectnum].rects[placement].x, 275 rectpositions[rectnum].rects[placement].y, 276 rectpositions[rectnum].rects[placement].w, 277 rectpositions[rectnum].rects[placement].h); 278#endif 279 280 /* 281 * Decrement each entry in the overlaps array to reflect the 282 * removal of this rectangle placement. 283 */ 284 for (yy = 0; yy < rectpositions[rectnum].rects[placement].h; yy++) { 285 y = yy + rectpositions[rectnum].rects[placement].y; 286 for (xx = 0; xx < rectpositions[rectnum].rects[placement].w; xx++) { 287 x = xx + rectpositions[rectnum].rects[placement].x; 288 289 assert(overlaps[(rectnum * h + y) * w + x] != 0); 290 291 if (overlaps[(rectnum * h + y) * w + x] > 0) 292 overlaps[(rectnum * h + y) * w + x]--; 293 } 294 } 295 296 /* 297 * Remove the placement from the list of positions for that 298 * rectangle, by interchanging it with the one on the end. 299 */ 300 if (placement < rectpositions[rectnum].n - 1) { 301 struct rect t; 302 303 t = rectpositions[rectnum].rects[rectpositions[rectnum].n - 1]; 304 rectpositions[rectnum].rects[rectpositions[rectnum].n - 1] = 305 rectpositions[rectnum].rects[placement]; 306 rectpositions[rectnum].rects[placement] = t; 307 } 308 rectpositions[rectnum].n--; 309} 310 311static void remove_number_placement(int w, int h, struct numberdata *number, 312 int index, int *rectbyplace) 313{ 314 /* 315 * Remove the entry from the rectbyplace array. 316 */ 317 rectbyplace[number->points[index].y * w + number->points[index].x] = -1; 318 319 /* 320 * Remove the placement from the list of candidates for that 321 * number, by interchanging it with the one on the end. 322 */ 323 if (index < number->npoints - 1) { 324 struct point t; 325 326 t = number->points[number->npoints - 1]; 327 number->points[number->npoints - 1] = number->points[index]; 328 number->points[index] = t; 329 } 330 number->npoints--; 331} 332 333/* 334 * Returns 0 for failure to solve due to inconsistency; 1 for 335 * success; 2 for failure to complete a solution due to either 336 * ambiguity or it being too difficult. 337 */ 338static int rect_solver(int w, int h, int nrects, struct numberdata *numbers, 339 unsigned char *hedge, unsigned char *vedge, 340 random_state *rs) 341{ 342 struct rectlist *rectpositions; 343 int *overlaps, *rectbyplace, *workspace; 344 int i, ret; 345 346 /* 347 * Start by setting up a list of candidate positions for each 348 * rectangle. 349 */ 350 rectpositions = snewn(nrects, struct rectlist); 351 for (i = 0; i < nrects; i++) { 352 int rw, rh, area = numbers[i].area; 353 int j, minx, miny, maxx, maxy; 354 struct rect *rlist; 355 int rlistn, rlistsize; 356 357 /* 358 * For each rectangle, begin by finding the bounding 359 * rectangle of its candidate number placements. 360 */ 361 maxx = maxy = -1; 362 minx = w; 363 miny = h; 364 for (j = 0; j < numbers[i].npoints; j++) { 365 if (minx > numbers[i].points[j].x) minx = numbers[i].points[j].x; 366 if (miny > numbers[i].points[j].y) miny = numbers[i].points[j].y; 367 if (maxx < numbers[i].points[j].x) maxx = numbers[i].points[j].x; 368 if (maxy < numbers[i].points[j].y) maxy = numbers[i].points[j].y; 369 } 370 371 /* 372 * Now loop over all possible rectangle placements 373 * overlapping a point within that bounding rectangle; 374 * ensure each one actually contains a candidate number 375 * placement, and add it to the list. 376 */ 377 rlist = NULL; 378 rlistn = rlistsize = 0; 379 380 for (rw = 1; rw <= area && rw <= w; rw++) { 381 int x, y; 382 383 if (area % rw) 384 continue; 385 rh = area / rw; 386 if (rh > h) 387 continue; 388 389 for (y = miny - rh + 1; y <= maxy; y++) { 390 if (y < 0 || y+rh > h) 391 continue; 392 393 for (x = minx - rw + 1; x <= maxx; x++) { 394 if (x < 0 || x+rw > w) 395 continue; 396 397 /* 398 * See if we can find a candidate number 399 * placement within this rectangle. 400 */ 401 for (j = 0; j < numbers[i].npoints; j++) 402 if (numbers[i].points[j].x >= x && 403 numbers[i].points[j].x < x+rw && 404 numbers[i].points[j].y >= y && 405 numbers[i].points[j].y < y+rh) 406 break; 407 408 if (j < numbers[i].npoints) { 409 /* 410 * Add this to the list of candidate 411 * placements for this rectangle. 412 */ 413 if (rlistn >= rlistsize) { 414 rlistsize = rlistn + 32; 415 rlist = sresize(rlist, rlistsize, struct rect); 416 } 417 rlist[rlistn].x = x; 418 rlist[rlistn].y = y; 419 rlist[rlistn].w = rw; 420 rlist[rlistn].h = rh; 421#ifdef SOLVER_DIAGNOSTICS 422 printf("rect %d [area %d]: candidate position at" 423 " %d,%d w=%d h=%d\n", 424 i, area, x, y, rw, rh); 425#endif 426 rlistn++; 427 } 428 } 429 } 430 } 431 432 rectpositions[i].rects = rlist; 433 rectpositions[i].n = rlistn; 434 } 435 436 /* 437 * Next, construct a multidimensional array tracking how many 438 * candidate positions for each rectangle overlap each square. 439 * 440 * Indexing of this array is by the formula 441 * 442 * overlaps[(rectindex * h + y) * w + x] 443 * 444 * A positive or zero value indicates what it sounds as if it 445 * should; -1 indicates that this square _cannot_ be part of 446 * this rectangle; and -2 indicates that it _definitely_ is 447 * (which is distinct from 1, because one might very well know 448 * that _if_ square S is part of rectangle R then it must be 449 * because R is placed in a certain position without knowing 450 * that it definitely _is_). 451 */ 452 overlaps = snewn(nrects * w * h, int); 453 memset(overlaps, 0, nrects * w * h * sizeof(int)); 454 for (i = 0; i < nrects; i++) { 455 int j; 456 457 for (j = 0; j < rectpositions[i].n; j++) { 458 int xx, yy; 459 460 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) 461 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) 462 overlaps[(i * h + yy+rectpositions[i].rects[j].y) * w + 463 xx+rectpositions[i].rects[j].x]++; 464 } 465 } 466 467 /* 468 * Also we want an array covering the grid once, to make it 469 * easy to figure out which squares are candidate number 470 * placements for which rectangles. (The existence of this 471 * single array assumes that no square starts off as a 472 * candidate number placement for more than one rectangle. This 473 * assumption is justified, because this solver is _either_ 474 * used to solve real problems - in which case there is a 475 * single placement for every number - _or_ used to decide on 476 * number placements for a new puzzle, in which case each 477 * number's placements are confined to the intended position of 478 * the rectangle containing that number.) 479 */ 480 rectbyplace = snewn(w * h, int); 481 for (i = 0; i < w*h; i++) 482 rectbyplace[i] = -1; 483 484 for (i = 0; i < nrects; i++) { 485 int j; 486 487 for (j = 0; j < numbers[i].npoints; j++) { 488 int x = numbers[i].points[j].x; 489 int y = numbers[i].points[j].y; 490 491 assert(rectbyplace[y * w + x] == -1); 492 rectbyplace[y * w + x] = i; 493 } 494 } 495 496 workspace = snewn(nrects, int); 497 498 /* 499 * Now run the actual deduction loop. 500 */ 501 while (1) { 502 bool done_something = false; 503 504#ifdef SOLVER_DIAGNOSTICS 505 printf("starting deduction loop\n"); 506 507 for (i = 0; i < nrects; i++) { 508 printf("rect %d overlaps:\n", i); 509 { 510 int x, y; 511 for (y = 0; y < h; y++) { 512 for (x = 0; x < w; x++) { 513 printf("%3d", overlaps[(i * h + y) * w + x]); 514 } 515 printf("\n"); 516 } 517 } 518 } 519 printf("rectbyplace:\n"); 520 { 521 int x, y; 522 for (y = 0; y < h; y++) { 523 for (x = 0; x < w; x++) { 524 printf("%3d", rectbyplace[y * w + x]); 525 } 526 printf("\n"); 527 } 528 } 529#endif 530 531 /* 532 * Housekeeping. Look for rectangles whose number has only 533 * one candidate position left, and mark that square as 534 * known if it isn't already. 535 */ 536 for (i = 0; i < nrects; i++) { 537 if (numbers[i].npoints == 1) { 538 int x = numbers[i].points[0].x; 539 int y = numbers[i].points[0].y; 540 if (overlaps[(i * h + y) * w + x] >= -1) { 541 int j; 542 543 if (overlaps[(i * h + y) * w + x] <= 0) { 544 ret = 0; /* inconsistency */ 545 goto cleanup; 546 } 547#ifdef SOLVER_DIAGNOSTICS 548 printf("marking %d,%d as known for rect %d" 549 " (sole remaining number position)\n", x, y, i); 550#endif 551 552 for (j = 0; j < nrects; j++) 553 overlaps[(j * h + y) * w + x] = -1; 554 555 overlaps[(i * h + y) * w + x] = -2; 556 } 557 } 558 } 559 560 /* 561 * Now look at the intersection of all possible placements 562 * for each rectangle, and mark all squares in that 563 * intersection as known for that rectangle if they aren't 564 * already. 565 */ 566 for (i = 0; i < nrects; i++) { 567 int minx, miny, maxx, maxy, xx, yy, j; 568 569 minx = miny = 0; 570 maxx = w; 571 maxy = h; 572 573 for (j = 0; j < rectpositions[i].n; j++) { 574 int x = rectpositions[i].rects[j].x; 575 int y = rectpositions[i].rects[j].y; 576 int w = rectpositions[i].rects[j].w; 577 int h = rectpositions[i].rects[j].h; 578 579 if (minx < x) minx = x; 580 if (miny < y) miny = y; 581 if (maxx > x+w) maxx = x+w; 582 if (maxy > y+h) maxy = y+h; 583 } 584 585 for (yy = miny; yy < maxy; yy++) 586 for (xx = minx; xx < maxx; xx++) 587 if (overlaps[(i * h + yy) * w + xx] >= -1) { 588 if (overlaps[(i * h + yy) * w + xx] <= 0) { 589 ret = 0; /* inconsistency */ 590 goto cleanup; 591 } 592#ifdef SOLVER_DIAGNOSTICS 593 printf("marking %d,%d as known for rect %d" 594 " (intersection of all placements)\n", 595 xx, yy, i); 596#endif 597 598 for (j = 0; j < nrects; j++) 599 overlaps[(j * h + yy) * w + xx] = -1; 600 601 overlaps[(i * h + yy) * w + xx] = -2; 602 } 603 } 604 605 /* 606 * Rectangle-focused deduction. Look at each rectangle in 607 * turn and try to rule out some of its candidate 608 * placements. 609 */ 610 for (i = 0; i < nrects; i++) { 611 int j; 612 613 for (j = 0; j < rectpositions[i].n; j++) { 614 int xx, yy, k; 615 bool del = false; 616 617 for (k = 0; k < nrects; k++) 618 workspace[k] = 0; 619 620 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) { 621 int y = yy + rectpositions[i].rects[j].y; 622 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) { 623 int x = xx + rectpositions[i].rects[j].x; 624 625 if (overlaps[(i * h + y) * w + x] == -1) { 626 /* 627 * This placement overlaps a square 628 * which is _known_ to be part of 629 * another rectangle. Therefore we must 630 * rule it out. 631 */ 632#ifdef SOLVER_DIAGNOSTICS 633 printf("rect %d placement at %d,%d w=%d h=%d " 634 "contains %d,%d which is known-other\n", i, 635 rectpositions[i].rects[j].x, 636 rectpositions[i].rects[j].y, 637 rectpositions[i].rects[j].w, 638 rectpositions[i].rects[j].h, 639 x, y); 640#endif 641 del = true; 642 } 643 644 if (rectbyplace[y * w + x] != -1) { 645 /* 646 * This placement overlaps one of the 647 * candidate number placements for some 648 * rectangle. Count it. 649 */ 650 workspace[rectbyplace[y * w + x]]++; 651 } 652 } 653 } 654 655 if (!del) { 656 /* 657 * If we haven't ruled this placement out 658 * already, see if it overlaps _all_ of the 659 * candidate number placements for any 660 * rectangle. If so, we can rule it out. 661 */ 662 for (k = 0; k < nrects; k++) 663 if (k != i && workspace[k] == numbers[k].npoints) { 664#ifdef SOLVER_DIAGNOSTICS 665 printf("rect %d placement at %d,%d w=%d h=%d " 666 "contains all number points for rect %d\n", 667 i, 668 rectpositions[i].rects[j].x, 669 rectpositions[i].rects[j].y, 670 rectpositions[i].rects[j].w, 671 rectpositions[i].rects[j].h, 672 k); 673#endif 674 del = true; 675 break; 676 } 677 678 /* 679 * Failing that, see if it overlaps at least 680 * one of the candidate number placements for 681 * itself! (This might not be the case if one 682 * of those number placements has been removed 683 * recently.). 684 */ 685 if (!del && workspace[i] == 0) { 686#ifdef SOLVER_DIAGNOSTICS 687 printf("rect %d placement at %d,%d w=%d h=%d " 688 "contains none of its own number points\n", 689 i, 690 rectpositions[i].rects[j].x, 691 rectpositions[i].rects[j].y, 692 rectpositions[i].rects[j].w, 693 rectpositions[i].rects[j].h); 694#endif 695 del = true; 696 } 697 } 698 699 if (del) { 700 remove_rect_placement(w, h, rectpositions, overlaps, i, j); 701 702 j--; /* don't skip over next placement */ 703 704 done_something = true; 705 } 706 } 707 } 708 709 /* 710 * Square-focused deduction. Look at each square not marked 711 * as known, and see if there are any which can only be 712 * part of a single rectangle. 713 */ 714 { 715 int x, y, n, index; 716 for (y = 0; y < h; y++) for (x = 0; x < w; x++) { 717 /* Known squares are marked as <0 everywhere, so we only need 718 * to check the overlaps entry for rect 0. */ 719 if (overlaps[y * w + x] < 0) 720 continue; /* known already */ 721 722 n = 0; 723 index = -1; 724 for (i = 0; i < nrects; i++) 725 if (overlaps[(i * h + y) * w + x] > 0) 726 n++, index = i; 727 728 if (n == 1) { 729 int j; 730 731 /* 732 * Now we can rule out all placements for 733 * rectangle `index' which _don't_ contain 734 * square x,y. 735 */ 736#ifdef SOLVER_DIAGNOSTICS 737 printf("square %d,%d can only be in rectangle %d\n", 738 x, y, index); 739#endif 740 for (j = 0; j < rectpositions[index].n; j++) { 741 struct rect *r = &rectpositions[index].rects[j]; 742 if (x >= r->x && x < r->x + r->w && 743 y >= r->y && y < r->y + r->h) 744 continue; /* this one is OK */ 745 remove_rect_placement(w, h, rectpositions, overlaps, 746 index, j); 747 j--; /* don't skip over next placement */ 748 done_something = true; 749 } 750 } 751 } 752 } 753 754 /* 755 * If we've managed to deduce anything by normal means, 756 * loop round again and see if there's more to be done. 757 * Only if normal deduction has completely failed us should 758 * we now move on to narrowing down the possible number 759 * placements. 760 */ 761 if (done_something) 762 continue; 763 764 /* 765 * Now we have done everything we can with the current set 766 * of number placements. So we need to winnow the number 767 * placements so as to narrow down the possibilities. We do 768 * this by searching for a candidate placement (of _any_ 769 * rectangle) which overlaps a candidate placement of the 770 * number for some other rectangle. 771 */ 772 if (rs) { 773 struct rpn { 774 int rect; 775 int placement; 776 int number; 777 } *rpns = NULL; 778 size_t nrpns = 0, rpnsize = 0; 779 int j; 780 781 for (i = 0; i < nrects; i++) { 782 for (j = 0; j < rectpositions[i].n; j++) { 783 int xx, yy; 784 785 for (yy = 0; yy < rectpositions[i].rects[j].h; yy++) { 786 int y = yy + rectpositions[i].rects[j].y; 787 for (xx = 0; xx < rectpositions[i].rects[j].w; xx++) { 788 int x = xx + rectpositions[i].rects[j].x; 789 790 if (rectbyplace[y * w + x] >= 0 && 791 rectbyplace[y * w + x] != i) { 792 /* 793 * Add this to the list of 794 * winnowing possibilities. 795 */ 796 if (nrpns >= rpnsize) { 797 rpnsize = rpnsize * 3 / 2 + 32; 798 rpns = sresize(rpns, rpnsize, struct rpn); 799 } 800 rpns[nrpns].rect = i; 801 rpns[nrpns].placement = j; 802 rpns[nrpns].number = rectbyplace[y * w + x]; 803 nrpns++; 804 } 805 } 806 } 807 808 } 809 } 810 811#ifdef SOLVER_DIAGNOSTICS 812 printf("%d candidate rect placements we could eliminate\n", nrpns); 813#endif 814 if (nrpns > 0) { 815 /* 816 * Now choose one of these unwanted rectangle 817 * placements, and eliminate it. 818 */ 819 int index = random_upto(rs, nrpns); 820 int k, m; 821 struct rpn rpn = rpns[index]; 822 struct rect r; 823 sfree(rpns); 824 825 i = rpn.rect; 826 j = rpn.placement; 827 k = rpn.number; 828 r = rectpositions[i].rects[j]; 829 830 /* 831 * We rule out placement j of rectangle i by means 832 * of removing all of rectangle k's candidate 833 * number placements which do _not_ overlap it. 834 * This will ensure that it is eliminated during 835 * the next pass of rectangle-focused deduction. 836 */ 837#ifdef SOLVER_DIAGNOSTICS 838 printf("ensuring number for rect %d is within" 839 " rect %d's placement at %d,%d w=%d h=%d\n", 840 k, i, r.x, r.y, r.w, r.h); 841#endif 842 843 for (m = 0; m < numbers[k].npoints; m++) { 844 int x = numbers[k].points[m].x; 845 int y = numbers[k].points[m].y; 846 847 if (x < r.x || x >= r.x + r.w || 848 y < r.y || y >= r.y + r.h) { 849#ifdef SOLVER_DIAGNOSTICS 850 printf("eliminating number for rect %d at %d,%d\n", 851 k, x, y); 852#endif 853 remove_number_placement(w, h, &numbers[k], 854 m, rectbyplace); 855 m--; /* don't skip the next one */ 856 done_something = true; 857 } 858 } 859 } 860 } 861 862 if (!done_something) { 863#ifdef SOLVER_DIAGNOSTICS 864 printf("terminating deduction loop\n"); 865#endif 866 break; 867 } 868 } 869 870 cleanup: 871 ret = 1; 872 for (i = 0; i < nrects; i++) { 873#ifdef SOLVER_DIAGNOSTICS 874 printf("rect %d has %d possible placements\n", 875 i, rectpositions[i].n); 876#endif 877 if (rectpositions[i].n <= 0) { 878 ret = 0; /* inconsistency */ 879 } else if (rectpositions[i].n > 1) { 880 ret = 2; /* remaining uncertainty */ 881 } else if (hedge && vedge) { 882 /* 883 * Place the rectangle in its only possible position. 884 */ 885 int x, y; 886 struct rect *r = &rectpositions[i].rects[0]; 887 888 for (y = 0; y < r->h; y++) { 889 if (r->x > 0) 890 vedge[(r->y+y) * w + r->x] = 1; 891 if (r->x+r->w < w) 892 vedge[(r->y+y) * w + r->x+r->w] = 1; 893 } 894 for (x = 0; x < r->w; x++) { 895 if (r->y > 0) 896 hedge[r->y * w + r->x+x] = 1; 897 if (r->y+r->h < h) 898 hedge[(r->y+r->h) * w + r->x+x] = 1; 899 } 900 } 901 } 902 903 /* 904 * Free up all allocated storage. 905 */ 906 sfree(workspace); 907 sfree(rectbyplace); 908 sfree(overlaps); 909 for (i = 0; i < nrects; i++) 910 sfree(rectpositions[i].rects); 911 sfree(rectpositions); 912 913 return ret; 914} 915 916/* ---------------------------------------------------------------------- 917 * Grid generation code. 918 */ 919 920/* 921 * This function does one of two things. If passed r==NULL, it 922 * counts the number of possible rectangles which cover the given 923 * square, and returns it in *n. If passed r!=NULL then it _reads_ 924 * *n to find an index, counts the possible rectangles until it 925 * reaches the nth, and writes it into r. 926 * 927 * `scratch' is expected to point to an array of 2 * params->w 928 * ints, used internally as scratch space (and passed in like this 929 * to avoid re-allocating and re-freeing it every time round a 930 * tight loop). 931 */ 932static void enum_rects(game_params *params, int *grid, struct rect *r, int *n, 933 int sx, int sy, int *scratch) 934{ 935 int rw, rh, mw, mh; 936 int x, y, dx, dy; 937 int maxarea, realmaxarea; 938 int index = 0; 939 int *top, *bottom; 940 941 /* 942 * Maximum rectangle area is 1/6 of total grid size, unless 943 * this means we can't place any rectangles at all in which 944 * case we set it to 2 at minimum. 945 */ 946 maxarea = params->w * params->h / 6; 947 if (maxarea < 2) 948 maxarea = 2; 949 950 /* 951 * Scan the grid to find the limits of the region within which 952 * any rectangle containing this point must fall. This will 953 * save us trawling the inside of every rectangle later on to 954 * see if it contains any used squares. 955 */ 956 top = scratch; 957 bottom = scratch + params->w; 958 for (dy = -1; dy <= +1; dy += 2) { 959 int *array = (dy == -1 ? top : bottom); 960 for (dx = -1; dx <= +1; dx += 2) { 961 for (x = sx; x >= 0 && x < params->w; x += dx) { 962 array[x] = -2 * params->h * dy; 963 for (y = sy; y >= 0 && y < params->h; y += dy) { 964 if (index(params, grid, x, y) == -1 && 965 (x == sx || dy*y <= dy*array[x-dx])) 966 array[x] = y; 967 else 968 break; 969 } 970 } 971 } 972 } 973 974 /* 975 * Now scan again to work out the largest rectangles we can fit 976 * in the grid, so that we can terminate the following loops 977 * early once we get down to not having much space left in the 978 * grid. 979 */ 980 realmaxarea = 0; 981 for (x = 0; x < params->w; x++) { 982 int x2; 983 984 rh = bottom[x] - top[x] + 1; 985 if (rh <= 0) 986 continue; /* no rectangles can start here */ 987 988 dx = (x > sx ? -1 : +1); 989 for (x2 = x; x2 >= 0 && x2 < params->w; x2 += dx) 990 if (bottom[x2] < bottom[x] || top[x2] > top[x]) 991 break; 992 993 rw = abs(x2 - x); 994 if (realmaxarea < rw * rh) 995 realmaxarea = rw * rh; 996 } 997 998 if (realmaxarea > maxarea) 999 realmaxarea = maxarea; 1000 1001 /* 1002 * Rectangles which go right the way across the grid are 1003 * boring, although they can't be helped in the case of 1004 * extremely small grids. (Also they might be generated later 1005 * on by the singleton-removal process; we can't help that.) 1006 */ 1007 mw = params->w - 1; 1008 if (mw < 3) mw++; 1009 mh = params->h - 1; 1010 if (mh < 3) mh++; 1011 1012 for (rw = 1; rw <= mw; rw++) 1013 for (rh = 1; rh <= mh; rh++) { 1014 if (rw * rh > realmaxarea) 1015 continue; 1016 if (rw * rh == 1) 1017 continue; 1018 for (x = max(sx - rw + 1, 0); x <= min(sx, params->w - rw); x++) 1019 for (y = max(sy - rh + 1, 0); y <= min(sy, params->h - rh); 1020 y++) { 1021 /* 1022 * Check this rectangle against the region we 1023 * defined above. 1024 */ 1025 if (top[x] <= y && top[x+rw-1] <= y && 1026 bottom[x] >= y+rh-1 && bottom[x+rw-1] >= y+rh-1) { 1027 if (r && index == *n) { 1028 r->x = x; 1029 r->y = y; 1030 r->w = rw; 1031 r->h = rh; 1032 return; 1033 } 1034 index++; 1035 } 1036 } 1037 } 1038 1039 assert(!r); 1040 *n = index; 1041} 1042 1043static void place_rect(game_params *params, int *grid, struct rect r) 1044{ 1045 int idx = INDEX(params, r.x, r.y); 1046 int x, y; 1047 1048 for (x = r.x; x < r.x+r.w; x++) 1049 for (y = r.y; y < r.y+r.h; y++) { 1050 index(params, grid, x, y) = idx; 1051 } 1052#ifdef GENERATION_DIAGNOSTICS 1053 printf(" placing rectangle at (%d,%d) size %d x %d\n", 1054 r.x, r.y, r.w, r.h); 1055#endif 1056} 1057 1058static struct rect find_rect(game_params *params, int *grid, int x, int y) 1059{ 1060 int idx, w, h; 1061 struct rect r; 1062 1063 /* 1064 * Find the top left of the rectangle. 1065 */ 1066 idx = index(params, grid, x, y); 1067 1068 if (idx < 0) { 1069 r.x = x; 1070 r.y = y; 1071 r.w = r.h = 1; 1072 return r; /* 1x1 singleton here */ 1073 } 1074 1075 y = idx / params->w; 1076 x = idx % params->w; 1077 1078 /* 1079 * Find the width and height of the rectangle. 1080 */ 1081 for (w = 1; 1082 (x+w < params->w && index(params,grid,x+w,y)==idx); 1083 w++); 1084 for (h = 1; 1085 (y+h < params->h && index(params,grid,x,y+h)==idx); 1086 h++); 1087 1088 r.x = x; 1089 r.y = y; 1090 r.w = w; 1091 r.h = h; 1092 1093 return r; 1094} 1095 1096#ifdef GENERATION_DIAGNOSTICS 1097static void display_grid(game_params *params, int *grid, int *numbers, int all) 1098{ 1099 unsigned char *egrid = snewn((params->w*2+3) * (params->h*2+3), 1100 unsigned char); 1101 int x, y; 1102 int r = (params->w*2+3); 1103 1104 memset(egrid, 0, (params->w*2+3) * (params->h*2+3)); 1105 1106 for (x = 0; x < params->w; x++) 1107 for (y = 0; y < params->h; y++) { 1108 int i = index(params, grid, x, y); 1109 if (x == 0 || index(params, grid, x-1, y) != i) 1110 egrid[(2*y+2) * r + (2*x+1)] = 1; 1111 if (x == params->w-1 || index(params, grid, x+1, y) != i) 1112 egrid[(2*y+2) * r + (2*x+3)] = 1; 1113 if (y == 0 || index(params, grid, x, y-1) != i) 1114 egrid[(2*y+1) * r + (2*x+2)] = 1; 1115 if (y == params->h-1 || index(params, grid, x, y+1) != i) 1116 egrid[(2*y+3) * r + (2*x+2)] = 1; 1117 } 1118 1119 for (y = 1; y < 2*params->h+2; y++) { 1120 for (x = 1; x < 2*params->w+2; x++) { 1121 if (!((y|x)&1)) { 1122 int k = numbers ? index(params, numbers, x/2-1, y/2-1) : 0; 1123 if (k || (all && numbers)) printf("%2d", k); else printf(" "); 1124 } else if (!((y&x)&1)) { 1125 int v = egrid[y*r+x]; 1126 if ((y&1) && v) v = '-'; 1127 if ((x&1) && v) v = '|'; 1128 if (!v) v = ' '; 1129 putchar(v); 1130 if (!(x&1)) putchar(v); 1131 } else { 1132 int c, d = 0; 1133 if (egrid[y*r+(x+1)]) d |= 1; 1134 if (egrid[(y-1)*r+x]) d |= 2; 1135 if (egrid[y*r+(x-1)]) d |= 4; 1136 if (egrid[(y+1)*r+x]) d |= 8; 1137 c = " ??+?-++?+|+++++"[d]; 1138 putchar(c); 1139 if (!(x&1)) putchar(c); 1140 } 1141 } 1142 putchar('\n'); 1143 } 1144 1145 sfree(egrid); 1146} 1147#endif 1148 1149static char *new_game_desc(const game_params *params_in, random_state *rs, 1150 char **aux, bool interactive) 1151{ 1152 game_params params_copy = *params_in; /* structure copy */ 1153 game_params *params = &params_copy; 1154 int *grid, *numbers = NULL; 1155 int x, y, y2, y2last, yx, run, i, nsquares; 1156 char *desc, *p; 1157 int *enum_rects_scratch; 1158 game_params params2real, *params2 = &params2real; 1159 1160 while (1) { 1161 /* 1162 * Set up the smaller width and height which we will use to 1163 * generate the base grid. 1164 */ 1165 params2->w = (int)((float)params->w / (1.0F + params->expandfactor)); 1166 if (params2->w < 2 && params->w >= 2) params2->w = 2; 1167 params2->h = (int)((float)params->h / (1.0F + params->expandfactor)); 1168 if (params2->h < 2 && params->h >= 2) params2->h = 2; 1169 1170 grid = snewn(params2->w * params2->h, int); 1171 1172 enum_rects_scratch = snewn(2 * params2->w, int); 1173 1174 nsquares = 0; 1175 for (y = 0; y < params2->h; y++) 1176 for (x = 0; x < params2->w; x++) { 1177 index(params2, grid, x, y) = -1; 1178 nsquares++; 1179 } 1180 1181 /* 1182 * Place rectangles until we can't any more. We do this by 1183 * finding a square we haven't yet covered, and randomly 1184 * choosing a rectangle to cover it. 1185 */ 1186 1187 while (nsquares > 0) { 1188 int square = random_upto(rs, nsquares); 1189 int n; 1190 struct rect r; 1191 1192 x = params2->w; 1193 y = params2->h; 1194 for (y = 0; y < params2->h; y++) { 1195 for (x = 0; x < params2->w; x++) { 1196 if (index(params2, grid, x, y) == -1 && square-- == 0) 1197 break; 1198 } 1199 if (x < params2->w) 1200 break; 1201 } 1202 assert(x < params2->w && y < params2->h); 1203 1204 /* 1205 * Now see how many rectangles fit around this one. 1206 */ 1207 enum_rects(params2, grid, NULL, &n, x, y, enum_rects_scratch); 1208 1209 if (!n) { 1210 /* 1211 * There are no possible rectangles covering this 1212 * square, meaning it must be a singleton. Mark it 1213 * -2 so we know not to keep trying. 1214 */ 1215 index(params2, grid, x, y) = -2; 1216 nsquares--; 1217 } else { 1218 /* 1219 * Pick one at random. 1220 */ 1221 n = random_upto(rs, n); 1222 enum_rects(params2, grid, &r, &n, x, y, enum_rects_scratch); 1223 1224 /* 1225 * Place it. 1226 */ 1227 place_rect(params2, grid, r); 1228 nsquares -= r.w * r.h; 1229 } 1230 } 1231 1232 sfree(enum_rects_scratch); 1233 1234 /* 1235 * Deal with singleton spaces remaining in the grid, one by 1236 * one. 1237 * 1238 * We do this by making a local change to the layout. There are 1239 * several possibilities: 1240 * 1241 * +-----+-----+ Here, we can remove the singleton by 1242 * | | | extending the 1x2 rectangle below it 1243 * +--+--+-----+ into a 1x3. 1244 * | | | | 1245 * | +--+ | 1246 * | | | | 1247 * | | | | 1248 * | | | | 1249 * +--+--+-----+ 1250 * 1251 * +--+--+--+ Here, that trick doesn't work: there's no 1252 * | | | 1 x n rectangle with the singleton at one 1253 * | | | end. Instead, we extend a 1 x n rectangle 1254 * | | | _out_ from the singleton, shaving a layer 1255 * +--+--+ | off the end of another rectangle. So if we 1256 * | | | | extended up, we'd make our singleton part 1257 * | +--+--+ of a 1x3 and generate a 1x2 where the 2x2 1258 * | | | used to be; or we could extend right into 1259 * +--+-----+ a 2x1, turning the 1x3 into a 1x2. 1260 * 1261 * +-----+--+ Here, we can't even do _that_, since any 1262 * | | | direction we choose to extend the singleton 1263 * +--+--+ | will produce a new singleton as a result of 1264 * | | | | truncating one of the size-2 rectangles. 1265 * | +--+--+ Fortunately, this case can _only_ occur when 1266 * | | | a singleton is surrounded by four size-2s 1267 * +--+-----+ in this fashion; so instead we can simply 1268 * replace the whole section with a single 3x3. 1269 */ 1270 for (x = 0; x < params2->w; x++) { 1271 for (y = 0; y < params2->h; y++) { 1272 if (index(params2, grid, x, y) < 0) { 1273 int dirs[4], ndirs; 1274 1275#ifdef GENERATION_DIAGNOSTICS 1276 display_grid(params2, grid, NULL, false); 1277 printf("singleton at %d,%d\n", x, y); 1278#endif 1279 1280 /* 1281 * Check in which directions we can feasibly extend 1282 * the singleton. We can extend in a particular 1283 * direction iff either: 1284 * 1285 * - the rectangle on that side of the singleton 1286 * is not 2x1, and we are at one end of the edge 1287 * of it we are touching 1288 * 1289 * - it is 2x1 but we are on its short side. 1290 * 1291 * FIXME: we could plausibly choose between these 1292 * based on the sizes of the rectangles they would 1293 * create? 1294 */ 1295 ndirs = 0; 1296 if (x < params2->w-1) { 1297 struct rect r = find_rect(params2, grid, x+1, y); 1298 if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1) 1299 dirs[ndirs++] = 1; /* right */ 1300 } 1301 if (y > 0) { 1302 struct rect r = find_rect(params2, grid, x, y-1); 1303 if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1) 1304 dirs[ndirs++] = 2; /* up */ 1305 } 1306 if (x > 0) { 1307 struct rect r = find_rect(params2, grid, x-1, y); 1308 if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1) 1309 dirs[ndirs++] = 4; /* left */ 1310 } 1311 if (y < params2->h-1) { 1312 struct rect r = find_rect(params2, grid, x, y+1); 1313 if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1) 1314 dirs[ndirs++] = 8; /* down */ 1315 } 1316 1317 if (ndirs > 0) { 1318 int which, dir; 1319 struct rect r1, r2; 1320 memset(&r1, 0, sizeof(struct rect)); 1321 memset(&r2, 0, sizeof(struct rect)); 1322 which = random_upto(rs, ndirs); 1323 dir = dirs[which]; 1324 1325 switch (dir) { 1326 case 1: /* right */ 1327 assert(x < params2->w+1); 1328#ifdef GENERATION_DIAGNOSTICS 1329 printf("extending right\n"); 1330#endif 1331 r1 = find_rect(params2, grid, x+1, y); 1332 r2.x = x; 1333 r2.y = y; 1334 r2.w = 1 + r1.w; 1335 r2.h = 1; 1336 if (r1.y == y) 1337 r1.y++; 1338 r1.h--; 1339 break; 1340 case 2: /* up */ 1341 assert(y > 0); 1342#ifdef GENERATION_DIAGNOSTICS 1343 printf("extending up\n"); 1344#endif 1345 r1 = find_rect(params2, grid, x, y-1); 1346 r2.x = x; 1347 r2.y = r1.y; 1348 r2.w = 1; 1349 r2.h = 1 + r1.h; 1350 if (r1.x == x) 1351 r1.x++; 1352 r1.w--; 1353 break; 1354 case 4: /* left */ 1355 assert(x > 0); 1356#ifdef GENERATION_DIAGNOSTICS 1357 printf("extending left\n"); 1358#endif 1359 r1 = find_rect(params2, grid, x-1, y); 1360 r2.x = r1.x; 1361 r2.y = y; 1362 r2.w = 1 + r1.w; 1363 r2.h = 1; 1364 if (r1.y == y) 1365 r1.y++; 1366 r1.h--; 1367 break; 1368 case 8: /* down */ 1369 assert(y < params2->h+1); 1370#ifdef GENERATION_DIAGNOSTICS 1371 printf("extending down\n"); 1372#endif 1373 r1 = find_rect(params2, grid, x, y+1); 1374 r2.x = x; 1375 r2.y = y; 1376 r2.w = 1; 1377 r2.h = 1 + r1.h; 1378 if (r1.x == x) 1379 r1.x++; 1380 r1.w--; 1381 break; 1382 default: /* should never happen */ 1383 assert(!"invalid direction"); 1384 } 1385 if (r1.h > 0 && r1.w > 0) 1386 place_rect(params2, grid, r1); 1387 place_rect(params2, grid, r2); 1388 } else { 1389#ifndef NDEBUG 1390 /* 1391 * Sanity-check that there really is a 3x3 1392 * rectangle surrounding this singleton and it 1393 * contains absolutely everything we could 1394 * possibly need. 1395 */ 1396 { 1397 int xx, yy; 1398 assert(x > 0 && x < params2->w-1); 1399 assert(y > 0 && y < params2->h-1); 1400 1401 for (xx = x-1; xx <= x+1; xx++) 1402 for (yy = y-1; yy <= y+1; yy++) { 1403 struct rect r = find_rect(params2,grid,xx,yy); 1404 assert(r.x >= x-1); 1405 assert(r.y >= y-1); 1406 assert(r.x+r.w-1 <= x+1); 1407 assert(r.y+r.h-1 <= y+1); 1408 } 1409 } 1410#endif 1411 1412#ifdef GENERATION_DIAGNOSTICS 1413 printf("need the 3x3 trick\n"); 1414#endif 1415 1416 /* 1417 * FIXME: If the maximum rectangle area for 1418 * this grid is less than 9, we ought to 1419 * subdivide the 3x3 in some fashion. There are 1420 * five other possibilities: 1421 * 1422 * - a 6 and a 3 1423 * - a 4, a 3 and a 2 1424 * - three 3s 1425 * - a 3 and three 2s (two different arrangements). 1426 */ 1427 1428 { 1429 struct rect r; 1430 r.x = x-1; 1431 r.y = y-1; 1432 r.w = r.h = 3; 1433 place_rect(params2, grid, r); 1434 } 1435 } 1436 } 1437 } 1438 } 1439 1440 /* 1441 * We have now constructed a grid of the size specified in 1442 * params2. Now we extend it into a grid of the size specified 1443 * in params. We do this in two passes: we extend it vertically 1444 * until it's the right height, then we transpose it, then 1445 * extend it vertically again (getting it effectively the right 1446 * width), then finally transpose again. 1447 */ 1448 for (i = 0; i < 2; i++) { 1449 int *grid2, *expand, *where; 1450 game_params params3real, *params3 = &params3real; 1451 1452#ifdef GENERATION_DIAGNOSTICS 1453 printf("before expansion:\n"); 1454 display_grid(params2, grid, NULL, true); 1455#endif 1456 1457 /* 1458 * Set up the new grid. 1459 */ 1460 grid2 = snewn(params2->w * params->h, int); 1461 expand = snewn(params2->h-1, int); 1462 where = snewn(params2->w, int); 1463 params3->w = params2->w; 1464 params3->h = params->h; 1465 1466 /* 1467 * Decide which horizontal edges are going to get expanded, 1468 * and by how much. 1469 */ 1470 for (y = 0; y < params2->h-1; y++) 1471 expand[y] = 0; 1472 for (y = params2->h; y < params->h; y++) { 1473 x = random_upto(rs, params2->h-1); 1474 expand[x]++; 1475 } 1476 1477#ifdef GENERATION_DIAGNOSTICS 1478 printf("expand[] = {"); 1479 for (y = 0; y < params2->h-1; y++) 1480 printf(" %d", expand[y]); 1481 printf(" }\n"); 1482#endif 1483 1484 /* 1485 * Perform the expansion. The way this works is that we 1486 * alternately: 1487 * 1488 * - copy a row from grid into grid2 1489 * 1490 * - invent some number of additional rows in grid2 where 1491 * there was previously only a horizontal line between 1492 * rows in grid, and make random decisions about where 1493 * among these to place each rectangle edge that ran 1494 * along this line. 1495 */ 1496 for (y = y2 = y2last = 0; y < params2->h; y++) { 1497 /* 1498 * Copy a single line from row y of grid into row y2 of 1499 * grid2. 1500 */ 1501 for (x = 0; x < params2->w; x++) { 1502 int val = index(params2, grid, x, y); 1503 if (val / params2->w == y && /* rect starts on this line */ 1504 (y2 == 0 || /* we're at the very top, or... */ 1505 index(params3, grid2, x, y2-1) / params3->w < y2last 1506 /* this rect isn't already started */)) 1507 index(params3, grid2, x, y2) = 1508 INDEX(params3, val % params2->w, y2); 1509 else 1510 index(params3, grid2, x, y2) = 1511 index(params3, grid2, x, y2-1); 1512 } 1513 1514 /* 1515 * If that was the last line, terminate the loop early. 1516 */ 1517 if (++y2 == params3->h) 1518 break; 1519 1520 y2last = y2; 1521 1522 /* 1523 * Invent some number of additional lines. First walk 1524 * along this line working out where to put all the 1525 * edges that coincide with it. 1526 */ 1527 yx = -1; 1528 for (x = 0; x < params2->w; x++) { 1529 if (index(params2, grid, x, y) != 1530 index(params2, grid, x, y+1)) { 1531 /* 1532 * This is a horizontal edge, so it needs 1533 * placing. 1534 */ 1535 if (x == 0 || 1536 (index(params2, grid, x-1, y) != 1537 index(params2, grid, x, y) && 1538 index(params2, grid, x-1, y+1) != 1539 index(params2, grid, x, y+1))) { 1540 /* 1541 * Here we have the chance to make a new 1542 * decision. 1543 */ 1544 yx = random_upto(rs, expand[y]+1); 1545 } else { 1546 /* 1547 * Here we just reuse the previous value of 1548 * yx. 1549 */ 1550 } 1551 } else 1552 yx = -1; 1553 where[x] = yx; 1554 } 1555 1556 for (yx = 0; yx < expand[y]; yx++) { 1557 /* 1558 * Invent a single row. For each square in the row, 1559 * we copy the grid entry from the square above it, 1560 * unless we're starting the new rectangle here. 1561 */ 1562 for (x = 0; x < params2->w; x++) { 1563 if (yx == where[x]) { 1564 int val = index(params2, grid, x, y+1); 1565 val %= params2->w; 1566 val = INDEX(params3, val, y2); 1567 index(params3, grid2, x, y2) = val; 1568 } else 1569 index(params3, grid2, x, y2) = 1570 index(params3, grid2, x, y2-1); 1571 } 1572 1573 y2++; 1574 } 1575 } 1576 1577 sfree(expand); 1578 sfree(where); 1579 1580#ifdef GENERATION_DIAGNOSTICS 1581 printf("after expansion:\n"); 1582 display_grid(params3, grid2, NULL, true); 1583#endif 1584 /* 1585 * Transpose. 1586 */ 1587 params2->w = params3->h; 1588 params2->h = params3->w; 1589 sfree(grid); 1590 grid = snewn(params2->w * params2->h, int); 1591 for (x = 0; x < params2->w; x++) 1592 for (y = 0; y < params2->h; y++) { 1593 int idx1 = INDEX(params2, x, y); 1594 int idx2 = INDEX(params3, y, x); 1595 int tmp; 1596 1597 tmp = grid2[idx2]; 1598 tmp = (tmp % params3->w) * params2->w + (tmp / params3->w); 1599 grid[idx1] = tmp; 1600 } 1601 1602 sfree(grid2); 1603 1604 { 1605 int tmp; 1606 tmp = params->w; 1607 params->w = params->h; 1608 params->h = tmp; 1609 } 1610 1611#ifdef GENERATION_DIAGNOSTICS 1612 printf("after transposition:\n"); 1613 display_grid(params2, grid, NULL, true); 1614#endif 1615 } 1616 1617 /* 1618 * Run the solver to narrow down the possible number 1619 * placements. 1620 */ 1621 { 1622 struct numberdata *nd; 1623 int nnumbers, i, ret; 1624 1625 /* Count the rectangles. */ 1626 nnumbers = 0; 1627 for (y = 0; y < params->h; y++) { 1628 for (x = 0; x < params->w; x++) { 1629 int idx = INDEX(params, x, y); 1630 if (index(params, grid, x, y) == idx) 1631 nnumbers++; 1632 } 1633 } 1634 1635 nd = snewn(nnumbers, struct numberdata); 1636 1637 /* Now set up each number's candidate position list. */ 1638 i = 0; 1639 for (y = 0; y < params->h; y++) { 1640 for (x = 0; x < params->w; x++) { 1641 int idx = INDEX(params, x, y); 1642 if (index(params, grid, x, y) == idx) { 1643 struct rect r = find_rect(params, grid, x, y); 1644 int j, k, m; 1645 1646 nd[i].area = r.w * r.h; 1647 nd[i].npoints = nd[i].area; 1648 nd[i].points = snewn(nd[i].npoints, struct point); 1649 m = 0; 1650 for (j = 0; j < r.h; j++) 1651 for (k = 0; k < r.w; k++) { 1652 nd[i].points[m].x = k + r.x; 1653 nd[i].points[m].y = j + r.y; 1654 m++; 1655 } 1656 assert(m == nd[i].npoints); 1657 1658 i++; 1659 } 1660 } 1661 } 1662 1663 if (params->unique) 1664 ret = rect_solver(params->w, params->h, nnumbers, nd, 1665 NULL, NULL, rs); 1666 else 1667 ret = 1; /* allow any number placement at all */ 1668 1669 if (ret == 1) { 1670 /* 1671 * Now place the numbers according to the solver's 1672 * recommendations. 1673 */ 1674 numbers = snewn(params->w * params->h, int); 1675 1676 for (y = 0; y < params->h; y++) 1677 for (x = 0; x < params->w; x++) { 1678 index(params, numbers, x, y) = 0; 1679 } 1680 1681 for (i = 0; i < nnumbers; i++) { 1682 int idx = random_upto(rs, nd[i].npoints); 1683 int x = nd[i].points[idx].x; 1684 int y = nd[i].points[idx].y; 1685 index(params,numbers,x,y) = nd[i].area; 1686 } 1687 } 1688 1689 /* 1690 * Clean up. 1691 */ 1692 for (i = 0; i < nnumbers; i++) 1693 sfree(nd[i].points); 1694 sfree(nd); 1695 1696 /* 1697 * If we've succeeded, then terminate the loop. 1698 */ 1699 if (ret == 1) 1700 break; 1701 } 1702 1703 /* 1704 * Give up and go round again. 1705 */ 1706 sfree(grid); 1707 } 1708 1709 /* 1710 * Store the solution in aux. 1711 */ 1712 { 1713 char *ai; 1714 int len; 1715 1716 len = 2 + (params->w-1)*params->h + (params->h-1)*params->w; 1717 ai = snewn(len, char); 1718 1719 ai[0] = 'S'; 1720 1721 p = ai+1; 1722 1723 for (y = 0; y < params->h; y++) 1724 for (x = 1; x < params->w; x++) 1725 *p++ = (index(params, grid, x, y) != 1726 index(params, grid, x-1, y) ? '1' : '0'); 1727 1728 for (y = 1; y < params->h; y++) 1729 for (x = 0; x < params->w; x++) 1730 *p++ = (index(params, grid, x, y) != 1731 index(params, grid, x, y-1) ? '1' : '0'); 1732 1733 assert(p - ai == len-1); 1734 *p = '\0'; 1735 1736 *aux = ai; 1737 } 1738 1739#ifdef GENERATION_DIAGNOSTICS 1740 display_grid(params, grid, numbers, false); 1741#endif 1742 1743 desc = snewn(11 * params->w * params->h, char); 1744 p = desc; 1745 run = 0; 1746 for (i = 0; i <= params->w * params->h; i++) { 1747 int n = (i < params->w * params->h ? numbers[i] : -1); 1748 1749 if (!n) 1750 run++; 1751 else { 1752 if (run) { 1753 while (run > 0) { 1754 int c = 'a' - 1 + run; 1755 if (run > 26) 1756 c = 'z'; 1757 *p++ = c; 1758 run -= c - ('a' - 1); 1759 } 1760 } else { 1761 /* 1762 * If there's a number in the very top left or 1763 * bottom right, there's no point putting an 1764 * unnecessary _ before or after it. 1765 */ 1766 if (p > desc && n > 0) 1767 *p++ = '_'; 1768 } 1769 if (n > 0) 1770 p += sprintf(p, "%d", n); 1771 run = 0; 1772 } 1773 } 1774 *p = '\0'; 1775 1776 sfree(grid); 1777 sfree(numbers); 1778 1779 return desc; 1780} 1781 1782static const char *validate_desc(const game_params *params, const char *desc) 1783{ 1784 int area = params->w * params->h; 1785 int squares = 0; 1786 1787 while (*desc) { 1788 int n = *desc++; 1789 if (n >= 'a' && n <= 'z') { 1790 squares += n - 'a' + 1; 1791 } else if (n == '_') { 1792 /* do nothing */; 1793 } else if (n > '0' && n <= '9') { 1794 squares++; 1795 while (*desc >= '0' && *desc <= '9') 1796 desc++; 1797 } else 1798 return "Invalid character in game description"; 1799 } 1800 1801 if (squares < area) 1802 return "Not enough data to fill grid"; 1803 1804 if (squares > area) 1805 return "Too much data to fit in grid"; 1806 1807 return NULL; 1808} 1809 1810static unsigned char *get_correct(game_state *state) 1811{ 1812 unsigned char *ret; 1813 int x, y; 1814 1815 ret = snewn(state->w * state->h, unsigned char); 1816 memset(ret, 0xFF, state->w * state->h); 1817 1818 for (x = 0; x < state->w; x++) 1819 for (y = 0; y < state->h; y++) 1820 if (index(state,ret,x,y) == 0xFF) { 1821 int rw, rh; 1822 int xx, yy; 1823 int num, area; 1824 bool valid; 1825 1826 /* 1827 * Find a rectangle starting at this point. 1828 */ 1829 rw = 1; 1830 while (x+rw < state->w && !vedge(state,x+rw,y)) 1831 rw++; 1832 rh = 1; 1833 while (y+rh < state->h && !hedge(state,x,y+rh)) 1834 rh++; 1835 1836 /* 1837 * We know what the dimensions of the rectangle 1838 * should be if it's there at all. Find out if we 1839 * really have a valid rectangle. 1840 */ 1841 valid = true; 1842 /* Check the horizontal edges. */ 1843 for (xx = x; xx < x+rw; xx++) { 1844 for (yy = y; yy <= y+rh; yy++) { 1845 int e = !HRANGE(state,xx,yy) || hedge(state,xx,yy); 1846 int ec = (yy == y || yy == y+rh); 1847 if (e != ec) 1848 valid = false; 1849 } 1850 } 1851 /* Check the vertical edges. */ 1852 for (yy = y; yy < y+rh; yy++) { 1853 for (xx = x; xx <= x+rw; xx++) { 1854 int e = !VRANGE(state,xx,yy) || vedge(state,xx,yy); 1855 int ec = (xx == x || xx == x+rw); 1856 if (e != ec) 1857 valid = false; 1858 } 1859 } 1860 1861 /* 1862 * If this is not a valid rectangle with no other 1863 * edges inside it, we just mark this square as not 1864 * complete and proceed to the next square. 1865 */ 1866 if (!valid) { 1867 index(state, ret, x, y) = 0; 1868 continue; 1869 } 1870 1871 /* 1872 * We have a rectangle. Now see what its area is, 1873 * and how many numbers are in it. 1874 */ 1875 num = 0; 1876 area = 0; 1877 for (xx = x; xx < x+rw; xx++) { 1878 for (yy = y; yy < y+rh; yy++) { 1879 area++; 1880 if (grid(state,xx,yy)) { 1881 if (num > 0) 1882 valid = false; /* two numbers */ 1883 num = grid(state,xx,yy); 1884 } 1885 } 1886 } 1887 if (num != area) 1888 valid = false; 1889 1890 /* 1891 * Now fill in the whole rectangle based on the 1892 * value of `valid'. 1893 */ 1894 for (xx = x; xx < x+rw; xx++) { 1895 for (yy = y; yy < y+rh; yy++) { 1896 index(state, ret, xx, yy) = valid; 1897 } 1898 } 1899 } 1900 1901 return ret; 1902} 1903 1904static game_state *new_game(midend *me, const game_params *params, 1905 const char *desc) 1906{ 1907 game_state *state = snew(game_state); 1908 int x, y, i, area; 1909 1910 state->w = params->w; 1911 state->h = params->h; 1912 1913 area = state->w * state->h; 1914 1915 state->grid = snewn(area, int); 1916 state->vedge = snewn(area, unsigned char); 1917 state->hedge = snewn(area, unsigned char); 1918 state->completed = false; 1919 state->cheated = false; 1920 1921 i = 0; 1922 while (*desc) { 1923 int n = *desc++; 1924 if (n >= 'a' && n <= 'z') { 1925 int run = n - 'a' + 1; 1926 assert(i + run <= area); 1927 while (run-- > 0) 1928 state->grid[i++] = 0; 1929 } else if (n == '_') { 1930 /* do nothing */; 1931 } else if (n > '0' && n <= '9') { 1932 assert(i < area); 1933 state->grid[i++] = atoi(desc-1); 1934 while (*desc >= '0' && *desc <= '9') 1935 desc++; 1936 } else { 1937 assert(!"We can't get here"); 1938 } 1939 } 1940 assert(i == area); 1941 1942 for (y = 0; y < state->h; y++) 1943 for (x = 0; x < state->w; x++) 1944 vedge(state,x,y) = hedge(state,x,y) = 0; 1945 1946 state->correct = get_correct(state); 1947 1948 return state; 1949} 1950 1951static game_state *dup_game(const game_state *state) 1952{ 1953 game_state *ret = snew(game_state); 1954 1955 ret->w = state->w; 1956 ret->h = state->h; 1957 1958 ret->vedge = snewn(state->w * state->h, unsigned char); 1959 ret->hedge = snewn(state->w * state->h, unsigned char); 1960 ret->grid = snewn(state->w * state->h, int); 1961 ret->correct = snewn(ret->w * ret->h, unsigned char); 1962 1963 ret->completed = state->completed; 1964 ret->cheated = state->cheated; 1965 1966 memcpy(ret->grid, state->grid, state->w * state->h * sizeof(int)); 1967 memcpy(ret->vedge, state->vedge, state->w*state->h*sizeof(unsigned char)); 1968 memcpy(ret->hedge, state->hedge, state->w*state->h*sizeof(unsigned char)); 1969 1970 memcpy(ret->correct, state->correct, state->w*state->h*sizeof(unsigned char)); 1971 1972 return ret; 1973} 1974 1975static void free_game(game_state *state) 1976{ 1977 sfree(state->grid); 1978 sfree(state->vedge); 1979 sfree(state->hedge); 1980 sfree(state->correct); 1981 sfree(state); 1982} 1983 1984static char *solve_game(const game_state *state, const game_state *currstate, 1985 const char *ai, const char **error) 1986{ 1987 unsigned char *vedge, *hedge; 1988 int x, y, len; 1989 char *ret, *p; 1990 int i, j, n; 1991 struct numberdata *nd; 1992 1993 if (ai) 1994 return dupstr(ai); 1995 1996 /* 1997 * Attempt the in-built solver. 1998 */ 1999 2000 /* Set up each number's (very short) candidate position list. */ 2001 for (i = n = 0; i < state->h * state->w; i++) 2002 if (state->grid[i]) 2003 n++; 2004 2005 nd = snewn(n, struct numberdata); 2006 2007 for (i = j = 0; i < state->h * state->w; i++) 2008 if (state->grid[i]) { 2009 nd[j].area = state->grid[i]; 2010 nd[j].npoints = 1; 2011 nd[j].points = snewn(1, struct point); 2012 nd[j].points[0].x = i % state->w; 2013 nd[j].points[0].y = i / state->w; 2014 j++; 2015 } 2016 2017 assert(j == n); 2018 2019 vedge = snewn(state->w * state->h, unsigned char); 2020 hedge = snewn(state->w * state->h, unsigned char); 2021 memset(vedge, 0, state->w * state->h); 2022 memset(hedge, 0, state->w * state->h); 2023 2024 rect_solver(state->w, state->h, n, nd, hedge, vedge, NULL); 2025 2026 /* 2027 * Clean up. 2028 */ 2029 for (i = 0; i < n; i++) 2030 sfree(nd[i].points); 2031 sfree(nd); 2032 2033 len = 2 + (state->w-1)*state->h + (state->h-1)*state->w; 2034 ret = snewn(len, char); 2035 2036 p = ret; 2037 *p++ = 'S'; 2038 for (y = 0; y < state->h; y++) 2039 for (x = 1; x < state->w; x++) 2040 *p++ = vedge[y*state->w+x] ? '1' : '0'; 2041 for (y = 1; y < state->h; y++) 2042 for (x = 0; x < state->w; x++) 2043 *p++ = hedge[y*state->w+x] ? '1' : '0'; 2044 *p++ = '\0'; 2045 assert(p - ret == len); 2046 2047 sfree(vedge); 2048 sfree(hedge); 2049 2050 return ret; 2051} 2052 2053static bool game_can_format_as_text_now(const game_params *params) 2054{ 2055 return true; 2056} 2057 2058static char *game_text_format(const game_state *state) 2059{ 2060 char *ret, *p, buf[80]; 2061 int i, x, y, col, maxlen; 2062 2063 /* 2064 * First determine the number of spaces required to display a 2065 * number. We'll use at least two, because one looks a bit 2066 * silly. 2067 */ 2068 col = 2; 2069 for (i = 0; i < state->w * state->h; i++) { 2070 x = sprintf(buf, "%d", state->grid[i]); 2071 if (col < x) col = x; 2072 } 2073 2074 /* 2075 * Now we know the exact total size of the grid we're going to 2076 * produce: it's got 2*h+1 rows, each containing w lots of col, 2077 * w+1 boundary characters and a trailing newline. 2078 */ 2079 maxlen = (2*state->h+1) * (state->w * (col+1) + 2); 2080 2081 ret = snewn(maxlen+1, char); 2082 p = ret; 2083 2084 for (y = 0; y <= 2*state->h; y++) { 2085 for (x = 0; x <= 2*state->w; x++) { 2086 if (x & y & 1) { 2087 /* 2088 * Display a number. 2089 */ 2090 int v = grid(state, x/2, y/2); 2091 if (v) 2092 sprintf(buf, "%*d", col, v); 2093 else 2094 sprintf(buf, "%*s", col, ""); 2095 memcpy(p, buf, col); 2096 p += col; 2097 } else if (x & 1) { 2098 /* 2099 * Display a horizontal edge or nothing. 2100 */ 2101 int h = (y==0 || y==2*state->h ? 1 : 2102 HRANGE(state, x/2, y/2) && hedge(state, x/2, y/2)); 2103 int i; 2104 if (h) 2105 h = '-'; 2106 else 2107 h = ' '; 2108 for (i = 0; i < col; i++) 2109 *p++ = h; 2110 } else if (y & 1) { 2111 /* 2112 * Display a vertical edge or nothing. 2113 */ 2114 int v = (x==0 || x==2*state->w ? 1 : 2115 VRANGE(state, x/2, y/2) && vedge(state, x/2, y/2)); 2116 if (v) 2117 *p++ = '|'; 2118 else 2119 *p++ = ' '; 2120 } else { 2121 /* 2122 * Display a corner, or a vertical edge, or a 2123 * horizontal edge, or nothing. 2124 */ 2125 int hl = (y==0 || y==2*state->h ? 1 : 2126 HRANGE(state, (x-1)/2, y/2) && hedge(state, (x-1)/2, y/2)); 2127 int hr = (y==0 || y==2*state->h ? 1 : 2128 HRANGE(state, (x+1)/2, y/2) && hedge(state, (x+1)/2, y/2)); 2129 int vu = (x==0 || x==2*state->w ? 1 : 2130 VRANGE(state, x/2, (y-1)/2) && vedge(state, x/2, (y-1)/2)); 2131 int vd = (x==0 || x==2*state->w ? 1 : 2132 VRANGE(state, x/2, (y+1)/2) && vedge(state, x/2, (y+1)/2)); 2133 if (!hl && !hr && !vu && !vd) 2134 *p++ = ' '; 2135 else if (hl && hr && !vu && !vd) 2136 *p++ = '-'; 2137 else if (!hl && !hr && vu && vd) 2138 *p++ = '|'; 2139 else 2140 *p++ = '+'; 2141 } 2142 } 2143 *p++ = '\n'; 2144 } 2145 2146 assert(p - ret == maxlen); 2147 *p = '\0'; 2148 return ret; 2149} 2150 2151struct game_ui { 2152 /* 2153 * These coordinates are 2 times the obvious grid coordinates. 2154 * Hence, the top left of the grid is (0,0), the grid point to 2155 * the right of that is (2,0), the one _below that_ is (2,2) 2156 * and so on. This is so that we can specify a drag start point 2157 * on an edge (one odd coordinate) or in the middle of a square 2158 * (two odd coordinates) rather than always at a corner. 2159 * 2160 * -1,-1 means no drag is in progress. 2161 */ 2162 int drag_start_x; 2163 int drag_start_y; 2164 int drag_end_x; 2165 int drag_end_y; 2166 /* 2167 * This flag is set as soon as a dragging action moves the 2168 * mouse pointer away from its starting point, so that even if 2169 * the pointer _returns_ to its starting point the action is 2170 * treated as a small drag rather than a click. 2171 */ 2172 bool dragged; 2173 /* This flag is set if we're doing an erase operation (i.e. 2174 * removing edges in the centre of the rectangle without altering 2175 * the outlines). 2176 */ 2177 bool erasing; 2178 /* 2179 * These are the co-ordinates of the top-left and bottom-right squares 2180 * in the drag box, respectively, or -1 otherwise. 2181 */ 2182 int x1; 2183 int y1; 2184 int x2; 2185 int y2; 2186 /* 2187 * These are the coordinates of a cursor, whether it's visible, and 2188 * whether it was used to start a drag. 2189 */ 2190 int cur_x, cur_y; 2191 bool cur_visible, cur_dragging; 2192}; 2193 2194static void reset_ui(game_ui *ui) 2195{ 2196 ui->drag_start_x = -1; 2197 ui->drag_start_y = -1; 2198 ui->drag_end_x = -1; 2199 ui->drag_end_y = -1; 2200 ui->x1 = -1; 2201 ui->y1 = -1; 2202 ui->x2 = -1; 2203 ui->y2 = -1; 2204 ui->dragged = false; 2205} 2206 2207static game_ui *new_ui(const game_state *state) 2208{ 2209 game_ui *ui = snew(game_ui); 2210 reset_ui(ui); 2211 ui->erasing = false; 2212 ui->cur_x = ui->cur_y = 0; 2213 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); 2214 ui->cur_dragging = false; 2215 return ui; 2216} 2217 2218static void free_ui(game_ui *ui) 2219{ 2220 sfree(ui); 2221} 2222 2223static void coord_round(float x, float y, int *xr, int *yr) 2224{ 2225 float xs, ys, xv, yv, dx, dy, dist; 2226 2227 /* 2228 * Find the nearest square-centre. 2229 */ 2230 xs = (float)floor(x) + 0.5F; 2231 ys = (float)floor(y) + 0.5F; 2232 2233 /* 2234 * And find the nearest grid vertex. 2235 */ 2236 xv = (float)floor(x + 0.5F); 2237 yv = (float)floor(y + 0.5F); 2238 2239 /* 2240 * We allocate clicks in parts of the grid square to either 2241 * corners, edges or square centres, as follows: 2242 * 2243 * +--+--------+--+ 2244 * | | | | 2245 * +--+ +--+ 2246 * | `. ,' | 2247 * | +--+ | 2248 * | | | | 2249 * | +--+ | 2250 * | ,' `. | 2251 * +--+ +--+ 2252 * | | | | 2253 * +--+--------+--+ 2254 * 2255 * (Not to scale!) 2256 * 2257 * In other words: we measure the square distance (i.e. 2258 * max(dx,dy)) from the click to the nearest corner, and if 2259 * it's within CORNER_TOLERANCE then we return a corner click. 2260 * We measure the square distance from the click to the nearest 2261 * centre, and if that's within CENTRE_TOLERANCE we return a 2262 * centre click. Failing that, we find which of the two edge 2263 * centres is nearer to the click and return that edge. 2264 */ 2265 2266 /* 2267 * Check for corner click. 2268 */ 2269 dx = (float)fabs(x - xv); 2270 dy = (float)fabs(y - yv); 2271 dist = (dx > dy ? dx : dy); 2272 if (dist < CORNER_TOLERANCE) { 2273 *xr = 2 * (int)xv; 2274 *yr = 2 * (int)yv; 2275 } else { 2276 /* 2277 * Check for centre click. 2278 */ 2279 dx = (float)fabs(x - xs); 2280 dy = (float)fabs(y - ys); 2281 dist = (dx > dy ? dx : dy); 2282 if (dist < CENTRE_TOLERANCE) { 2283 *xr = 1 + 2 * (int)xs; 2284 *yr = 1 + 2 * (int)ys; 2285 } else { 2286 /* 2287 * Failing both of those, see which edge we're closer to. 2288 * Conveniently, this is simply done by testing the relative 2289 * magnitude of dx and dy (which are currently distances from 2290 * the square centre). 2291 */ 2292 if (dx > dy) { 2293 /* Vertical edge: x-coord of corner, 2294 * y-coord of square centre. */ 2295 *xr = 2 * (int)xv; 2296 *yr = 1 + 2 * (int)floor(ys); 2297 } else { 2298 /* Horizontal edge: x-coord of square centre, 2299 * y-coord of corner. */ 2300 *xr = 1 + 2 * (int)floor(xs); 2301 *yr = 2 * (int)yv; 2302 } 2303 } 2304 } 2305} 2306 2307/* 2308 * Returns true if it has made any change to the grid. 2309 */ 2310static bool grid_draw_rect(const game_state *state, 2311 unsigned char *hedge, unsigned char *vedge, 2312 int c, bool really, bool outline, 2313 int x1, int y1, int x2, int y2) 2314{ 2315 int x, y; 2316 bool changed = false; 2317 2318 /* 2319 * Draw horizontal edges of rectangles. 2320 */ 2321 for (x = x1; x < x2; x++) 2322 for (y = y1; y <= y2; y++) 2323 if (HRANGE(state,x,y)) { 2324 int val = index(state,hedge,x,y); 2325 if (y == y1 || y == y2) { 2326 if (!outline) continue; 2327 val = c; 2328 } else if (c == 1) 2329 val = 0; 2330 changed = changed || (index(state,hedge,x,y) != val); 2331 if (really) 2332 index(state,hedge,x,y) = val; 2333 } 2334 2335 /* 2336 * Draw vertical edges of rectangles. 2337 */ 2338 for (y = y1; y < y2; y++) 2339 for (x = x1; x <= x2; x++) 2340 if (VRANGE(state,x,y)) { 2341 int val = index(state,vedge,x,y); 2342 if (x == x1 || x == x2) { 2343 if (!outline) continue; 2344 val = c; 2345 } else if (c == 1) 2346 val = 0; 2347 changed = changed || (index(state,vedge,x,y) != val); 2348 if (really) 2349 index(state,vedge,x,y) = val; 2350 } 2351 2352 return changed; 2353} 2354 2355static bool ui_draw_rect(const game_state *state, const game_ui *ui, 2356 unsigned char *hedge, unsigned char *vedge, int c, 2357 bool really, bool outline) 2358{ 2359 return grid_draw_rect(state, hedge, vedge, c, really, outline, 2360 ui->x1, ui->y1, ui->x2, ui->y2); 2361} 2362 2363static void game_changed_state(game_ui *ui, const game_state *oldstate, 2364 const game_state *newstate) 2365{ 2366} 2367 2368struct game_drawstate { 2369 bool started; 2370 int w, h, tilesize; 2371 unsigned long *visible; 2372}; 2373 2374static const char *current_key_label(const game_ui *ui, 2375 const game_state *state, int button) 2376{ 2377 if (IS_CURSOR_SELECT(button) && ui->cur_visible && 2378 !(ui->drag_start_x >= 0 && !ui->cur_dragging)) { 2379 if (ui->cur_dragging) { 2380 if (!ui->dragged) return "Cancel"; 2381 if ((button == CURSOR_SELECT2) == ui->erasing) return "Done"; 2382 return "Cancel"; 2383 } 2384 return button == CURSOR_SELECT ? "Mark" : "Erase"; 2385 } 2386 return ""; 2387} 2388 2389static char *interpret_move(const game_state *from, game_ui *ui, 2390 const game_drawstate *ds, 2391 int x, int y, int button) 2392{ 2393 int xc, yc; 2394 bool startdrag = false, enddrag = false, active = false, erasing = false; 2395 char buf[80], *ret; 2396 2397 button = STRIP_BUTTON_MODIFIERS(button); 2398 2399 coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc); 2400 2401 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2402 if (ui->drag_start_x >= 0 && ui->cur_dragging) 2403 reset_ui(ui); /* cancel keyboard dragging */ 2404 startdrag = true; 2405 ui->cur_visible = ui->cur_dragging = false; 2406 active = true; 2407 erasing = (button == RIGHT_BUTTON); 2408 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) { 2409 /* We assert we should have had a LEFT_BUTTON first. */ 2410 if (ui->cur_visible) { 2411 ui->cur_visible = false; 2412 active = true; 2413 } 2414 assert(!ui->cur_dragging); 2415 enddrag = true; 2416 erasing = (button == RIGHT_RELEASE); 2417 } else if (IS_CURSOR_MOVE(button)) { 2418 char *ret; 2419 ret = move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, 2420 false, &ui->cur_visible); 2421 active = true; 2422 if (!ui->cur_dragging || ret != MOVE_UI_UPDATE) return ret; 2423 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); 2424 } else if (IS_CURSOR_SELECT(button)) { 2425 if (ui->drag_start_x >= 0 && !ui->cur_dragging) { 2426 /* 2427 * If a mouse drag is in progress, ignore attempts to 2428 * start a keyboard one. 2429 */ 2430 return NULL; 2431 } 2432 if (!ui->cur_visible) { 2433 assert(!ui->cur_dragging); 2434 ui->cur_visible = true; 2435 return MOVE_UI_UPDATE; 2436 } 2437 coord_round((float)ui->cur_x + 0.5F, (float)ui->cur_y + 0.5F, &xc, &yc); 2438 erasing = (button == CURSOR_SELECT2); 2439 if (ui->cur_dragging) { 2440 ui->cur_dragging = false; 2441 enddrag = true; 2442 active = true; 2443 } else { 2444 ui->cur_dragging = true; 2445 startdrag = true; 2446 active = true; 2447 } 2448 } else if (button == '\b' || button == 27) { 2449 if (!ui->cur_dragging) { 2450 ui->cur_visible = false; 2451 } else { 2452 assert(ui->cur_visible); 2453 reset_ui(ui); /* cancel keyboard dragging */ 2454 ui->cur_dragging = false; 2455 } 2456 return MOVE_UI_UPDATE; 2457 } else if (button != LEFT_DRAG && button != RIGHT_DRAG) { 2458 return NULL; 2459 } 2460 2461 if (startdrag && 2462 xc >= 0 && xc <= 2*from->w && 2463 yc >= 0 && yc <= 2*from->h) { 2464 2465 ui->drag_start_x = xc; 2466 ui->drag_start_y = yc; 2467 ui->drag_end_x = -1; 2468 ui->drag_end_y = -1; 2469 ui->dragged = false; 2470 ui->erasing = erasing; 2471 active = true; 2472 } 2473 2474 if (ui->drag_start_x >= 0 && 2475 (xc != ui->drag_end_x || yc != ui->drag_end_y)) { 2476 int t; 2477 2478 if (ui->drag_end_x != -1 && ui->drag_end_y != -1) 2479 ui->dragged = true; 2480 ui->drag_end_x = xc; 2481 ui->drag_end_y = yc; 2482 active = true; 2483 2484 if (xc >= 0 && xc <= 2*from->w && 2485 yc >= 0 && yc <= 2*from->h) { 2486 ui->x1 = ui->drag_start_x; 2487 ui->x2 = ui->drag_end_x; 2488 if (ui->x2 < ui->x1) { t = ui->x1; ui->x1 = ui->x2; ui->x2 = t; } 2489 2490 ui->y1 = ui->drag_start_y; 2491 ui->y2 = ui->drag_end_y; 2492 if (ui->y2 < ui->y1) { t = ui->y1; ui->y1 = ui->y2; ui->y2 = t; } 2493 2494 ui->x1 = ui->x1 / 2; /* rounds down */ 2495 ui->x2 = (ui->x2+1) / 2; /* rounds up */ 2496 ui->y1 = ui->y1 / 2; /* rounds down */ 2497 ui->y2 = (ui->y2+1) / 2; /* rounds up */ 2498 } else { 2499 ui->x1 = -1; 2500 ui->y1 = -1; 2501 ui->x2 = -1; 2502 ui->y2 = -1; 2503 } 2504 } 2505 2506 ret = NULL; 2507 2508 if (enddrag && (ui->drag_start_x >= 0)) { 2509 if (xc >= 0 && xc <= 2*from->w && 2510 yc >= 0 && yc <= 2*from->h && 2511 erasing == ui->erasing) { 2512 2513 if (ui->dragged) { 2514 if (ui_draw_rect(from, ui, from->hedge, 2515 from->vedge, 1, false, !ui->erasing)) { 2516 sprintf(buf, "%c%d,%d,%d,%d", 2517 (int)(ui->erasing ? 'E' : 'R'), 2518 ui->x1, ui->y1, ui->x2 - ui->x1, ui->y2 - ui->y1); 2519 ret = dupstr(buf); 2520 } 2521 } else { 2522 if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) { 2523 sprintf(buf, "H%d,%d", xc/2, yc/2); 2524 ret = dupstr(buf); 2525 } 2526 if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) { 2527 sprintf(buf, "V%d,%d", xc/2, yc/2); 2528 ret = dupstr(buf); 2529 } 2530 } 2531 } 2532 2533 reset_ui(ui); 2534 active = true; 2535 } 2536 2537 if (ret) 2538 return ret; /* a move has been made */ 2539 else if (active) 2540 return MOVE_UI_UPDATE; 2541 else 2542 return NULL; 2543} 2544 2545static game_state *execute_move(const game_state *from, const char *move) 2546{ 2547 game_state *ret; 2548 int x1, y1, x2, y2, mode; 2549 2550 if (move[0] == 'S') { 2551 const char *p = move+1; 2552 int x, y; 2553 2554 ret = dup_game(from); 2555 ret->cheated = true; 2556 2557 for (y = 0; y < ret->h; y++) 2558 for (x = 1; x < ret->w; x++) { 2559 vedge(ret, x, y) = (*p == '1'); 2560 if (*p) p++; 2561 } 2562 for (y = 1; y < ret->h; y++) 2563 for (x = 0; x < ret->w; x++) { 2564 hedge(ret, x, y) = (*p == '1'); 2565 if (*p) p++; 2566 } 2567 2568 sfree(ret->correct); 2569 ret->correct = get_correct(ret); 2570 2571 return ret; 2572 2573 } else if ((move[0] == 'R' || move[0] == 'E') && 2574 sscanf(move+1, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4 && 2575 x1 >= 0 && x2 >= 0 && x1+x2 <= from->w && 2576 y1 >= 0 && y2 >= 0 && y1+y2 <= from->h) { 2577 x2 += x1; 2578 y2 += y1; 2579 mode = move[0]; 2580 } else if ((move[0] == 'H' || move[0] == 'V') && 2581 sscanf(move+1, "%d,%d", &x1, &y1) == 2 && 2582 (move[0] == 'H' ? HRANGE(from, x1, y1) : 2583 VRANGE(from, x1, y1))) { 2584 mode = move[0]; 2585 } else 2586 return NULL; /* can't parse move string */ 2587 2588 ret = dup_game(from); 2589 2590 if (mode == 'R' || mode == 'E') { 2591 grid_draw_rect(ret, ret->hedge, ret->vedge, 1, true, 2592 mode == 'R', x1, y1, x2, y2); 2593 } else if (mode == 'H') { 2594 hedge(ret,x1,y1) = !hedge(ret,x1,y1); 2595 } else if (mode == 'V') { 2596 vedge(ret,x1,y1) = !vedge(ret,x1,y1); 2597 } 2598 2599 sfree(ret->correct); 2600 ret->correct = get_correct(ret); 2601 2602 /* 2603 * We've made a real change to the grid. Check to see 2604 * if the game has been completed. 2605 */ 2606 if (!ret->completed) { 2607 int x, y; 2608 bool ok; 2609 2610 ok = true; 2611 for (x = 0; x < ret->w; x++) 2612 for (y = 0; y < ret->h; y++) 2613 if (!index(ret, ret->correct, x, y)) 2614 ok = false; 2615 2616 if (ok) 2617 ret->completed = true; 2618 } 2619 2620 return ret; 2621} 2622 2623/* ---------------------------------------------------------------------- 2624 * Drawing routines. 2625 */ 2626 2627#define CORRECT (1L<<16) 2628#define CURSOR (1L<<17) 2629 2630#define COLOUR(k) ( (k)==1 ? COL_LINE : (k)==2 ? COL_DRAG : COL_DRAGERASE ) 2631#define MAX4(x,y,z,w) ( max(max(x,y),max(z,w)) ) 2632 2633static void game_compute_size(const game_params *params, int tilesize, 2634 const game_ui *ui, int *x, int *y) 2635{ 2636 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2637 struct { int tilesize; } ads, *ds = &ads; 2638 ads.tilesize = tilesize; 2639 2640 *x = params->w * TILE_SIZE + 2*BORDER + 1; 2641 *y = params->h * TILE_SIZE + 2*BORDER + 1; 2642} 2643 2644static void game_set_size(drawing *dr, game_drawstate *ds, 2645 const game_params *params, int tilesize) 2646{ 2647 ds->tilesize = tilesize; 2648} 2649 2650static float *game_colours(frontend *fe, int *ncolours) 2651{ 2652 float *ret = snewn(3 * NCOLOURS, float); 2653 2654 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 2655 2656 ret[COL_GRID * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0]; 2657 ret[COL_GRID * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1]; 2658 ret[COL_GRID * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2]; 2659 2660 ret[COL_DRAG * 3 + 0] = 1.0F; 2661 ret[COL_DRAG * 3 + 1] = 0.0F; 2662 ret[COL_DRAG * 3 + 2] = 0.0F; 2663 2664 ret[COL_DRAGERASE * 3 + 0] = 0.2F; 2665 ret[COL_DRAGERASE * 3 + 1] = 0.2F; 2666 ret[COL_DRAGERASE * 3 + 2] = 1.0F; 2667 2668 ret[COL_CORRECT * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0]; 2669 ret[COL_CORRECT * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1]; 2670 ret[COL_CORRECT * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2]; 2671 2672 ret[COL_LINE * 3 + 0] = 0.0F; 2673 ret[COL_LINE * 3 + 1] = 0.0F; 2674 ret[COL_LINE * 3 + 2] = 0.0F; 2675 2676 ret[COL_TEXT * 3 + 0] = 0.0F; 2677 ret[COL_TEXT * 3 + 1] = 0.0F; 2678 ret[COL_TEXT * 3 + 2] = 0.0F; 2679 2680 ret[COL_CURSOR * 3 + 0] = 1.0F; 2681 ret[COL_CURSOR * 3 + 1] = 0.5F; 2682 ret[COL_CURSOR * 3 + 2] = 0.5F; 2683 2684 *ncolours = NCOLOURS; 2685 return ret; 2686} 2687 2688static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 2689{ 2690 struct game_drawstate *ds = snew(struct game_drawstate); 2691 int i; 2692 2693 ds->started = false; 2694 ds->w = state->w; 2695 ds->h = state->h; 2696 ds->visible = snewn(ds->w * ds->h, unsigned long); 2697 ds->tilesize = 0; /* not decided yet */ 2698 for (i = 0; i < ds->w * ds->h; i++) 2699 ds->visible[i] = 0xFFFF; 2700 2701 return ds; 2702} 2703 2704static void game_free_drawstate(drawing *dr, game_drawstate *ds) 2705{ 2706 sfree(ds->visible); 2707 sfree(ds); 2708} 2709 2710static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, 2711 int x, int y, unsigned char *hedge, unsigned char *vedge, 2712 unsigned char *corners, unsigned long bgflags) 2713{ 2714 int cx = COORD(x), cy = COORD(y); 2715 char str[80]; 2716 2717 draw_rect(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID); 2718 draw_rect(dr, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1, 2719 (bgflags & CURSOR) ? COL_CURSOR : 2720 (bgflags & CORRECT) ? COL_CORRECT : COL_BACKGROUND); 2721 2722 if (grid(state,x,y)) { 2723 sprintf(str, "%d", grid(state,x,y)); 2724 draw_text(dr, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE, 2725 TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_TEXT, str); 2726 } 2727 2728 /* 2729 * Draw edges. 2730 */ 2731 if (!HRANGE(state,x,y) || index(state,hedge,x,y)) 2732 draw_rect(dr, cx, cy, TILE_SIZE+1, 2, 2733 HRANGE(state,x,y) ? COLOUR(index(state,hedge,x,y)) : 2734 COL_LINE); 2735 if (!HRANGE(state,x,y+1) || index(state,hedge,x,y+1)) 2736 draw_rect(dr, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2, 2737 HRANGE(state,x,y+1) ? COLOUR(index(state,hedge,x,y+1)) : 2738 COL_LINE); 2739 if (!VRANGE(state,x,y) || index(state,vedge,x,y)) 2740 draw_rect(dr, cx, cy, 2, TILE_SIZE+1, 2741 VRANGE(state,x,y) ? COLOUR(index(state,vedge,x,y)) : 2742 COL_LINE); 2743 if (!VRANGE(state,x+1,y) || index(state,vedge,x+1,y)) 2744 draw_rect(dr, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1, 2745 VRANGE(state,x+1,y) ? COLOUR(index(state,vedge,x+1,y)) : 2746 COL_LINE); 2747 2748 /* 2749 * Draw corners. 2750 */ 2751 if (index(state,corners,x,y)) 2752 draw_rect(dr, cx, cy, 2, 2, 2753 COLOUR(index(state,corners,x,y))); 2754 if (x+1 < state->w && index(state,corners,x+1,y)) 2755 draw_rect(dr, cx+TILE_SIZE-1, cy, 2, 2, 2756 COLOUR(index(state,corners,x+1,y))); 2757 if (y+1 < state->h && index(state,corners,x,y+1)) 2758 draw_rect(dr, cx, cy+TILE_SIZE-1, 2, 2, 2759 COLOUR(index(state,corners,x,y+1))); 2760 if (x+1 < state->w && y+1 < state->h && index(state,corners,x+1,y+1)) 2761 draw_rect(dr, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2, 2762 COLOUR(index(state,corners,x+1,y+1))); 2763 2764 draw_update(dr, cx, cy, TILE_SIZE+1, TILE_SIZE+1); 2765} 2766 2767static void game_redraw(drawing *dr, game_drawstate *ds, 2768 const game_state *oldstate, const game_state *state, 2769 int dir, const game_ui *ui, 2770 float animtime, float flashtime) 2771{ 2772 int x, y; 2773 unsigned char *hedge, *vedge, *corners; 2774 2775 if (ui->dragged) { 2776 hedge = snewn(state->w*state->h, unsigned char); 2777 vedge = snewn(state->w*state->h, unsigned char); 2778 memcpy(hedge, state->hedge, state->w*state->h); 2779 memcpy(vedge, state->vedge, state->w*state->h); 2780 ui_draw_rect(state, ui, hedge, vedge, ui->erasing ? 3 : 2, true, true); 2781 } else { 2782 hedge = state->hedge; 2783 vedge = state->vedge; 2784 } 2785 2786 corners = snewn(state->w * state->h, unsigned char); 2787 memset(corners, 0, state->w * state->h); 2788 for (x = 0; x < state->w; x++) 2789 for (y = 0; y < state->h; y++) { 2790 if (x > 0) { 2791 int e = index(state, vedge, x, y); 2792 if (index(state,corners,x,y) < e) 2793 index(state,corners,x,y) = e; 2794 if (y+1 < state->h && 2795 index(state,corners,x,y+1) < e) 2796 index(state,corners,x,y+1) = e; 2797 } 2798 if (y > 0) { 2799 int e = index(state, hedge, x, y); 2800 if (index(state,corners,x,y) < e) 2801 index(state,corners,x,y) = e; 2802 if (x+1 < state->w && 2803 index(state,corners,x+1,y) < e) 2804 index(state,corners,x+1,y) = e; 2805 } 2806 } 2807 2808 if (!ds->started) { 2809 draw_rect(dr, COORD(0)-1, COORD(0)-1, 2810 ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE); 2811 ds->started = true; 2812 draw_update(dr, 0, 0, 2813 state->w * TILE_SIZE + 2*BORDER + 1, 2814 state->h * TILE_SIZE + 2*BORDER + 1); 2815 } 2816 2817 for (x = 0; x < state->w; x++) 2818 for (y = 0; y < state->h; y++) { 2819 unsigned long c = 0; 2820 2821 if (HRANGE(state,x,y)) 2822 c |= index(state,hedge,x,y); 2823 if (HRANGE(state,x,y+1)) 2824 c |= index(state,hedge,x,y+1) << 2; 2825 if (VRANGE(state,x,y)) 2826 c |= index(state,vedge,x,y) << 4; 2827 if (VRANGE(state,x+1,y)) 2828 c |= index(state,vedge,x+1,y) << 6; 2829 c |= index(state,corners,x,y) << 8; 2830 if (x+1 < state->w) 2831 c |= index(state,corners,x+1,y) << 10; 2832 if (y+1 < state->h) 2833 c |= index(state,corners,x,y+1) << 12; 2834 if (x+1 < state->w && y+1 < state->h) 2835 /* cast to prevent 2<<14 sign-extending on promotion to long */ 2836 c |= (unsigned long)index(state,corners,x+1,y+1) << 14; 2837 if (index(state, state->correct, x, y) && !flashtime) 2838 c |= CORRECT; 2839 if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y) 2840 c |= CURSOR; 2841 2842 if (index(ds,ds->visible,x,y) != c) { 2843 draw_tile(dr, ds, state, x, y, hedge, vedge, corners, 2844 (c & (CORRECT|CURSOR)) ); 2845 index(ds,ds->visible,x,y) = c; 2846 } 2847 } 2848 2849 { 2850 char buf[256]; 2851 2852 if (ui->dragged && 2853 ui->x1 >= 0 && ui->y1 >= 0 && 2854 ui->x2 >= 0 && ui->y2 >= 0) { 2855 sprintf(buf, "%dx%d ", 2856 ui->x2-ui->x1, 2857 ui->y2-ui->y1); 2858 } else { 2859 buf[0] = '\0'; 2860 } 2861 2862 if (state->cheated) 2863 strcat(buf, "Auto-solved."); 2864 else if (state->completed) 2865 strcat(buf, "COMPLETED!"); 2866 2867 status_bar(dr, buf); 2868 } 2869 2870 if (hedge != state->hedge) { 2871 sfree(hedge); 2872 sfree(vedge); 2873 } 2874 2875 sfree(corners); 2876} 2877 2878static float game_anim_length(const game_state *oldstate, 2879 const game_state *newstate, int dir, game_ui *ui) 2880{ 2881 return 0.0F; 2882} 2883 2884static float game_flash_length(const game_state *oldstate, 2885 const game_state *newstate, int dir, game_ui *ui) 2886{ 2887 if (!oldstate->completed && newstate->completed && 2888 !oldstate->cheated && !newstate->cheated) 2889 return FLASH_TIME; 2890 return 0.0F; 2891} 2892 2893static void game_get_cursor_location(const game_ui *ui, 2894 const game_drawstate *ds, 2895 const game_state *state, 2896 const game_params *params, 2897 int *x, int *y, int *w, int *h) 2898{ 2899 if(ui->cur_visible) { 2900 *x = COORD(ui->cur_x); 2901 *y = COORD(ui->cur_y); 2902 *w = *h = TILE_SIZE; 2903 } 2904} 2905 2906static int game_status(const game_state *state) 2907{ 2908 return state->completed ? +1 : 0; 2909} 2910 2911static void game_print_size(const game_params *params, const game_ui *ui, 2912 float *x, float *y) 2913{ 2914 int pw, ph; 2915 2916 /* 2917 * I'll use 5mm squares by default. 2918 */ 2919 game_compute_size(params, 500, ui, &pw, &ph); 2920 *x = pw / 100.0F; 2921 *y = ph / 100.0F; 2922} 2923 2924static void game_print(drawing *dr, const game_state *state, const game_ui *ui, 2925 int tilesize) 2926{ 2927 int w = state->w, h = state->h; 2928 int ink = print_mono_colour(dr, 0); 2929 int x, y; 2930 2931 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2932 game_drawstate ads, *ds = &ads; 2933 game_set_size(dr, ds, NULL, tilesize); 2934 2935 /* 2936 * Border. 2937 */ 2938 print_line_width(dr, TILE_SIZE / 10); 2939 draw_rect_outline(dr, COORD(0), COORD(0), w*TILE_SIZE, h*TILE_SIZE, ink); 2940 2941 /* 2942 * Grid. We have to make the grid lines particularly thin, 2943 * because users will be drawing lines _along_ them and we want 2944 * those lines to be visible. 2945 */ 2946 print_line_width(dr, TILE_SIZE / 256); 2947 for (x = 1; x < w; x++) 2948 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), ink); 2949 for (y = 1; y < h; y++) 2950 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), ink); 2951 2952 /* 2953 * Solution. 2954 */ 2955 print_line_width(dr, TILE_SIZE / 10); 2956 for (y = 0; y <= h; y++) 2957 for (x = 0; x <= w; x++) { 2958 if (HRANGE(state,x,y) && hedge(state,x,y)) 2959 draw_line(dr, COORD(x), COORD(y), COORD(x+1), COORD(y), ink); 2960 if (VRANGE(state,x,y) && vedge(state,x,y)) 2961 draw_line(dr, COORD(x), COORD(y), COORD(x), COORD(y+1), ink); 2962 } 2963 2964 /* 2965 * Clues. 2966 */ 2967 for (y = 0; y < h; y++) 2968 for (x = 0; x < w; x++) 2969 if (grid(state,x,y)) { 2970 char str[80]; 2971 sprintf(str, "%d", grid(state,x,y)); 2972 draw_text(dr, COORD(x)+TILE_SIZE/2, COORD(y)+TILE_SIZE/2, 2973 FONT_VARIABLE, TILE_SIZE/2, 2974 ALIGN_HCENTRE | ALIGN_VCENTRE, ink, str); 2975 } 2976} 2977 2978#ifdef COMBINED 2979#define thegame rect 2980#endif 2981 2982const struct game thegame = { 2983 "Rectangles", "games.rectangles", "rect", 2984 default_params, 2985 game_fetch_preset, NULL, 2986 decode_params, 2987 encode_params, 2988 free_params, 2989 dup_params, 2990 true, game_configure, custom_params, 2991 validate_params, 2992 new_game_desc, 2993 validate_desc, 2994 new_game, 2995 dup_game, 2996 free_game, 2997 true, solve_game, 2998 true, game_can_format_as_text_now, game_text_format, 2999 NULL, NULL, /* get_prefs, set_prefs */ 3000 new_ui, 3001 free_ui, 3002 NULL, /* encode_ui */ 3003 NULL, /* decode_ui */ 3004 NULL, /* game_request_keys */ 3005 game_changed_state, 3006 current_key_label, 3007 interpret_move, 3008 execute_move, 3009 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3010 game_colours, 3011 game_new_drawstate, 3012 game_free_drawstate, 3013 game_redraw, 3014 game_anim_length, 3015 game_flash_length, 3016 game_get_cursor_location, 3017 game_status, 3018 true, false, game_print_size, game_print, 3019 true, /* wants_statusbar */ 3020 false, NULL, /* timing_state */ 3021 0, /* flags */ 3022}; 3023 3024/* vim: set shiftwidth=4 tabstop=8: */