A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2856 lines 96 kB view raw
1/* 2 * undead: Implementation of Haunted Mirror Mazes 3 * 4 * http://www.janko.at/Raetsel/Spukschloss/index.htm 5 * 6 * Puzzle definition is the total number of each monster type, the 7 * grid definition, and the list of sightings (clockwise, starting 8 * from top left corner) 9 * 10 * Example: (Janko puzzle No. 1, 11 * http://www.janko.at/Raetsel/Spukschloss/001.a.htm ) 12 * 13 * Ghosts: 0 Vampires: 2 Zombies: 6 14 * 15 * 2 1 1 1 16 * 1 \ \ . / 2 17 * 0 \ . / . 2 18 * 0 / . / . 2 19 * 3 . . . \ 2 20 * 3 3 2 2 21 * 22 * would be encoded into: 23 * 4x4:0,2,6,LLaRLaRaRaRdL,2,1,1,1,2,2,2,2,2,2,3,3,3,0,0,1 24 * 25 * Additionally, the game description can contain monsters fixed at a 26 * certain grid position. The internal generator does not (yet) use 27 * this feature, but this is needed to enter puzzles like Janko No. 28 * 14, which is encoded as: 29 * 8x5:12,12,0,LaRbLaRaLaRLbRaVaVaGRaRaRaLbLaRbRLb,0,2,0,2,2,1,2,1,3,1,0,1,8,4,3,0,0,2,3,2,7,2,1,6,2,1 30 * 31 */ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <assert.h> 37#include <ctype.h> 38#ifdef NO_TGMATH_H 39# include <math.h> 40#else 41# include <tgmath.h> 42#endif 43 44#include "puzzles.h" 45 46enum { 47 COL_BACKGROUND, 48 COL_GRID, 49 COL_TEXT, 50 COL_ERROR, 51 COL_HIGHLIGHT, 52 COL_FLASH, 53 COL_GHOST, 54 COL_ZOMBIE, 55 COL_VAMPIRE, 56 COL_DONE, 57 NCOLOURS 58}; 59 60enum { 61 PREF_PENCIL_KEEP_HIGHLIGHT, 62 PREF_MONSTERS, 63 N_PREF_ITEMS 64}; 65 66#define DIFFLIST(A) \ 67 A(EASY,Easy,e) \ 68 A(NORMAL,Normal,n) \ 69 A(TRICKY,Tricky,t) 70#define ENUM(upper,title,lower) DIFF_ ## upper, 71#define TITLE(upper,title,lower) #title, 72#define ENCODE(upper,title,lower) #lower 73#define CONFIG(upper,title,lower) ":" #title 74enum { DIFFLIST(ENUM) DIFFCOUNT }; 75static char const *const undead_diffnames[] = { DIFFLIST(TITLE) "(count)" }; 76static char const undead_diffchars[] = DIFFLIST(ENCODE); 77#define DIFFCONFIG DIFFLIST(CONFIG) 78 79struct game_params { 80 int w; /* Grid width */ 81 int h; /* Grid height */ 82 int diff; /* Puzzle difficulty */ 83}; 84 85static const struct game_params undead_presets[] = { 86 { 4, 4, DIFF_EASY }, 87 { 4, 4, DIFF_NORMAL }, 88 { 4, 4, DIFF_TRICKY }, 89 { 5, 5, DIFF_EASY }, 90 { 5, 5, DIFF_NORMAL }, 91 { 5, 5, DIFF_TRICKY }, 92 { 7, 7, DIFF_EASY }, 93 { 7, 7, DIFF_NORMAL } 94}; 95 96#define DEFAULT_PRESET 1 97 98static game_params *default_params(void) { 99 game_params *ret = snew(game_params); 100 101 *ret = undead_presets[DEFAULT_PRESET]; 102 return ret; 103} 104 105static bool game_fetch_preset(int i, char **name, game_params **params) { 106 game_params *ret; 107 char buf[64]; 108 109 if (i < 0 || i >= lenof(undead_presets)) return false; 110 111 ret = default_params(); 112 *ret = undead_presets[i]; /* struct copy */ 113 *params = ret; 114 115 sprintf(buf, "%dx%d %s", 116 undead_presets[i].w, undead_presets[i].h, 117 undead_diffnames[undead_presets[i].diff]); 118 *name = dupstr(buf); 119 120 return true; 121} 122 123static void free_params(game_params *params) { 124 sfree(params); 125} 126 127static game_params *dup_params(const game_params *params) 128{ 129 game_params *ret = snew(game_params); 130 *ret = *params; /* structure copy */ 131 return ret; 132} 133 134static void decode_params(game_params *params, char const *string) { 135 params->w = params->h = atoi(string); 136 137 while (*string && isdigit((unsigned char) *string)) ++string; 138 if (*string == 'x') { 139 string++; 140 params->h = atoi(string); 141 while (*string && isdigit((unsigned char)*string)) string++; 142 } 143 144 params->diff = DIFF_NORMAL; 145 if (*string == 'd') { 146 int i; 147 string++; 148 for (i = 0; i < DIFFCOUNT; i++) 149 if (*string == undead_diffchars[i]) 150 params->diff = i; 151 if (*string) string++; 152 } 153 154 return; 155} 156 157static char *encode_params(const game_params *params, bool full) 158{ 159 char buf[256]; 160 sprintf(buf, "%dx%d", params->w, params->h); 161 if (full) 162 sprintf(buf + strlen(buf), "d%c", undead_diffchars[params->diff]); 163 return dupstr(buf); 164} 165 166static config_item *game_configure(const game_params *params) 167{ 168 config_item *ret; 169 char buf[64]; 170 171 ret = snewn(4, config_item); 172 173 ret[0].name = "Width"; 174 ret[0].type = C_STRING; 175 sprintf(buf, "%d", params->w); 176 ret[0].u.string.sval = dupstr(buf); 177 178 ret[1].name = "Height"; 179 ret[1].type = C_STRING; 180 sprintf(buf, "%d", params->h); 181 ret[1].u.string.sval = dupstr(buf); 182 183 ret[2].name = "Difficulty"; 184 ret[2].type = C_CHOICES; 185 ret[2].u.choices.choicenames = DIFFCONFIG; 186 ret[2].u.choices.selected = params->diff; 187 188 ret[3].name = NULL; 189 ret[3].type = C_END; 190 191 return ret; 192} 193 194static game_params *custom_params(const config_item *cfg) 195{ 196 game_params *ret = snew(game_params); 197 198 ret->w = atoi(cfg[0].u.string.sval); 199 ret->h = atoi(cfg[1].u.string.sval); 200 ret->diff = cfg[2].u.choices.selected; 201 return ret; 202} 203 204static const char *validate_params(const game_params *params, bool full) 205{ 206 if (params->w < 3) return "Width must be at least 3"; 207 if (params->h < 3) return "Height must be at least 3"; 208 if (params->w > 54 / params->h) return "Grid is too big"; 209 if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating"; 210 return NULL; 211} 212 213/* --------------------------------------------------------------- */ 214/* Game state allocation, deallocation. */ 215 216struct path { 217 int length; 218 int *p; 219 int grid_start; 220 int grid_end; 221 int num_monsters; 222 int *mapping; 223 int sightings_start; 224 int sightings_end; 225 int *xy; 226}; 227 228struct game_common { 229 int refcount; 230 struct game_params params; 231 int wh; 232 int num_ghosts,num_vampires,num_zombies,num_total; 233 int num_paths; 234 struct path *paths; 235 int *grid; 236 int *xinfo; 237 bool *fixed; 238}; 239 240struct game_state { 241 struct game_common *common; 242 int *guess; 243 unsigned char *pencils; 244 bool *cell_errors; 245 bool *hint_errors; 246 bool *hints_done; 247 bool count_errors[3]; 248 bool solved; 249 bool cheated; 250}; 251 252static game_state *new_state(const game_params *params) { 253 int i; 254 game_state *state = snew(game_state); 255 state->common = snew(struct game_common); 256 257 state->common->refcount = 1; 258 state->common->params.w = params->w; 259 state->common->params.h = params->h; 260 state->common->params.diff = params->diff; 261 262 state->common->wh = (state->common->params.w +2) * (state->common->params.h +2); 263 264 state->common->num_ghosts = 0; 265 state->common->num_vampires = 0; 266 state->common->num_zombies = 0; 267 state->common->num_total = 0; 268 269 state->common->grid = snewn(state->common->wh, int); 270 state->common->xinfo = snewn(state->common->wh, int); 271 state->common->fixed = NULL; 272 273 state->common->num_paths = 274 state->common->params.w + state->common->params.h; 275 state->common->paths = snewn(state->common->num_paths, struct path); 276 277 for (i=0;i<state->common->num_paths;i++) { 278 state->common->paths[i].length = 0; 279 state->common->paths[i].grid_start = -1; 280 state->common->paths[i].grid_end = -1; 281 state->common->paths[i].num_monsters = 0; 282 state->common->paths[i].sightings_start = 0; 283 state->common->paths[i].sightings_end = 0; 284 state->common->paths[i].p = snewn(state->common->wh,int); 285 state->common->paths[i].xy = snewn(state->common->wh,int); 286 state->common->paths[i].mapping = snewn(state->common->wh,int); 287 } 288 289 state->guess = NULL; 290 state->pencils = NULL; 291 292 state->cell_errors = snewn(state->common->wh, bool); 293 for (i=0;i<state->common->wh;i++) 294 state->cell_errors[i] = false; 295 state->hint_errors = snewn(2*state->common->num_paths, bool); 296 for (i=0;i<2*state->common->num_paths;i++) 297 state->hint_errors[i] = false; 298 state->hints_done = snewn(2 * state->common->num_paths, bool); 299 memset(state->hints_done, 0, 300 2 * state->common->num_paths * sizeof(bool)); 301 for (i=0;i<3;i++) 302 state->count_errors[i] = false; 303 304 state->solved = false; 305 state->cheated = false; 306 307 return state; 308} 309 310static game_state *dup_game(const game_state *state) 311{ 312 game_state *ret = snew(game_state); 313 314 ret->common = state->common; 315 ret->common->refcount++; 316 317 if (state->guess != NULL) { 318 ret->guess = snewn(ret->common->num_total,int); 319 memcpy(ret->guess, state->guess, ret->common->num_total*sizeof(int)); 320 } 321 else ret->guess = NULL; 322 323 if (state->pencils != NULL) { 324 ret->pencils = snewn(ret->common->num_total,unsigned char); 325 memcpy(ret->pencils, state->pencils, 326 ret->common->num_total*sizeof(unsigned char)); 327 } 328 else ret->pencils = NULL; 329 330 if (state->cell_errors != NULL) { 331 ret->cell_errors = snewn(ret->common->wh,bool); 332 memcpy(ret->cell_errors, state->cell_errors, 333 ret->common->wh*sizeof(bool)); 334 } 335 else ret->cell_errors = NULL; 336 337 if (state->hint_errors != NULL) { 338 ret->hint_errors = snewn(2*ret->common->num_paths,bool); 339 memcpy(ret->hint_errors, state->hint_errors, 340 2*ret->common->num_paths*sizeof(bool)); 341 } 342 else ret->hint_errors = NULL; 343 344 if (state->hints_done != NULL) { 345 ret->hints_done = snewn(2 * state->common->num_paths, bool); 346 memcpy(ret->hints_done, state->hints_done, 347 2 * state->common->num_paths * sizeof(bool)); 348 } 349 else ret->hints_done = NULL; 350 351 ret->count_errors[0] = state->count_errors[0]; 352 ret->count_errors[1] = state->count_errors[1]; 353 ret->count_errors[2] = state->count_errors[2]; 354 355 ret->solved = state->solved; 356 ret->cheated = state->cheated; 357 358 return ret; 359} 360 361static void free_game(game_state *state) { 362 int i; 363 364 state->common->refcount--; 365 if (state->common->refcount == 0) { 366 for (i=0;i<state->common->num_paths;i++) { 367 sfree(state->common->paths[i].mapping); 368 sfree(state->common->paths[i].xy); 369 sfree(state->common->paths[i].p); 370 } 371 sfree(state->common->paths); 372 sfree(state->common->xinfo); 373 sfree(state->common->grid); 374 if (state->common->fixed != NULL) sfree(state->common->fixed); 375 sfree(state->common); 376 } 377 if (state->hints_done != NULL) sfree(state->hints_done); 378 if (state->hint_errors != NULL) sfree(state->hint_errors); 379 if (state->cell_errors != NULL) sfree(state->cell_errors); 380 if (state->pencils != NULL) sfree(state->pencils); 381 if (state->guess != NULL) sfree(state->guess); 382 sfree(state); 383 384 return; 385} 386 387/* --------------------------------------------------------------- */ 388/* Puzzle generator */ 389 390/* cell states */ 391enum { 392 CELL_EMPTY, 393 CELL_MIRROR_L, 394 CELL_MIRROR_R, 395 CELL_GHOST, 396 CELL_VAMPIRE, 397 CELL_ZOMBIE, 398 CELL_UNDEF 399}; 400 401/* grid walk directions */ 402enum { 403 DIRECTION_NONE, 404 DIRECTION_UP, 405 DIRECTION_RIGHT, 406 DIRECTION_LEFT, 407 DIRECTION_DOWN 408}; 409 410static int range2grid(int rangeno, int width, int height, int *x, int *y) { 411 412 if (rangeno < 0) { 413 *x = 0; *y = 0; return DIRECTION_NONE; 414 } 415 if (rangeno < width) { 416 *x = rangeno+1; *y = 0; return DIRECTION_DOWN; 417 } 418 rangeno = rangeno - width; 419 if (rangeno < height) { 420 *x = width+1; *y = rangeno+1; return DIRECTION_LEFT; 421 } 422 rangeno = rangeno - height; 423 if (rangeno < width) { 424 *x = width-rangeno; *y = height+1; return DIRECTION_UP; 425 } 426 rangeno = rangeno - width; 427 if (rangeno < height) { 428 *x = 0; *y = height-rangeno; return DIRECTION_RIGHT; 429 } 430 *x = 0; *y = 0; 431 return DIRECTION_NONE; 432} 433 434static int grid2range(int x, int y, int w, int h) { 435 if (x>0 && x<w+1 && y>0 && y<h+1) return -1; 436 if (x<0 || x>w+1 || y<0 || y>h+1) return -1; 437 if ((x == 0 || x==w+1) && (y==0 || y==h+1)) return -1; 438 if (y==0) return x-1; 439 if (x==(w+1)) return y-1+w; 440 if (y==(h+1)) return 2*w + h - x; 441 return 2*(w+h) - y; 442} 443 444static void make_paths(game_state *state) { 445 int i; 446 int count = 0; 447 448 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { 449 int x,y,dir; 450 int j,k,num_monsters; 451 bool found; 452 int c,p; 453 found = false; 454 /* Check whether inverse path is already in list */ 455 for (j=0;j<count;j++) { 456 if (i == state->common->paths[j].grid_end) { 457 found = true; 458 break; 459 } 460 } 461 if (found) continue; 462 463 /* We found a new path through the mirror maze */ 464 state->common->paths[count].grid_start = i; 465 dir = range2grid(i, state->common->params.w, 466 state->common->params.h,&x,&y); 467 state->common->paths[count].sightings_start = 468 state->common->grid[x+y*(state->common->params.w +2)]; 469 while (true) { 470 int c,r; 471 472 if (dir == DIRECTION_DOWN) y++; 473 else if (dir == DIRECTION_LEFT) x--; 474 else if (dir == DIRECTION_UP) y--; 475 else if (dir == DIRECTION_RIGHT) x++; 476 477 r = grid2range(x, y, state->common->params.w, 478 state->common->params.h); 479 if (r != -1) { 480 state->common->paths[count].grid_end = r; 481 state->common->paths[count].sightings_end = 482 state->common->grid[x+y*(state->common->params.w +2)]; 483 break; 484 } 485 486 c = state->common->grid[x+y*(state->common->params.w+2)]; 487 state->common->paths[count].xy[state->common->paths[count].length] = 488 x+y*(state->common->params.w+2); 489 if (c == CELL_MIRROR_L) { 490 state->common->paths[count].p[state->common->paths[count].length] = -1; 491 if (dir == DIRECTION_DOWN) dir = DIRECTION_RIGHT; 492 else if (dir == DIRECTION_LEFT) dir = DIRECTION_UP; 493 else if (dir == DIRECTION_UP) dir = DIRECTION_LEFT; 494 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_DOWN; 495 } 496 else if (c == CELL_MIRROR_R) { 497 state->common->paths[count].p[state->common->paths[count].length] = -1; 498 if (dir == DIRECTION_DOWN) dir = DIRECTION_LEFT; 499 else if (dir == DIRECTION_LEFT) dir = DIRECTION_DOWN; 500 else if (dir == DIRECTION_UP) dir = DIRECTION_RIGHT; 501 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_UP; 502 } 503 else { 504 state->common->paths[count].p[state->common->paths[count].length] = 505 state->common->xinfo[x+y*(state->common->params.w+2)]; 506 } 507 state->common->paths[count].length++; 508 } 509 /* Count unique monster entries in each path */ 510 state->common->paths[count].num_monsters = 0; 511 for (j=0;j<state->common->num_total;j++) { 512 num_monsters = 0; 513 for (k=0;k<state->common->paths[count].length;k++) 514 if (state->common->paths[count].p[k] == j) 515 num_monsters++; 516 if (num_monsters > 0) 517 state->common->paths[count].num_monsters++; 518 } 519 520 /* Generate mapping vector */ 521 c = 0; 522 for (p=0;p<state->common->paths[count].length;p++) { 523 int m; 524 m = state->common->paths[count].p[p]; 525 if (m == -1) continue; 526 found = false; 527 for (j=0; j<c; j++) 528 if (state->common->paths[count].mapping[j] == m) found = true; 529 if (!found) state->common->paths[count].mapping[c++] = m; 530 } 531 count++; 532 } 533 return; 534} 535 536struct guess { 537 int length; 538 int *guess; 539 int *possible; 540}; 541 542static bool next_list(struct guess *g, int pos) { 543 544 if (pos == 0) { 545 if ((g->guess[pos] == 1 && g->possible[pos] == 1) || 546 (g->guess[pos] == 2 && (g->possible[pos] == 3 || 547 g->possible[pos] == 2)) || 548 g->guess[pos] == 4) 549 return false; 550 if (g->guess[pos] == 1 && (g->possible[pos] == 3 || 551 g->possible[pos] == 7)) { 552 g->guess[pos] = 2; return true; 553 } 554 if (g->guess[pos] == 1 && g->possible[pos] == 5) { 555 g->guess[pos] = 4; return true; 556 } 557 if (g->guess[pos] == 2 && (g->possible[pos] == 6 || g->possible[pos] == 7)) { 558 g->guess[pos] = 4; return true; 559 } 560 } 561 562 if (g->guess[pos] == 1) { 563 if (g->possible[pos] == 1) { 564 return next_list(g,pos-1); 565 } 566 if (g->possible[pos] == 3 || g->possible[pos] == 7) { 567 g->guess[pos] = 2; return true; 568 } 569 if (g->possible[pos] == 5) { 570 g->guess[pos] = 4; return true; 571 } 572 } 573 574 if (g->guess[pos] == 2) { 575 if (g->possible[pos] == 2) { 576 return next_list(g,pos-1); 577 } 578 if (g->possible[pos] == 3) { 579 g->guess[pos] = 1; return next_list(g,pos-1); 580 } 581 if (g->possible[pos] == 6 || g->possible[pos] == 7) { 582 g->guess[pos] = 4; return true; 583 } 584 } 585 586 if (g->guess[pos] == 4) { 587 if (g->possible[pos] == 5 || g->possible[pos] == 7) { 588 g->guess[pos] = 1; return next_list(g,pos-1); 589 } 590 if (g->possible[pos] == 6) { 591 g->guess[pos] = 2; return next_list(g,pos-1); 592 } 593 if (g->possible[pos] == 4) { 594 return next_list(g,pos-1); 595 } 596 } 597 return false; 598} 599 600static void get_unique(game_state *state, int counter, random_state *rs) { 601 602 int p,i,c,pathlimit,count_uniques; 603 struct guess path_guess; 604 int *view_count; 605 606 struct entry { 607 struct entry *link; 608 int *guess; 609 int start_view; 610 int end_view; 611 }; 612 613 struct { 614 struct entry *head; 615 struct entry *node; 616 } views, single_views, test_views; 617 618 struct entry test_entry; 619 620 path_guess.length = state->common->paths[counter].num_monsters; 621 path_guess.guess = snewn(path_guess.length,int); 622 path_guess.possible = snewn(path_guess.length,int); 623 for (i=0;i<path_guess.length;i++) 624 path_guess.guess[i] = path_guess.possible[i] = 0; 625 626 for (p=0;p<path_guess.length;p++) { 627 path_guess.possible[p] = 628 state->guess[state->common->paths[counter].mapping[p]]; 629 switch (path_guess.possible[p]) { 630 case 1: path_guess.guess[p] = 1; break; 631 case 2: path_guess.guess[p] = 2; break; 632 case 3: path_guess.guess[p] = 1; break; 633 case 4: path_guess.guess[p] = 4; break; 634 case 5: path_guess.guess[p] = 1; break; 635 case 6: path_guess.guess[p] = 2; break; 636 case 7: path_guess.guess[p] = 1; break; 637 } 638 } 639 640 views.head = NULL; 641 views.node = NULL; 642 643 pathlimit = state->common->paths[counter].length + 1; 644 view_count = snewn(pathlimit*pathlimit, int); 645 for (i = 0; i < pathlimit*pathlimit; i++) 646 view_count[i] = 0; 647 648 do { 649 bool mirror; 650 int start_view, end_view; 651 652 mirror = false; 653 start_view = 0; 654 for (p=0;p<state->common->paths[counter].length;p++) { 655 if (state->common->paths[counter].p[p] == -1) mirror = true; 656 else { 657 for (i=0;i<path_guess.length;i++) { 658 if (state->common->paths[counter].p[p] == 659 state->common->paths[counter].mapping[i]) { 660 if (path_guess.guess[i] == 1 && mirror) 661 start_view++; 662 if (path_guess.guess[i] == 2 && !mirror) 663 start_view++; 664 if (path_guess.guess[i] == 4) 665 start_view++; 666 break; 667 } 668 } 669 } 670 } 671 mirror = false; 672 end_view = 0; 673 for (p=state->common->paths[counter].length-1;p>=0;p--) { 674 if (state->common->paths[counter].p[p] == -1) mirror = true; 675 else { 676 for (i=0;i<path_guess.length;i++) { 677 if (state->common->paths[counter].p[p] == 678 state->common->paths[counter].mapping[i]) { 679 if (path_guess.guess[i] == 1 && mirror) 680 end_view++; 681 if (path_guess.guess[i] == 2 && !mirror) 682 end_view++; 683 if (path_guess.guess[i] == 4) 684 end_view++; 685 break; 686 } 687 } 688 } 689 } 690 691 assert(start_view >= 0 && start_view < pathlimit); 692 assert(end_view >= 0 && end_view < pathlimit); 693 i = start_view * pathlimit + end_view; 694 view_count[i]++; 695 if (view_count[i] == 1) { 696 views.node = snewn(1,struct entry); 697 views.node->link = views.head; 698 views.node->guess = snewn(path_guess.length,int); 699 views.head = views.node; 700 views.node->start_view = start_view; 701 views.node->end_view = end_view; 702 memcpy(views.node->guess, path_guess.guess, 703 path_guess.length*sizeof(int)); 704 } 705 } while (next_list(&path_guess, path_guess.length-1)); 706 707 /* extract single entries from view list */ 708 709 test_views.head = views.head; 710 test_views.node = views.node; 711 712 test_entry.guess = snewn(path_guess.length,int); 713 714 single_views.head = NULL; 715 single_views.node = NULL; 716 717 count_uniques = 0; 718 while (test_views.head != NULL) { 719 test_views.node = test_views.head; 720 test_views.head = test_views.head->link; 721 i = test_views.node->start_view * pathlimit + test_views.node->end_view; 722 if (view_count[i] == 1) { 723 single_views.node = snewn(1,struct entry); 724 single_views.node->link = single_views.head; 725 single_views.node->guess = snewn(path_guess.length,int); 726 single_views.head = single_views.node; 727 single_views.node->start_view = test_views.node->start_view; 728 single_views.node->end_view = test_views.node->end_view; 729 memcpy(single_views.node->guess, test_views.node->guess, 730 path_guess.length*sizeof(int)); 731 count_uniques++; 732 } 733 } 734 735 sfree(view_count); 736 737 if (count_uniques > 0) { 738 test_entry.start_view = 0; 739 test_entry.end_view = 0; 740 /* Choose one unique guess per random */ 741 /* While we are busy with looping through single_views, we 742 * conveniently free the linked list single_view */ 743 c = random_upto(rs,count_uniques); 744 while(single_views.head != NULL) { 745 single_views.node = single_views.head; 746 single_views.head = single_views.head->link; 747 if (c-- == 0) { 748 memcpy(test_entry.guess, single_views.node->guess, 749 path_guess.length*sizeof(int)); 750 test_entry.start_view = single_views.node->start_view; 751 test_entry.end_view = single_views.node->end_view; 752 } 753 sfree(single_views.node->guess); 754 sfree(single_views.node); 755 } 756 757 /* Modify state_guess according to path_guess.mapping */ 758 for (i=0;i<path_guess.length;i++) 759 state->guess[state->common->paths[counter].mapping[i]] = 760 test_entry.guess[i]; 761 } 762 763 sfree(test_entry.guess); 764 765 while (views.head != NULL) { 766 views.node = views.head; 767 views.head = views.head->link; 768 sfree(views.node->guess); 769 sfree(views.node); 770 } 771 772 sfree(path_guess.possible); 773 sfree(path_guess.guess); 774 775 return; 776} 777 778static int count_monsters(game_state *state, 779 int *cGhost, int *cVampire, int *cZombie) { 780 int cNone; 781 int i; 782 783 *cGhost = *cVampire = *cZombie = cNone = 0; 784 785 for (i=0;i<state->common->num_total;i++) { 786 if (state->guess[i] == 1) (*cGhost)++; 787 else if (state->guess[i] == 2) (*cVampire)++; 788 else if (state->guess[i] == 4) (*cZombie)++; 789 else cNone++; 790 } 791 792 return cNone; 793} 794 795static bool check_numbers(game_state *state, int *guess) { 796 bool valid; 797 int i; 798 int count_ghosts, count_vampires, count_zombies; 799 800 count_ghosts = count_vampires = count_zombies = 0; 801 for (i=0;i<state->common->num_total;i++) { 802 if (guess[i] == 1) count_ghosts++; 803 if (guess[i] == 2) count_vampires++; 804 if (guess[i] == 4) count_zombies++; 805 } 806 807 valid = true; 808 809 if (count_ghosts > state->common->num_ghosts) valid = false; 810 if (count_vampires > state->common->num_vampires) valid = false; 811 if (count_zombies > state->common->num_zombies) valid = false; 812 813 return valid; 814} 815 816static bool check_solution(int *g, struct path path) { 817 int i; 818 bool mirror; 819 int count; 820 821 count = 0; 822 mirror = false; 823 for (i=0;i<path.length;i++) { 824 if (path.p[i] == -1) mirror = true; 825 else { 826 if (g[path.p[i]] == 1 && mirror) count++; 827 else if (g[path.p[i]] == 2 && !mirror) count++; 828 else if (g[path.p[i]] == 4) count++; 829 } 830 } 831 if (count != path.sightings_start) return false; 832 833 count = 0; 834 mirror = false; 835 for (i=path.length-1;i>=0;i--) { 836 if (path.p[i] == -1) mirror = true; 837 else { 838 if (g[path.p[i]] == 1 && mirror) count++; 839 else if (g[path.p[i]] == 2 && !mirror) count++; 840 else if (g[path.p[i]] == 4) count++; 841 } 842 } 843 if (count != path.sightings_end) return false; 844 845 return true; 846} 847 848static bool solve_iterative(game_state *state, struct path *paths) { 849 bool solved; 850 int p,i,j,count; 851 852 int *guess; 853 int *possible; 854 855 struct guess loop; 856 857 solved = true; 858 loop.length = state->common->num_total; 859 guess = snewn(state->common->num_total,int); 860 possible = snewn(state->common->num_total,int); 861 862 for (i=0;i<state->common->num_total;i++) { 863 guess[i] = state->guess[i]; 864 possible[i] = 0; 865 } 866 867 for (p=0;p<state->common->num_paths;p++) { 868 if (paths[p].num_monsters > 0) { 869 loop.length = paths[p].num_monsters; 870 loop.guess = snewn(paths[p].num_monsters,int); 871 loop.possible = snewn(paths[p].num_monsters,int); 872 873 for (i=0;i<paths[p].num_monsters;i++) { 874 switch (state->guess[paths[p].mapping[i]]) { 875 case 1: loop.guess[i] = 1; break; 876 case 2: loop.guess[i] = 2; break; 877 case 3: loop.guess[i] = 1; break; 878 case 4: loop.guess[i] = 4; break; 879 case 5: loop.guess[i] = 1; break; 880 case 6: loop.guess[i] = 2; break; 881 case 7: loop.guess[i] = 1; break; 882 } 883 loop.possible[i] = state->guess[paths[p].mapping[i]]; 884 possible[paths[p].mapping[i]] = 0; 885 } 886 887 while(true) { 888 for (i=0;i<state->common->num_total;i++) { 889 guess[i] = state->guess[i]; 890 } 891 count = 0; 892 for (i=0;i<paths[p].num_monsters;i++) 893 guess[paths[p].mapping[i]] = loop.guess[count++]; 894 if (check_numbers(state,guess) && 895 check_solution(guess,paths[p])) 896 for (j=0;j<paths[p].num_monsters;j++) 897 possible[paths[p].mapping[j]] |= loop.guess[j]; 898 if (!next_list(&loop,loop.length-1)) break; 899 } 900 for (i=0;i<paths[p].num_monsters;i++) 901 state->guess[paths[p].mapping[i]] &= 902 possible[paths[p].mapping[i]]; 903 sfree(loop.possible); 904 sfree(loop.guess); 905 } 906 } 907 908 for (i=0;i<state->common->num_total;i++) { 909 if (state->guess[i] == 3 || state->guess[i] == 5 || 910 state->guess[i] == 6 || state->guess[i] == 7) { 911 solved = false; break; 912 } 913 } 914 915 sfree(possible); 916 sfree(guess); 917 918 return solved; 919} 920 921static bool solve_bruteforce(game_state *state, struct path *paths) { 922 bool solved, correct; 923 int number_solutions; 924 int p,i; 925 926 struct guess loop; 927 928 loop.guess = snewn(state->common->num_total,int); 929 loop.possible = snewn(state->common->num_total,int); 930 931 for (i=0;i<state->common->num_total;i++) { 932 loop.possible[i] = state->guess[i]; 933 switch (state->guess[i]) { 934 case 1: loop.guess[i] = 1; break; 935 case 2: loop.guess[i] = 2; break; 936 case 3: loop.guess[i] = 1; break; 937 case 4: loop.guess[i] = 4; break; 938 case 5: loop.guess[i] = 1; break; 939 case 6: loop.guess[i] = 2; break; 940 case 7: loop.guess[i] = 1; break; 941 } 942 } 943 944 solved = false; 945 number_solutions = 0; 946 947 while (true) { 948 949 correct = true; 950 if (!check_numbers(state,loop.guess)) correct = false; 951 else 952 for (p=0;p<state->common->num_paths;p++) 953 if (!check_solution(loop.guess,paths[p])) { 954 correct = false; break; 955 } 956 if (correct) { 957 number_solutions++; 958 solved = true; 959 if(number_solutions > 1) { 960 solved = false; 961 break; 962 } 963 for (i=0;i<state->common->num_total;i++) 964 state->guess[i] = loop.guess[i]; 965 } 966 if (!next_list(&loop,state->common->num_total -1)) { 967 break; 968 } 969 } 970 971 sfree(loop.possible); 972 sfree(loop.guess); 973 974 return solved; 975} 976 977static int path_cmp(const void *a, const void *b) { 978 const struct path *pa = (const struct path *)a; 979 const struct path *pb = (const struct path *)b; 980 return pa->num_monsters - pb->num_monsters; 981} 982 983static char *new_game_desc(const game_params *params, random_state *rs, 984 char **aux, bool interactive) { 985 int count,c,w,h,r,p,g; 986 game_state *new; 987 988 /* Variables for puzzle generation algorithm */ 989 int filling; 990 int max_length; 991 int count_ghosts, count_vampires, count_zombies; 992 bool abort; 993 float ratio; 994 995 /* Variables for solver algorithm */ 996 bool solved_iterative, solved_bruteforce, contains_inconsistency; 997 int count_ambiguous; 998 int iterative_depth; 999 int *old_guess; 1000 1001 /* Variables for game description generation */ 1002 int x,y; 1003 char *e; 1004 char *desc; 1005 1006 while (true) { 1007 new = new_state(params); 1008 abort = false; 1009 1010 /* Fill grid with random mirrors and (later to be populated) 1011 * empty monster cells */ 1012 count = 0; 1013 for (h=1;h<new->common->params.h+1;h++) 1014 for (w=1;w<new->common->params.w+1;w++) { 1015 c = random_upto(rs,5); 1016 if (c >= 2) { 1017 new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY; 1018 new->common->xinfo[w+h*(new->common->params.w+2)] = count++; 1019 } 1020 else if (c == 0) { 1021 new->common->grid[w+h*(new->common->params.w+2)] = 1022 CELL_MIRROR_L; 1023 new->common->xinfo[w+h*(new->common->params.w+2)] = -1; 1024 } 1025 else { 1026 new->common->grid[w+h*(new->common->params.w+2)] = 1027 CELL_MIRROR_R; 1028 new->common->xinfo[w+h*(new->common->params.w+2)] = -1; 1029 } 1030 } 1031 new->common->num_total = count; /* Total number of monsters in maze */ 1032 1033 /* Puzzle is boring if it has too few monster cells. Discard 1034 * grid, make new grid */ 1035 if (new->common->num_total <= 4) { 1036 free_game(new); 1037 continue; 1038 } 1039 1040 /* Monsters / Mirrors ratio should be balanced */ 1041 ratio = (float)new->common->num_total / 1042 (float)(new->common->params.w * new->common->params.h); 1043 if (ratio < 0.48F || ratio > 0.78F) { 1044 free_game(new); 1045 continue; 1046 } 1047 1048 /* Assign clue identifiers */ 1049 for (r=0;r<2*(new->common->params.w+new->common->params.h);r++) { 1050 int x,y,gridno; 1051 gridno = range2grid(r,new->common->params.w,new->common->params.h, 1052 &x,&y); 1053 new->common->grid[x+y*(new->common->params.w +2)] = gridno; 1054 new->common->xinfo[x+y*(new->common->params.w +2)] = 0; 1055 } 1056 /* The four corners don't matter at all for the game. Set them 1057 * all to zero, just to have a nice data structure */ 1058 new->common->grid[0] = 0; 1059 new->common->xinfo[0] = 0; 1060 new->common->grid[new->common->params.w+1] = 0; 1061 new->common->xinfo[new->common->params.w+1] = 0; 1062 new->common->grid[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; 1063 new->common->xinfo[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0; 1064 new->common->grid[(new->common->params.h+1)*(new->common->params.w+2)] = 0; 1065 new->common->xinfo[(new->common->params.h+1)*(new->common->params.w+2)] = 0; 1066 1067 /* Initialize solution vector */ 1068 new->guess = snewn(new->common->num_total,int); 1069 for (g=0;g<new->common->num_total;g++) new->guess[g] = 7; 1070 1071 /* Initialize fixed flag from common. Not needed for the 1072 * puzzle generator; initialize it for having clean code */ 1073 new->common->fixed = snewn(new->common->num_total, bool); 1074 for (g=0;g<new->common->num_total;g++) 1075 new->common->fixed[g] = false; 1076 1077 /* paths generation */ 1078 make_paths(new); 1079 1080 /* Grid is invalid if max. path length > threshold. Discard 1081 * grid, make new one */ 1082 switch (new->common->params.diff) { 1083 case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break; 1084 case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break; 1085 case DIFF_TRICKY: max_length = 9; break; 1086 default: max_length = 9; break; 1087 } 1088 1089 for (p=0;p<new->common->num_paths;p++) { 1090 if (new->common->paths[p].num_monsters > max_length) { 1091 abort = true; 1092 } 1093 } 1094 if (abort) { 1095 free_game(new); 1096 continue; 1097 } 1098 1099 qsort(new->common->paths, new->common->num_paths, 1100 sizeof(struct path), path_cmp); 1101 1102 /* Grid monster initialization */ 1103 /* For easy puzzles, we try to fill nearly the whole grid 1104 with unique solution paths (up to 2) For more difficult 1105 puzzles, we fill only roughly half the grid, and choose 1106 random monsters for the rest For hard puzzles, we fill 1107 even less paths with unique solutions */ 1108 1109 switch (new->common->params.diff) { 1110 case DIFF_EASY: filling = 2; break; 1111 case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; 1112 case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break; 1113 default: filling = 0; break; 1114 } 1115 1116 count = 0; 1117 while ( (count_monsters(new, &count_ghosts, &count_vampires, 1118 &count_zombies)) > filling) { 1119 if ((count) >= new->common->num_paths) break; 1120 if (new->common->paths[count].num_monsters == 0) { 1121 count++; 1122 continue; 1123 } 1124 get_unique(new,count,rs); 1125 count++; 1126 } 1127 1128 /* Fill any remaining ambiguous entries with random monsters */ 1129 for(g=0;g<new->common->num_total;g++) { 1130 if (new->guess[g] == 7) { 1131 r = random_upto(rs,3); 1132 new->guess[g] = (r == 0) ? 1 : ( (r == 1) ? 2 : 4 ); 1133 } 1134 } 1135 1136 /* Determine all hints */ 1137 count_monsters(new, &new->common->num_ghosts, 1138 &new->common->num_vampires, &new->common->num_zombies); 1139 1140 /* Puzzle is trivial if it has only one type of monster. Discard. */ 1141 if ((new->common->num_ghosts == 0 && new->common->num_vampires == 0) || 1142 (new->common->num_ghosts == 0 && new->common->num_zombies == 0) || 1143 (new->common->num_vampires == 0 && new->common->num_zombies == 0)) { 1144 free_game(new); 1145 continue; 1146 } 1147 1148 /* Discard puzzle if difficulty Tricky, and it has only 1 1149 * member of any monster type */ 1150 if (new->common->params.diff == DIFF_TRICKY && 1151 (new->common->num_ghosts <= 1 || 1152 new->common->num_vampires <= 1 || new->common->num_zombies <= 1)) { 1153 free_game(new); 1154 continue; 1155 } 1156 1157 for (w=1;w<new->common->params.w+1;w++) 1158 for (h=1;h<new->common->params.h+1;h++) { 1159 c = new->common->xinfo[w+h*(new->common->params.w+2)]; 1160 if (c >= 0) { 1161 if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST; 1162 if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE; 1163 if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE; 1164 } 1165 } 1166 1167 /* Prepare path information needed by the solver (containing all hints) */ 1168 for (p=0;p<new->common->num_paths;p++) { 1169 bool mirror; 1170 int x,y; 1171 1172 new->common->paths[p].sightings_start = 0; 1173 new->common->paths[p].sightings_end = 0; 1174 1175 mirror = false; 1176 for (g=0;g<new->common->paths[p].length;g++) { 1177 1178 if (new->common->paths[p].p[g] == -1) mirror = true; 1179 else { 1180 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror) (new->common->paths[p].sightings_start)++; 1181 else if (new->guess[new->common->paths[p].p[g]] == 2 && !mirror) (new->common->paths[p].sightings_start)++; 1182 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_start)++; 1183 } 1184 } 1185 1186 mirror = false; 1187 for (g=new->common->paths[p].length-1;g>=0;g--) { 1188 if (new->common->paths[p].p[g] == -1) mirror = true; 1189 else { 1190 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror) (new->common->paths[p].sightings_end)++; 1191 else if (new->guess[new->common->paths[p].p[g]] == 2 && !mirror) (new->common->paths[p].sightings_end)++; 1192 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_end)++; 1193 } 1194 } 1195 1196 range2grid(new->common->paths[p].grid_start, 1197 new->common->params.w,new->common->params.h,&x,&y); 1198 new->common->grid[x+y*(new->common->params.w +2)] = 1199 new->common->paths[p].sightings_start; 1200 range2grid(new->common->paths[p].grid_end, 1201 new->common->params.w,new->common->params.h,&x,&y); 1202 new->common->grid[x+y*(new->common->params.w +2)] = 1203 new->common->paths[p].sightings_end; 1204 } 1205 1206 /* Try to solve the puzzle with the iterative solver */ 1207 old_guess = snewn(new->common->num_total,int); 1208 for (p=0;p<new->common->num_total;p++) { 1209 new->guess[p] = 7; 1210 old_guess[p] = 7; 1211 } 1212 iterative_depth = 0; 1213 solved_iterative = false; 1214 contains_inconsistency = false; 1215 count_ambiguous = 0; 1216 1217 while (true) { 1218 bool no_change = true; 1219 solved_iterative = solve_iterative(new,new->common->paths); 1220 iterative_depth++; 1221 for (p=0;p<new->common->num_total;p++) { 1222 if (new->guess[p] != old_guess[p]) no_change = false; 1223 old_guess[p] = new->guess[p]; 1224 if (new->guess[p] == 0) contains_inconsistency = true; 1225 } 1226 if (solved_iterative || no_change) break; 1227 } 1228 1229 /* If necessary, try to solve the puzzle with the brute-force solver */ 1230 solved_bruteforce = false; 1231 if (new->common->params.diff != DIFF_EASY && 1232 !solved_iterative && !contains_inconsistency) { 1233 for (p=0;p<new->common->num_total;p++) 1234 if (new->guess[p] != 1 && new->guess[p] != 2 && 1235 new->guess[p] != 4) count_ambiguous++; 1236 1237 solved_bruteforce = solve_bruteforce(new, new->common->paths); 1238 } 1239 1240 /* Determine puzzle difficulty level */ 1241 if (new->common->params.diff == DIFF_EASY && solved_iterative && 1242 iterative_depth <= 3 && !contains_inconsistency) { 1243/* printf("Puzzle level: EASY Level %d Ratio %f Ambiguous %d (Found after %i tries)\n",iterative_depth, ratio, count_ambiguous, i); */ 1244 break; 1245 } 1246 1247 if (new->common->params.diff == DIFF_NORMAL && 1248 ((solved_iterative && iterative_depth > 3) || 1249 (solved_bruteforce && count_ambiguous < 4)) && 1250 !contains_inconsistency) { 1251/* printf("Puzzle level: NORMAL Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ 1252 break; 1253 } 1254 if (new->common->params.diff == DIFF_TRICKY && 1255 solved_bruteforce && iterative_depth > 0 && 1256 count_ambiguous >= 4 && !contains_inconsistency) { 1257/* printf("Puzzle level: TRICKY Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */ 1258 break; 1259 } 1260 1261 /* If puzzle is not solvable or does not satisfy the desired 1262 * difficulty level, free memory and start from scratch */ 1263 sfree(old_guess); 1264 free_game(new); 1265 } 1266 1267 /* We have a valid puzzle! */ 1268 1269 desc = snewn(10 + new->common->wh + 1270 6*(new->common->params.w + new->common->params.h), char); 1271 e = desc; 1272 1273 /* Encode monster counts */ 1274 e += sprintf(e, "%d,", new->common->num_ghosts); 1275 e += sprintf(e, "%d,", new->common->num_vampires); 1276 e += sprintf(e, "%d,", new->common->num_zombies); 1277 1278 /* Encode grid */ 1279 count = 0; 1280 for (y=1;y<new->common->params.h+1;y++) 1281 for (x=1;x<new->common->params.w+1;x++) { 1282 c = new->common->grid[x+y*(new->common->params.w+2)]; 1283 if (count > 25) { 1284 *e++ = 'z'; 1285 count -= 26; 1286 } 1287 if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) { 1288 count++; 1289 } 1290 else if (c == CELL_MIRROR_L) { 1291 if (count > 0) *e++ = (count-1 + 'a'); 1292 *e++ = 'L'; 1293 count = 0; 1294 } 1295 else { 1296 if (count > 0) *e++ = (count-1 + 'a'); 1297 *e++ = 'R'; 1298 count = 0; 1299 } 1300 } 1301 if (count > 0) *e++ = (count-1 + 'a'); 1302 1303 /* Encode hints */ 1304 for (p=0;p<2*(new->common->params.w + new->common->params.h);p++) { 1305 range2grid(p,new->common->params.w,new->common->params.h,&x,&y); 1306 e += sprintf(e, ",%d", new->common->grid[x+y*(new->common->params.w+2)]); 1307 } 1308 1309 *e++ = '\0'; 1310 desc = sresize(desc, e - desc, char); 1311 1312 sfree(old_guess); 1313 free_game(new); 1314 1315 return desc; 1316} 1317 1318static void num2grid(int num, int width, int height, int *x, int *y) { 1319 *x = 1+(num%width); 1320 *y = 1+(num/width); 1321 return; 1322} 1323 1324static key_label *game_request_keys(const game_params *params, int *nkeys) 1325{ 1326 key_label *keys = snewn(4, key_label); 1327 *nkeys = 4; 1328 1329 keys[0].button = 'G'; 1330 keys[0].label = dupstr("Ghost"); 1331 1332 keys[1].button = 'V'; 1333 keys[1].label = dupstr("Vampire"); 1334 1335 keys[2].button = 'Z'; 1336 keys[2].label = dupstr("Zombie"); 1337 1338 keys[3].button = '\b'; 1339 keys[3].label = NULL; 1340 1341 return keys; 1342} 1343 1344static game_state *new_game(midend *me, const game_params *params, 1345 const char *desc) 1346{ 1347 int i; 1348 int n; 1349 int count; 1350 1351 game_state *state = new_state(params); 1352 1353 state->common->num_ghosts = atoi(desc); 1354 while (*desc && isdigit((unsigned char)*desc)) desc++; 1355 desc++; 1356 state->common->num_vampires = atoi(desc); 1357 while (*desc && isdigit((unsigned char)*desc)) desc++; 1358 desc++; 1359 state->common->num_zombies = atoi(desc); 1360 while (*desc && isdigit((unsigned char)*desc)) desc++; 1361 desc++; 1362 1363 state->common->num_total = state->common->num_ghosts + state->common->num_vampires + state->common->num_zombies; 1364 1365 state->guess = snewn(state->common->num_total,int); 1366 state->pencils = snewn(state->common->num_total,unsigned char); 1367 state->common->fixed = snewn(state->common->num_total, bool); 1368 for (i=0;i<state->common->num_total;i++) { 1369 state->guess[i] = 7; 1370 state->pencils[i] = 0; 1371 state->common->fixed[i] = false; 1372 } 1373 for (i=0;i<state->common->wh;i++) 1374 state->cell_errors[i] = false; 1375 for (i=0;i<2*state->common->num_paths;i++) 1376 state->hint_errors[i] = false; 1377 for (i=0;i<3;i++) 1378 state->count_errors[i] = false; 1379 1380 count = 0; 1381 n = 0; 1382 while (*desc != ',') { 1383 int c; 1384 int x,y; 1385 1386 if (*desc == 'L') { 1387 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1388 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_L; 1389 state->common->xinfo[x+y*(state->common->params.w+2)] = -1; 1390 n++; 1391 } 1392 else if (*desc == 'R') { 1393 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1394 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_R; 1395 state->common->xinfo[x+y*(state->common->params.w+2)] = -1; 1396 n++; 1397 } 1398 else if (*desc == 'G') { 1399 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1400 state->common->grid[x+y*(state->common->params.w +2)] = CELL_GHOST; 1401 state->common->xinfo[x+y*(state->common->params.w+2)] = count; 1402 state->guess[count] = 1; 1403 state->common->fixed[count++] = true; 1404 n++; 1405 } 1406 else if (*desc == 'V') { 1407 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1408 state->common->grid[x+y*(state->common->params.w +2)] = CELL_VAMPIRE; 1409 state->common->xinfo[x+y*(state->common->params.w+2)] = count; 1410 state->guess[count] = 2; 1411 state->common->fixed[count++] = true; 1412 n++; 1413 } 1414 else if (*desc == 'Z') { 1415 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1416 state->common->grid[x+y*(state->common->params.w +2)] = CELL_ZOMBIE; 1417 state->common->xinfo[x+y*(state->common->params.w+2)] = count; 1418 state->guess[count] = 4; 1419 state->common->fixed[count++] = true; 1420 n++; 1421 } 1422 else { 1423 c = *desc - ('a' -1); 1424 while (c-- > 0) { 1425 num2grid(n,state->common->params.w,state->common->params.h,&x,&y); 1426 state->common->grid[x+y*(state->common->params.w +2)] = CELL_EMPTY; 1427 state->common->xinfo[x+y*(state->common->params.w+2)] = count; 1428 state->guess[count] = 7; 1429 state->common->fixed[count++] = false; 1430 n++; 1431 } 1432 } 1433 desc++; 1434 } 1435 desc++; 1436 1437 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) { 1438 int x,y; 1439 int sights; 1440 1441 sights = atoi(desc); 1442 while (*desc && isdigit((unsigned char)*desc)) desc++; 1443 desc++; 1444 1445 1446 range2grid(i,state->common->params.w,state->common->params.h,&x,&y); 1447 state->common->grid[x+y*(state->common->params.w +2)] = sights; 1448 state->common->xinfo[x+y*(state->common->params.w +2)] = -2; 1449 } 1450 1451 state->common->grid[0] = 0; 1452 state->common->xinfo[0] = -2; 1453 state->common->grid[state->common->params.w+1] = 0; 1454 state->common->xinfo[state->common->params.w+1] = -2; 1455 state->common->grid[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = 0; 1456 state->common->xinfo[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = -2; 1457 state->common->grid[(state->common->params.h+1)*(state->common->params.w+2)] = 0; 1458 state->common->xinfo[(state->common->params.h+1)*(state->common->params.w+2)] = -2; 1459 1460 make_paths(state); 1461 qsort(state->common->paths, state->common->num_paths, sizeof(struct path), path_cmp); 1462 1463 return state; 1464} 1465 1466static const char *validate_desc(const game_params *params, const char *desc) 1467{ 1468 int i; 1469 int w = params->w, h = params->h; 1470 int wh = w*h; 1471 int area; 1472 int monsters; 1473 int monster_count; 1474 const char *desc_s = desc; 1475 1476 for (i=0;i<3;i++) { 1477 if (!*desc) return "Faulty game description"; 1478 while (*desc && isdigit((unsigned char)*desc)) { desc++; } 1479 if (*desc != ',') return "Invalid character in number list"; 1480 desc++; 1481 } 1482 desc = desc_s; 1483 1484 area = monsters = monster_count = 0; 1485 for (i=0;i<3;i++) { 1486 monster_count += atoi(desc); 1487 while (*desc && isdigit((unsigned char)*desc)) desc++; 1488 desc++; 1489 } 1490 while (*desc && *desc != ',') { 1491 if (*desc >= 'a' && *desc <= 'z') { 1492 area += *desc - 'a' +1; monsters += *desc - 'a' +1; 1493 } else if (*desc == 'G' || *desc == 'V' || *desc == 'Z') { 1494 area++; monsters++; 1495 } else if (*desc == 'L' || *desc == 'R') { 1496 area++; 1497 } else 1498 return "Invalid character in grid specification"; 1499 desc++; 1500 } 1501 if (area < wh) return "Not enough data to fill grid"; 1502 else if (area > wh) return "Too much data to fill grid"; 1503 if (monsters != monster_count) 1504 return "Monster numbers do not match grid spaces"; 1505 1506 for (i = 0; i < 2*(w+h); i++) { 1507 if (!*desc) return "Not enough numbers given after grid specification"; 1508 else if (*desc != ',') return "Invalid character in number list"; 1509 desc++; 1510 while (*desc && isdigit((unsigned char)*desc)) { desc++; } 1511 } 1512 1513 if (*desc) return "Unexpected additional data at end of game description"; 1514 1515 return NULL; 1516} 1517 1518static char *solve_game(const game_state *state_start, const game_state *currstate, 1519 const char *aux, const char **error) 1520{ 1521 int p; 1522 int *old_guess; 1523 int iterative_depth; 1524 bool solved_iterative, solved_bruteforce, contains_inconsistency; 1525 int count_ambiguous; 1526 1527 int i; 1528 char *move, *c; 1529 1530 game_state *solve_state = dup_game(currstate); 1531 1532 old_guess = snewn(solve_state->common->num_total,int); 1533 for (p=0;p<solve_state->common->num_total;p++) { 1534 if (solve_state->common->fixed[p]) { 1535 old_guess[p] = solve_state->guess[p] = state_start->guess[p]; 1536 } 1537 else { 1538 old_guess[p] = solve_state->guess[p] = 7; 1539 } 1540 } 1541 iterative_depth = 0; 1542 solved_iterative = false; 1543 contains_inconsistency = false; 1544 count_ambiguous = 0; 1545 1546 /* Try to solve the puzzle with the iterative solver */ 1547 while (true) { 1548 bool no_change = true; 1549 solved_iterative = 1550 solve_iterative(solve_state,solve_state->common->paths); 1551 iterative_depth++; 1552 for (p=0;p<solve_state->common->num_total;p++) { 1553 if (solve_state->guess[p] != old_guess[p]) no_change = false; 1554 old_guess[p] = solve_state->guess[p]; 1555 if (solve_state->guess[p] == 0) contains_inconsistency = true; 1556 } 1557 if (solved_iterative || no_change || contains_inconsistency) break; 1558 } 1559 1560 if (contains_inconsistency) { 1561 *error = "Puzzle is inconsistent"; 1562 sfree(old_guess); 1563 free_game(solve_state); 1564 return NULL; 1565 } 1566 1567 /* If necessary, try to solve the puzzle with the brute-force solver */ 1568 solved_bruteforce = false; 1569 if (!solved_iterative) { 1570 for (p=0;p<solve_state->common->num_total;p++) 1571 if (solve_state->guess[p] != 1 && solve_state->guess[p] != 2 && 1572 solve_state->guess[p] != 4) count_ambiguous++; 1573 solved_bruteforce = 1574 solve_bruteforce(solve_state, solve_state->common->paths); 1575 } 1576 1577 if (!solved_iterative && !solved_bruteforce) { 1578 *error = "Puzzle is unsolvable"; 1579 sfree(old_guess); 1580 free_game(solve_state); 1581 return NULL; 1582 } 1583 1584/* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */ 1585 (void)iterative_depth; 1586 (void)count_ambiguous; 1587 1588 move = snewn(solve_state->common->num_total * 4 +2, char); 1589 c = move; 1590 *c++='S'; 1591 for (i = 0; i < solve_state->common->num_total; i++) { 1592 if (solve_state->guess[i] == 1) c += sprintf(c, ";G%d", i); 1593 if (solve_state->guess[i] == 2) c += sprintf(c, ";V%d", i); 1594 if (solve_state->guess[i] == 4) c += sprintf(c, ";Z%d", i); 1595 } 1596 *c++ = '\0'; 1597 move = sresize(move, c - move, char); 1598 1599 sfree(old_guess); 1600 free_game(solve_state); 1601 return move; 1602} 1603 1604static bool game_can_format_as_text_now(const game_params *params) 1605{ 1606 return true; 1607} 1608 1609static char *game_text_format(const game_state *state) 1610{ 1611 int w,h,c,r,xi,g; 1612 char *ret; 1613 char buf[120]; 1614 1615 ret = snewn(50 + 6*(state->common->params.w +2) + 1616 6*(state->common->params.h+2) + 1617 3*(state->common->params.w * state->common->params.h), char); 1618 1619 sprintf(ret,"G: %d V: %d Z: %d\n\n",state->common->num_ghosts, 1620 state->common->num_vampires, state->common->num_zombies); 1621 1622 for (h=0;h<state->common->params.h+2;h++) { 1623 for (w=0;w<state->common->params.w+2;w++) { 1624 c = state->common->grid[w+h*(state->common->params.w+2)]; 1625 xi = state->common->xinfo[w+h*(state->common->params.w+2)]; 1626 r = grid2range(w,h,state->common->params.w,state->common->params.h); 1627 if (r != -1) { 1628 sprintf(buf,"%2d", c); strcat(ret,buf); 1629 } else if (c == CELL_MIRROR_L) { 1630 sprintf(buf," \\"); strcat(ret,buf); 1631 } else if (c == CELL_MIRROR_R) { 1632 sprintf(buf," /"); strcat(ret,buf); 1633 } else if (xi >= 0) { 1634 g = state->guess[xi]; 1635 if (g == 1) { sprintf(buf," G"); strcat(ret,buf); } 1636 else if (g == 2) { sprintf(buf," V"); strcat(ret,buf); } 1637 else if (g == 4) { sprintf(buf," Z"); strcat(ret,buf); } 1638 else { sprintf(buf," ."); strcat(ret,buf); } 1639 } else { 1640 sprintf(buf," "); strcat(ret,buf); 1641 } 1642 } 1643 sprintf(buf,"\n"); strcat(ret,buf); 1644 } 1645 1646 return ret; 1647} 1648 1649struct game_ui { 1650 int hx, hy; /* as for solo.c, highlight pos */ 1651 bool hshow, hpencil, hcursor; /* show state, type, and ?cursor. */ 1652 bool ascii; 1653 1654 /* 1655 * User preference option: if the user right-clicks in a square 1656 * and presses a monster key to add/remove a pencil mark, do we 1657 * hide the mouse highlight again afterwards? 1658 * 1659 * Historically our answer was yes. The Android port prefers no. 1660 * There are advantages both ways, depending how much you dislike 1661 * the highlight cluttering your view. So it's a preference. 1662 */ 1663 bool pencil_keep_highlight; 1664}; 1665 1666static game_ui *new_ui(const game_state *state) 1667{ 1668 game_ui *ui = snew(game_ui); 1669 ui->hpencil = false; 1670 ui->hx = ui->hy = ui->hshow = ui->hcursor = 1671 getenv_bool("PUZZLES_SHOW_CURSOR", false); 1672 ui->ascii = false; 1673 1674 ui->pencil_keep_highlight = false; 1675 1676 return ui; 1677} 1678 1679static config_item *get_prefs(game_ui *ui) 1680{ 1681 config_item *ret; 1682 1683 ret = snewn(N_PREF_ITEMS+1, config_item); 1684 1685 ret[PREF_PENCIL_KEEP_HIGHLIGHT].name = 1686 "Keep mouse highlight after changing a pencil mark"; 1687 ret[PREF_PENCIL_KEEP_HIGHLIGHT].kw = "pencil-keep-highlight"; 1688 ret[PREF_PENCIL_KEEP_HIGHLIGHT].type = C_BOOLEAN; 1689 ret[PREF_PENCIL_KEEP_HIGHLIGHT].u.boolean.bval = ui->pencil_keep_highlight; 1690 1691 ret[PREF_MONSTERS].name = "Monster representation"; 1692 ret[PREF_MONSTERS].kw = "monsters"; 1693 ret[PREF_MONSTERS].type = C_CHOICES; 1694 ret[PREF_MONSTERS].u.choices.choicenames = ":Pictures:Letters"; 1695 ret[PREF_MONSTERS].u.choices.choicekws = ":pictures:letters"; 1696 ret[PREF_MONSTERS].u.choices.selected = ui->ascii; 1697 1698 ret[N_PREF_ITEMS].name = NULL; 1699 ret[N_PREF_ITEMS].type = C_END; 1700 1701 return ret; 1702} 1703 1704static void set_prefs(game_ui *ui, const config_item *cfg) 1705{ 1706 ui->pencil_keep_highlight = cfg[PREF_PENCIL_KEEP_HIGHLIGHT].u.boolean.bval; 1707 ui->ascii = cfg[PREF_MONSTERS].u.choices.selected; 1708} 1709 1710static void free_ui(game_ui *ui) { 1711 sfree(ui); 1712 return; 1713} 1714 1715static void game_changed_state(game_ui *ui, const game_state *oldstate, 1716 const game_state *newstate) 1717{ 1718 /* See solo.c; if we were pencil-mode highlighting and 1719 * somehow a square has just been properly filled, cancel 1720 * pencil mode. */ 1721 if (ui->hshow && ui->hpencil && !ui->hcursor) { 1722 int g = newstate->guess[newstate->common->xinfo[ui->hx + ui->hy*(newstate->common->params.w+2)]]; 1723 if (g == 1 || g == 2 || g == 4) 1724 ui->hshow = false; 1725 } 1726} 1727 1728static const char *current_key_label(const game_ui *ui, 1729 const game_state *state, int button) 1730{ 1731 int xi; 1732 1733 if (ui->hshow && button == CURSOR_SELECT) 1734 return ui->hpencil ? "Ink" : "Pencil"; 1735 if (button == CURSOR_SELECT2) { 1736 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; 1737 if (xi >= 0 && !state->common->fixed[xi]) return "Clear"; 1738 } 1739 return ""; 1740} 1741 1742struct game_drawstate { 1743 int tilesize; 1744 bool started, solved; 1745 int w, h; 1746 1747 int *monsters; 1748 unsigned char *pencils; 1749 1750 bool count_errors[3]; 1751 bool *cell_errors; 1752 bool *hint_errors; 1753 bool *hints_done; 1754 1755 int hx, hy; 1756 bool hshow, hpencil; /* as for game_ui. */ 1757 bool hflash; 1758 bool ascii; 1759}; 1760 1761static bool is_clue(const game_state *state, int x, int y) 1762{ 1763 int h = state->common->params.h, w = state->common->params.w; 1764 1765 if (((x == 0 || x == w + 1) && y > 0 && y <= h) || 1766 ((y == 0 || y == h + 1) && x > 0 && x <= w)) 1767 return true; 1768 1769 return false; 1770} 1771 1772static int clue_index(const game_state *state, int x, int y) 1773{ 1774 int h = state->common->params.h, w = state->common->params.w; 1775 1776 if (y == 0) 1777 return x - 1; 1778 else if (x == w + 1) 1779 return w + y - 1; 1780 else if (y == h + 1) 1781 return 2 * w + h - x; 1782 else if (x == 0) 1783 return 2 * (w + h) - y; 1784 1785 return -1; 1786} 1787 1788#define TILESIZE (ds->tilesize) 1789#define BORDER (TILESIZE/4) 1790 1791static char *interpret_move(const game_state *state, game_ui *ui, 1792 const game_drawstate *ds, 1793 int x, int y, int button) 1794{ 1795 int gx,gy; 1796 int g,xi; 1797 char buf[80]; 1798 1799 gx = ((x-BORDER-1) / TILESIZE ); 1800 gy = ((y-BORDER-2) / TILESIZE ) - 1; 1801 1802 if (button == 'a' || button == 'A') { 1803 ui->ascii = !ui->ascii; 1804 return MOVE_UI_UPDATE; 1805 } 1806 1807 if (button == 'm' || button == 'M') { 1808 return dupstr("M"); 1809 } 1810 1811 if (ui->hshow && !ui->hpencil) { 1812 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; 1813 if (xi >= 0 && !state->common->fixed[xi]) { 1814 if (button == 'g' || button == 'G' || button == '1') { 1815 if (!ui->hcursor) ui->hshow = false; 1816 if (state->guess[xi] == 1) 1817 return ui->hcursor ? NULL : MOVE_UI_UPDATE; 1818 sprintf(buf,"G%d",xi); 1819 return dupstr(buf); 1820 } 1821 if (button == 'v' || button == 'V' || button == '2') { 1822 if (!ui->hcursor) ui->hshow = false; 1823 if (state->guess[xi] == 2) 1824 return ui->hcursor ? NULL : MOVE_UI_UPDATE; 1825 sprintf(buf,"V%d",xi); 1826 return dupstr(buf); 1827 } 1828 if (button == 'z' || button == 'Z' || button == '3') { 1829 if (!ui->hcursor) ui->hshow = false; 1830 if (state->guess[xi] == 4) 1831 return ui->hcursor ? NULL : MOVE_UI_UPDATE; 1832 sprintf(buf,"Z%d",xi); 1833 return dupstr(buf); 1834 } 1835 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 || 1836 button == '0' || button == '\b' ) { 1837 if (!ui->hcursor) ui->hshow = false; 1838 if (state->guess[xi] == 7 && state->pencils[xi] == 0) 1839 return ui->hcursor ? NULL : MOVE_UI_UPDATE; 1840 sprintf(buf,"E%d",xi); 1841 return dupstr(buf); 1842 } 1843 } 1844 } 1845 1846 if (IS_CURSOR_MOVE(button)) { 1847 if (ui->hx == 0 && ui->hy == 0) { 1848 ui->hx = 1; 1849 ui->hy = 1; 1850 } 1851 else switch (button) { 1852 case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break; 1853 case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break; 1854 case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break; 1855 case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break; 1856 } 1857 ui->hshow = true; 1858 ui->hcursor = true; 1859 return MOVE_UI_UPDATE; 1860 } 1861 if (ui->hshow && button == CURSOR_SELECT) { 1862 ui->hpencil = !ui->hpencil; 1863 ui->hcursor = true; 1864 return MOVE_UI_UPDATE; 1865 } 1866 1867 if (ui->hshow && ui->hpencil) { 1868 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)]; 1869 if (xi >= 0 && !state->common->fixed[xi]) { 1870 buf[0] = '\0'; 1871 1872 if (button == 'g' || button == 'G' || button == '1') { 1873 sprintf(buf,"g%d",xi); 1874 } else if (button == 'v' || button == 'V' || button == '2') { 1875 sprintf(buf,"v%d",xi); 1876 } else if (button == 'z' || button == 'Z' || button == '3') { 1877 sprintf(buf,"z%d",xi); 1878 } else if (button == 'e' || button == 'E' || 1879 button == CURSOR_SELECT2 || button == '0' || 1880 button == '\b') { 1881 if (state->pencils[xi] == 0) 1882 return ui->hcursor ? NULL : MOVE_UI_UPDATE; 1883 sprintf(buf,"E%d",xi); 1884 } 1885 1886 if (buf[0]) { 1887 /* 1888 * Hide the highlight after a keypress, if it was mouse- 1889 * generated. Also, don't hide it if this move has changed 1890 * pencil marks and the user preference says not to hide the 1891 * highlight in that situation. 1892 */ 1893 if (!ui->hcursor && 1894 !(ui->hpencil && ui->pencil_keep_highlight)) { 1895 ui->hpencil = false; 1896 ui->hshow = false; 1897 } 1898 return dupstr(buf); 1899 } 1900 } 1901 } 1902 1903 if (gx > 0 && gx < ds->w+1 && gy > 0 && gy < ds->h+1) { 1904 xi = state->common->xinfo[gx+gy*(state->common->params.w+2)]; 1905 if (xi >= 0 && !state->common->fixed[xi]) { 1906 g = state->guess[xi]; 1907 if (!ui->hshow) { 1908 if (button == LEFT_BUTTON) { 1909 ui->hshow = true; 1910 ui->hpencil = false; 1911 ui->hcursor = false; 1912 ui->hx = gx; ui->hy = gy; 1913 return MOVE_UI_UPDATE; 1914 } 1915 else if (button == RIGHT_BUTTON && g == 7) { 1916 ui->hshow = true; 1917 ui->hpencil = true; 1918 ui->hcursor = false; 1919 ui->hx = gx; ui->hy = gy; 1920 return MOVE_UI_UPDATE; 1921 } 1922 } 1923 else if (ui->hshow) { 1924 if (button == LEFT_BUTTON) { 1925 if (!ui->hpencil) { 1926 if (gx == ui->hx && gy == ui->hy) { 1927 ui->hshow = false; 1928 ui->hpencil = false; 1929 ui->hcursor = false; 1930 ui->hx = 0; ui->hy = 0; 1931 return MOVE_UI_UPDATE; 1932 } 1933 else { 1934 ui->hshow = true; 1935 ui->hpencil = false; 1936 ui->hcursor = false; 1937 ui->hx = gx; ui->hy = gy; 1938 return MOVE_UI_UPDATE; 1939 } 1940 } 1941 else { 1942 ui->hshow = true; 1943 ui->hpencil = false; 1944 ui->hcursor = false; 1945 ui->hx = gx; ui->hy = gy; 1946 return MOVE_UI_UPDATE; 1947 } 1948 } 1949 else if (button == RIGHT_BUTTON) { 1950 if (!ui->hpencil && g == 7) { 1951 ui->hshow = true; 1952 ui->hpencil = true; 1953 ui->hcursor = false; 1954 ui->hx = gx; ui->hy = gy; 1955 return MOVE_UI_UPDATE; 1956 } 1957 else { 1958 if (gx == ui->hx && gy == ui->hy) { 1959 ui->hshow = false; 1960 ui->hpencil = false; 1961 ui->hcursor = false; 1962 ui->hx = 0; ui->hy = 0; 1963 return MOVE_UI_UPDATE; 1964 } 1965 else if (g == 7) { 1966 ui->hshow = true; 1967 ui->hpencil = true; 1968 ui->hcursor = false; 1969 ui->hx = gx; ui->hy = gy; 1970 return MOVE_UI_UPDATE; 1971 } 1972 } 1973 } 1974 } 1975 } 1976 } else if (button == LEFT_BUTTON) { 1977 if (is_clue(state, gx, gy)) { 1978 sprintf(buf, "D%d,%d", gx, gy); 1979 return dupstr(buf); 1980 } 1981 } 1982 1983 return NULL; 1984} 1985 1986static bool check_numbers_draw(game_state *state, int *guess) { 1987 bool valid, filled; 1988 int i,x,y,xy; 1989 int count_ghosts, count_vampires, count_zombies; 1990 1991 count_ghosts = count_vampires = count_zombies = 0; 1992 for (i=0;i<state->common->num_total;i++) { 1993 if (guess[i] == 1) count_ghosts++; 1994 if (guess[i] == 2) count_vampires++; 1995 if (guess[i] == 4) count_zombies++; 1996 } 1997 1998 valid = true; 1999 filled = (count_ghosts + count_vampires + count_zombies >= 2000 state->common->num_total); 2001 2002 if (count_ghosts > state->common->num_ghosts || 2003 (filled && count_ghosts != state->common->num_ghosts) ) { 2004 valid = false; 2005 state->count_errors[0] = true; 2006 for (x=1;x<state->common->params.w+1;x++) 2007 for (y=1;y<state->common->params.h+1;y++) { 2008 xy = x+y*(state->common->params.w+2); 2009 if (state->common->xinfo[xy] >= 0 && 2010 guess[state->common->xinfo[xy]] == 1) 2011 state->cell_errors[xy] = true; 2012 } 2013 } 2014 if (count_vampires > state->common->num_vampires || 2015 (filled && count_vampires != state->common->num_vampires) ) { 2016 valid = false; 2017 state->count_errors[1] = true; 2018 for (x=1;x<state->common->params.w+1;x++) 2019 for (y=1;y<state->common->params.h+1;y++) { 2020 xy = x+y*(state->common->params.w+2); 2021 if (state->common->xinfo[xy] >= 0 && 2022 guess[state->common->xinfo[xy]] == 2) 2023 state->cell_errors[xy] = true; 2024 } 2025 } 2026 if (count_zombies > state->common->num_zombies || 2027 (filled && count_zombies != state->common->num_zombies) ) { 2028 valid = false; 2029 state->count_errors[2] = true; 2030 for (x=1;x<state->common->params.w+1;x++) 2031 for (y=1;y<state->common->params.h+1;y++) { 2032 xy = x+y*(state->common->params.w+2); 2033 if (state->common->xinfo[xy] >= 0 && 2034 guess[state->common->xinfo[xy]] == 4) 2035 state->cell_errors[xy] = true; 2036 } 2037 } 2038 2039 return valid; 2040} 2041 2042static bool check_path_solution(game_state *state, int p) { 2043 int i; 2044 bool mirror; 2045 int count; 2046 bool correct; 2047 int unfilled; 2048 2049 count = 0; 2050 mirror = false; 2051 correct = true; 2052 2053 unfilled = 0; 2054 for (i=0;i<state->common->paths[p].length;i++) { 2055 if (state->common->paths[p].p[i] == -1) mirror = true; 2056 else { 2057 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) 2058 count++; 2059 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) 2060 count++; 2061 else if (state->guess[state->common->paths[p].p[i]] == 4) 2062 count++; 2063 else if (state->guess[state->common->paths[p].p[i]] == 7) 2064 unfilled++; 2065 } 2066 } 2067 2068 if (count > state->common->paths[p].sightings_start || 2069 count + unfilled < state->common->paths[p].sightings_start) 2070 { 2071 correct = false; 2072 state->hint_errors[state->common->paths[p].grid_start] = true; 2073 } 2074 2075 count = 0; 2076 mirror = false; 2077 unfilled = 0; 2078 for (i=state->common->paths[p].length-1;i>=0;i--) { 2079 if (state->common->paths[p].p[i] == -1) mirror = true; 2080 else { 2081 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror) 2082 count++; 2083 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror) 2084 count++; 2085 else if (state->guess[state->common->paths[p].p[i]] == 4) 2086 count++; 2087 else if (state->guess[state->common->paths[p].p[i]] == 7) 2088 unfilled++; 2089 } 2090 } 2091 2092 if (count > state->common->paths[p].sightings_end || 2093 count + unfilled < state->common->paths[p].sightings_end) 2094 { 2095 correct = false; 2096 state->hint_errors[state->common->paths[p].grid_end] = true; 2097 } 2098 2099 if (!correct) { 2100 for (i=0;i<state->common->paths[p].length;i++) 2101 state->cell_errors[state->common->paths[p].xy[i]] = true; 2102 } 2103 2104 return correct; 2105} 2106 2107static game_state *execute_move(const game_state *state, const char *move) 2108{ 2109 int x,y,n,p,i; 2110 char c; 2111 bool correct; 2112 bool solver; 2113 2114 game_state *ret = dup_game(state); 2115 solver = false; 2116 2117 while (*move) { 2118 c = *move; 2119 if (c == 'S') { 2120 move++; 2121 solver = true; 2122 } else if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' || 2123 c == 'g' || c == 'v' || c == 'z') { 2124 move++; 2125 if (sscanf(move, "%d%n", &x, &n) != 1) goto badmove; 2126 if (x < 0 || x >= ret->common->num_total) goto badmove; 2127 if (c == 'G') ret->guess[x] = 1; 2128 if (c == 'V') ret->guess[x] = 2; 2129 if (c == 'Z') ret->guess[x] = 4; 2130 if (c == 'E') { ret->guess[x] = 7; ret->pencils[x] = 0; } 2131 if (c == 'g') ret->pencils[x] ^= 1; 2132 if (c == 'v') ret->pencils[x] ^= 2; 2133 if (c == 'z') ret->pencils[x] ^= 4; 2134 move += n; 2135 } else if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 && 2136 is_clue(ret, x, y)) { 2137 ret->hints_done[clue_index(ret, x, y)] ^= 1; 2138 move += n + 1; 2139 } else if (c == 'M') { 2140 /* 2141 * Fill in absolutely all pencil marks in unfilled 2142 * squares, for those who like to play by the rigorous 2143 * approach of starting off in that state and eliminating 2144 * things. 2145 */ 2146 for (i = 0; i < ret->common->num_total; i++) 2147 if (ret->guess[i] == 7) 2148 ret->pencils[i] = 7; 2149 move++; 2150 } else { 2151 /* Unknown move type. */ 2152 badmove: 2153 free_game(ret); 2154 return NULL; 2155 } 2156 if (*move == ';') move++; 2157 } 2158 2159 correct = true; 2160 2161 for (i=0;i<ret->common->wh;i++) ret->cell_errors[i] = false; 2162 for (i=0;i<2*ret->common->num_paths;i++) ret->hint_errors[i] = false; 2163 for (i=0;i<3;i++) ret->count_errors[i] = false; 2164 2165 if (!check_numbers_draw(ret,ret->guess)) correct = false; 2166 2167 for (p=0;p<state->common->num_paths;p++) 2168 if (!check_path_solution(ret,p)) correct = false; 2169 2170 for (i=0;i<state->common->num_total;i++) 2171 if (!(ret->guess[i] == 1 || ret->guess[i] == 2 || 2172 ret->guess[i] == 4)) correct = false; 2173 2174 if (correct && !solver) ret->solved = true; 2175 if (solver) ret->cheated = true; 2176 2177 return ret; 2178} 2179 2180/* ---------------------------------------------------------------------- 2181 * Drawing routines. 2182 */ 2183 2184#define PREFERRED_TILE_SIZE 64 2185 2186static void game_compute_size(const game_params *params, int tilesize, 2187 const game_ui *ui, int *x, int *y) 2188{ 2189 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2190 struct { int tilesize; } ads, *ds = &ads; 2191 ads.tilesize = tilesize; 2192 2193 *x = 2*BORDER+(params->w+2)*TILESIZE; 2194 *y = 2*BORDER+(params->h+3)*TILESIZE; 2195 return; 2196} 2197 2198static void game_set_size(drawing *dr, game_drawstate *ds, 2199 const game_params *params, int tilesize) 2200{ 2201 ds->tilesize = tilesize; 2202 return; 2203} 2204 2205#define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b))) 2206 2207static float *game_colours(frontend *fe, int *ncolours) 2208{ 2209 float *ret = snewn(3 * NCOLOURS, float); 2210 2211 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 2212 2213 ret[COL_GRID * 3 + 0] = 0.0F; 2214 ret[COL_GRID * 3 + 1] = 0.0F; 2215 ret[COL_GRID * 3 + 2] = 0.0F; 2216 2217 ret[COL_TEXT * 3 + 0] = 0.0F; 2218 ret[COL_TEXT * 3 + 1] = 0.0F; 2219 ret[COL_TEXT * 3 + 2] = 0.0F; 2220 2221 ret[COL_ERROR * 3 + 0] = 1.0F; 2222 ret[COL_ERROR * 3 + 1] = 0.0F; 2223 ret[COL_ERROR * 3 + 2] = 0.0F; 2224 2225 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0]; 2226 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1]; 2227 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2]; 2228 2229 ret[COL_FLASH * 3 + 0] = 1.0F; 2230 ret[COL_FLASH * 3 + 1] = 1.0F; 2231 ret[COL_FLASH * 3 + 2] = 1.0F; 2232 2233 ret[COL_GHOST * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; 2234 ret[COL_GHOST * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; 2235 ret[COL_GHOST * 3 + 2] = ret[COL_BACKGROUND * 3 + 0]; 2236 2237 ret[COL_ZOMBIE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; 2238 ret[COL_ZOMBIE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0]; 2239 ret[COL_ZOMBIE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.5F; 2240 2241 ret[COL_VAMPIRE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0]; 2242 ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; 2243 ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F; 2244 2245 ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F; 2246 ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F; 2247 ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F; 2248 2249 *ncolours = NCOLOURS; 2250 return ret; 2251} 2252 2253static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) 2254{ 2255 int i; 2256 struct game_drawstate *ds = snew(struct game_drawstate); 2257 2258 ds->tilesize = 0; 2259 ds->started = ds->solved = false; 2260 ds->w = state->common->params.w; 2261 ds->h = state->common->params.h; 2262 ds->ascii = false; 2263 2264 ds->count_errors[0] = false; 2265 ds->count_errors[1] = false; 2266 ds->count_errors[2] = false; 2267 2268 ds->monsters = snewn(state->common->num_total,int); 2269 for (i=0;i<(state->common->num_total);i++) 2270 ds->monsters[i] = 7; 2271 ds->pencils = snewn(state->common->num_total,unsigned char); 2272 for (i=0;i<state->common->num_total;i++) 2273 ds->pencils[i] = 0; 2274 2275 ds->cell_errors = snewn(state->common->wh,bool); 2276 for (i=0;i<state->common->wh;i++) 2277 ds->cell_errors[i] = false; 2278 ds->hint_errors = snewn(2*state->common->num_paths,bool); 2279 for (i=0;i<2*state->common->num_paths;i++) 2280 ds->hint_errors[i] = false; 2281 ds->hints_done = snewn(2 * state->common->num_paths, bool); 2282 memset(ds->hints_done, 0, 2283 2 * state->common->num_paths * sizeof(bool)); 2284 2285 ds->hshow = false; 2286 ds->hpencil = false; 2287 ds->hflash = false; 2288 ds->hx = ds->hy = 0; 2289 return ds; 2290} 2291 2292static void game_free_drawstate(drawing *dr, game_drawstate *ds) { 2293 sfree(ds->hints_done); 2294 sfree(ds->hint_errors); 2295 sfree(ds->cell_errors); 2296 sfree(ds->pencils); 2297 sfree(ds->monsters); 2298 sfree(ds); 2299 return; 2300} 2301 2302static void draw_cell_background(drawing *dr, game_drawstate *ds, 2303 const game_state *state, const game_ui *ui, 2304 int x, int y) { 2305 2306 bool hon; 2307 int dx,dy; 2308 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); 2309 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; 2310 2311 hon = (ui->hshow && x == ui->hx && y == ui->hy); 2312 draw_rect(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1,(hon && !ui->hpencil) ? COL_HIGHLIGHT : COL_BACKGROUND); 2313 2314 if (hon && ui->hpencil) { 2315 int coords[6]; 2316 coords[0] = dx-(TILESIZE/2)+1; 2317 coords[1] = dy-(TILESIZE/2)+1; 2318 coords[2] = coords[0] + TILESIZE/2; 2319 coords[3] = coords[1]; 2320 coords[4] = coords[0]; 2321 coords[5] = coords[1] + TILESIZE/2; 2322 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); 2323 } 2324 2325 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); 2326 2327 return; 2328} 2329 2330static void draw_circle_or_point(drawing *dr, int cx, int cy, int radius, 2331 int colour) 2332{ 2333 if (radius > 0) 2334 draw_circle(dr, cx, cy, radius, colour, colour); 2335 else 2336 draw_rect(dr, cx, cy, 1, 1, colour); 2337} 2338 2339static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y, 2340 int tilesize, bool hflash, int monster) 2341{ 2342 int black = (hflash ? COL_FLASH : COL_TEXT); 2343 2344 if (monster == 1) { /* ghost */ 2345 int poly[80], i, j; 2346 2347 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2+1); 2348 draw_circle(dr,x,y,2*tilesize/5, COL_GHOST,black); 2349 unclip(dr); 2350 2351 i = 0; 2352 poly[i++] = x - 2*tilesize/5; 2353 poly[i++] = y-2; 2354 poly[i++] = x - 2*tilesize/5; 2355 poly[i++] = y + 2*tilesize/5; 2356 2357 for (j = 0; j < 3; j++) { 2358 int total = (2*tilesize/5) * 2; 2359 int before = total * j / 3; 2360 int after = total * (j+1) / 3; 2361 int mid = (before + after) / 2; 2362 poly[i++] = x - 2*tilesize/5 + mid; 2363 poly[i++] = y + 2*tilesize/5 - (total / 6); 2364 poly[i++] = x - 2*tilesize/5 + after; 2365 poly[i++] = y + 2*tilesize/5; 2366 } 2367 2368 poly[i++] = x + 2*tilesize/5; 2369 poly[i++] = y-2; 2370 2371 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize-(tilesize/2)-1); 2372 draw_polygon(dr, poly, i/2, COL_GHOST, black); 2373 unclip(dr); 2374 2375 draw_circle(dr,x-tilesize/6,y-tilesize/12,tilesize/10, 2376 COL_BACKGROUND,black); 2377 draw_circle(dr,x+tilesize/6,y-tilesize/12,tilesize/10, 2378 COL_BACKGROUND,black); 2379 2380 draw_circle_or_point(dr,x-tilesize/6+1+tilesize/48,y-tilesize/12, 2381 tilesize/48,black); 2382 draw_circle_or_point(dr,x+tilesize/6+1+tilesize/48,y-tilesize/12, 2383 tilesize/48,black); 2384 2385 } else if (monster == 2) { /* vampire */ 2386 int poly[80], i; 2387 2388 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2); 2389 draw_circle(dr,x,y,2*tilesize/5,black,black); 2390 unclip(dr); 2391 2392 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); 2393 draw_circle(dr,x-tilesize/7,y,2*tilesize/5-tilesize/7, 2394 COL_VAMPIRE,black); 2395 unclip(dr); 2396 clip(dr,x,y-(tilesize/2)+2,tilesize/2+1,tilesize/2); 2397 draw_circle(dr,x+tilesize/7,y,2*tilesize/5-tilesize/7, 2398 COL_VAMPIRE,black); 2399 unclip(dr); 2400 2401 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize/2); 2402 draw_circle(dr,x,y,2*tilesize/5, COL_VAMPIRE,black); 2403 unclip(dr); 2404 2405 draw_circle(dr, x-tilesize/7, y-tilesize/16, tilesize/16, 2406 COL_BACKGROUND, black); 2407 draw_circle(dr, x+tilesize/7, y-tilesize/16, tilesize/16, 2408 COL_BACKGROUND, black); 2409 draw_circle_or_point(dr, x-tilesize/7, y-tilesize/16, tilesize/48, 2410 black); 2411 draw_circle_or_point(dr, x+tilesize/7, y-tilesize/16, tilesize/48, 2412 black); 2413 2414 clip(dr, x-(tilesize/2)+2, y+tilesize/8, tilesize-3, tilesize/4); 2415 2416 i = 0; 2417 poly[i++] = x-3*tilesize/16; 2418 poly[i++] = y+1*tilesize/8; 2419 poly[i++] = x-2*tilesize/16; 2420 poly[i++] = y+7*tilesize/24; 2421 poly[i++] = x-1*tilesize/16; 2422 poly[i++] = y+1*tilesize/8; 2423 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); 2424 i = 0; 2425 poly[i++] = x+3*tilesize/16; 2426 poly[i++] = y+1*tilesize/8; 2427 poly[i++] = x+2*tilesize/16; 2428 poly[i++] = y+7*tilesize/24; 2429 poly[i++] = x+1*tilesize/16; 2430 poly[i++] = y+1*tilesize/8; 2431 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black); 2432 2433 draw_circle(dr, x, y-tilesize/5, 2*tilesize/5, COL_VAMPIRE, black); 2434 unclip(dr); 2435 2436 } else if (monster == 4) { /* zombie */ 2437 draw_circle(dr,x,y,2*tilesize/5, COL_ZOMBIE,black); 2438 2439 draw_line(dr, 2440 x-tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, 2441 x-tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, 2442 black); 2443 draw_line(dr, 2444 x-tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, 2445 x-tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, 2446 black); 2447 draw_line(dr, 2448 x+tilesize/7-tilesize/16, y-tilesize/12-tilesize/16, 2449 x+tilesize/7+tilesize/16, y-tilesize/12+tilesize/16, 2450 black); 2451 draw_line(dr, 2452 x+tilesize/7+tilesize/16, y-tilesize/12-tilesize/16, 2453 x+tilesize/7-tilesize/16, y-tilesize/12+tilesize/16, 2454 black); 2455 2456 clip(dr, x-tilesize/5, y+tilesize/6, 2*tilesize/5+1, tilesize/2); 2457 draw_circle(dr, x-tilesize/15, y+tilesize/6, tilesize/12, 2458 COL_BACKGROUND, black); 2459 unclip(dr); 2460 2461 draw_line(dr, x-tilesize/5, y+tilesize/6, x+tilesize/5, y+tilesize/6, 2462 black); 2463 } 2464 2465 draw_update(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize-3); 2466} 2467 2468static void draw_monster_count(drawing *dr, game_drawstate *ds, 2469 const game_state *state, int c, bool hflash) { 2470 int dx,dy; 2471 char buf[MAX_DIGITS(int) + 1]; 2472 char bufm[8]; 2473 2474 dy = TILESIZE/4; 2475 dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4; 2476 switch (c) { 2477 case 0: 2478 sprintf(buf,"%d",state->common->num_ghosts); 2479 sprintf(bufm,"G"); 2480 dx -= 3*TILESIZE/2; 2481 break; 2482 case 1: 2483 sprintf(buf,"%d",state->common->num_vampires); 2484 sprintf(bufm,"V"); 2485 break; 2486 case 2: 2487 sprintf(buf,"%d",state->common->num_zombies); 2488 sprintf(bufm,"Z"); 2489 dx += 3*TILESIZE/2; 2490 break; 2491 } 2492 2493 draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE, 2494 COL_BACKGROUND); 2495 if (!ds->ascii) { 2496 draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2, 2497 2*TILESIZE/3, hflash, 1<<c); 2498 } else { 2499 draw_text(dr, dx-TILESIZE/3,dy+TILESIZE/2,FONT_VARIABLE,TILESIZE/2, 2500 ALIGN_HCENTRE|ALIGN_VCENTRE, 2501 hflash ? COL_FLASH : COL_TEXT, bufm); 2502 } 2503 draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2, 2504 ALIGN_HLEFT|ALIGN_VCENTRE, 2505 (state->count_errors[c] ? COL_ERROR : 2506 hflash ? COL_FLASH : COL_TEXT), buf); 2507 draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE); 2508 2509 return; 2510} 2511 2512static void draw_path_hint(drawing *dr, game_drawstate *ds, 2513 const struct game_params *params, 2514 int hint_index, bool hflash, int hint) { 2515 int x, y, color, dx, dy, text_dx, text_dy, text_size; 2516 char buf[MAX_DIGITS(int) + 1]; 2517 2518 if (ds->hint_errors[hint_index]) 2519 color = COL_ERROR; 2520 else if (hflash) 2521 color = COL_FLASH; 2522 else if (ds->hints_done[hint_index]) 2523 color = COL_DONE; 2524 else 2525 color = COL_TEXT; 2526 2527 range2grid(hint_index, params->w, params->h, &x, &y); 2528 /* Upper-left corner of the "tile" */ 2529 dx = BORDER + x * TILESIZE; 2530 dy = BORDER + y * TILESIZE + TILESIZE; 2531 /* Center of the "tile" */ 2532 text_dx = dx + TILESIZE / 2; 2533 text_dy = dy + TILESIZE / 2; 2534 /* Avoid wiping out the borders of the puzzle */ 2535 dx += 2; 2536 dy += 2; 2537 text_size = TILESIZE - 3; 2538 2539 sprintf(buf,"%d", hint); 2540 draw_rect(dr, dx, dy, text_size, text_size, COL_BACKGROUND); 2541 draw_text(dr, text_dx, text_dy, FONT_VARIABLE, TILESIZE / 2, 2542 ALIGN_HCENTRE | ALIGN_VCENTRE, color, buf); 2543 draw_update(dr, dx, dy, text_size, text_size); 2544 2545 return; 2546} 2547 2548static void draw_mirror(drawing *dr, game_drawstate *ds, 2549 const game_state *state, int x, int y, 2550 bool hflash, int mirror) { 2551 int dx,dy,mx1,my1,mx2,my2; 2552 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); 2553 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; 2554 2555 if (mirror == CELL_MIRROR_L) { 2556 mx1 = dx-(TILESIZE/4); 2557 my1 = dy-(TILESIZE/4); 2558 mx2 = dx+(TILESIZE/4); 2559 my2 = dy+(TILESIZE/4); 2560 } 2561 else { 2562 mx1 = dx-(TILESIZE/4); 2563 my1 = dy+(TILESIZE/4); 2564 mx2 = dx+(TILESIZE/4); 2565 my2 = dy-(TILESIZE/4); 2566 } 2567 draw_thick_line(dr,(float)(TILESIZE/16),mx1,my1,mx2,my2, 2568 hflash ? COL_FLASH : COL_TEXT); 2569 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1); 2570 2571 return; 2572} 2573 2574static void draw_big_monster(drawing *dr, game_drawstate *ds, 2575 const game_state *state, int x, int y, 2576 bool hflash, int monster) 2577{ 2578 int dx,dy; 2579 char buf[10]; 2580 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2); 2581 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE; 2582 if (ds->ascii) { 2583 if (monster == 1) sprintf(buf,"G"); 2584 else if (monster == 2) sprintf(buf,"V"); 2585 else if (monster == 4) sprintf(buf,"Z"); 2586 else sprintf(buf," "); 2587 draw_text(dr,dx,dy,FONT_VARIABLE,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE, 2588 hflash ? COL_FLASH : COL_TEXT,buf); 2589 draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3, 2590 TILESIZE-3); 2591 } 2592 else { 2593 draw_monster(dr, ds, dx, dy, 3*TILESIZE/4, hflash, monster); 2594 } 2595 return; 2596} 2597 2598static void draw_pencils(drawing *dr, game_drawstate *ds, 2599 const game_state *state, int x, int y, int pencil) 2600{ 2601 int dx, dy; 2602 int monsters[4]; 2603 int i, j, px, py; 2604 char buf[10]; 2605 dx = BORDER+(x* ds->tilesize)+(TILESIZE/4); 2606 dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE; 2607 2608 for (i = 0, j = 1; j < 8; j *= 2) 2609 if (pencil & j) 2610 monsters[i++] = j; 2611 while (i < 4) 2612 monsters[i++] = 0; 2613 2614 for (py = 0; py < 2; py++) 2615 for (px = 0; px < 2; px++) 2616 if (monsters[py*2+px]) { 2617 if (!ds->ascii) { 2618 draw_monster(dr, ds, 2619 dx + TILESIZE/2 * px, dy + TILESIZE/2 * py, 2620 TILESIZE/2, false, monsters[py*2+px]); 2621 } 2622 else { 2623 switch (monsters[py*2+px]) { 2624 case 1: sprintf(buf,"G"); break; 2625 case 2: sprintf(buf,"V"); break; 2626 case 4: sprintf(buf,"Z"); break; 2627 } 2628 draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py, 2629 FONT_VARIABLE,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE, 2630 COL_TEXT,buf); 2631 } 2632 } 2633 draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2, 2634 (TILESIZE/2)-3,(TILESIZE/2)-3); 2635 2636 return; 2637} 2638 2639#define FLASH_TIME 0.7F 2640 2641static bool is_hint_stale(const game_drawstate *ds, bool hflash, 2642 const game_state *state, int index) 2643{ 2644 bool ret = false; 2645 if (!ds->started) ret = true; 2646 if (ds->hflash != hflash) ret = true; 2647 2648 if (ds->hint_errors[index] != state->hint_errors[index]) { 2649 ds->hint_errors[index] = state->hint_errors[index]; 2650 ret = true; 2651 } 2652 2653 if (ds->hints_done[index] != state->hints_done[index]) { 2654 ds->hints_done[index] = state->hints_done[index]; 2655 ret = true; 2656 } 2657 2658 return ret; 2659} 2660 2661static void game_redraw(drawing *dr, game_drawstate *ds, 2662 const game_state *oldstate, const game_state *state, 2663 int dir, const game_ui *ui, 2664 float animtime, float flashtime) 2665{ 2666 int i,j,x,y,xy; 2667 int xi, c; 2668 bool stale, hflash, hchanged, changed_ascii; 2669 2670 hflash = (int)(flashtime * 5 / FLASH_TIME) % 2; 2671 2672 /* Draw static grid components at startup */ 2673 if (!ds->started) { 2674 draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1, 2675 (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID); 2676 for (i=0;i<ds->w;i++) 2677 for (j=0;j<ds->h;j++) 2678 draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1, 2679 BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1, 2680 ds->tilesize-1, COL_BACKGROUND); 2681 draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE, 2682 2*BORDER+(ds->h+3)*TILESIZE); 2683 } 2684 2685 hchanged = false; 2686 if (ds->hx != ui->hx || ds->hy != ui->hy || 2687 ds->hshow != ui->hshow || ds->hpencil != ui->hpencil) 2688 hchanged = true; 2689 2690 if (ds->ascii != ui->ascii) { 2691 ds->ascii = ui->ascii; 2692 changed_ascii = true; 2693 } else 2694 changed_ascii = false; 2695 2696 /* Draw monster count hints */ 2697 2698 for (i=0;i<3;i++) { 2699 stale = false; 2700 if (!ds->started) stale = true; 2701 if (ds->hflash != hflash) stale = true; 2702 if (changed_ascii) stale = true; 2703 if (ds->count_errors[i] != state->count_errors[i]) { 2704 stale = true; 2705 ds->count_errors[i] = state->count_errors[i]; 2706 } 2707 2708 if (stale) { 2709 draw_monster_count(dr, ds, state, i, hflash); 2710 } 2711 } 2712 2713 /* Draw path count hints */ 2714 for (i=0;i<state->common->num_paths;i++) { 2715 struct path *path = &state->common->paths[i]; 2716 2717 if (is_hint_stale(ds, hflash, state, path->grid_start)) { 2718 draw_path_hint(dr, ds, &state->common->params, path->grid_start, 2719 hflash, path->sightings_start); 2720 } 2721 2722 if (is_hint_stale(ds, hflash, state, path->grid_end)) { 2723 draw_path_hint(dr, ds, &state->common->params, path->grid_end, 2724 hflash, path->sightings_end); 2725 } 2726 } 2727 2728 /* Draw puzzle grid contents */ 2729 for (x = 1; x < ds->w+1; x++) 2730 for (y = 1; y < ds->h+1; y++) { 2731 stale = false; 2732 xy = x+y*(state->common->params.w+2); 2733 xi = state->common->xinfo[xy]; 2734 c = state->common->grid[xy]; 2735 2736 if (!ds->started) stale = true; 2737 if (ds->hflash != hflash) stale = true; 2738 if (changed_ascii) stale = true; 2739 2740 if (hchanged) { 2741 if ((x == ui->hx && y == ui->hy) || 2742 (x == ds->hx && y == ds->hy)) 2743 stale = true; 2744 } 2745 2746 if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) { 2747 stale = true; 2748 ds->monsters[xi] = state->guess[xi]; 2749 } 2750 2751 if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) { 2752 stale = true; 2753 ds->pencils[xi] = state->pencils[xi]; 2754 } 2755 2756 if (state->cell_errors[xy] != ds->cell_errors[xy]) { 2757 stale = true; 2758 ds->cell_errors[xy] = state->cell_errors[xy]; 2759 } 2760 2761 if (stale) { 2762 draw_cell_background(dr, ds, state, ui, x, y); 2763 if (xi < 0) 2764 draw_mirror(dr, ds, state, x, y, hflash, c); 2765 else if (state->guess[xi] == 1 || state->guess[xi] == 2 || 2766 state->guess[xi] == 4) 2767 draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]); 2768 else 2769 draw_pencils(dr, ds, state, x, y, state->pencils[xi]); 2770 } 2771 } 2772 2773 ds->hx = ui->hx; ds->hy = ui->hy; 2774 ds->hshow = ui->hshow; 2775 ds->hpencil = ui->hpencil; 2776 ds->hflash = hflash; 2777 ds->started = true; 2778 return; 2779} 2780 2781static float game_anim_length(const game_state *oldstate, 2782 const game_state *newstate, int dir, game_ui *ui) 2783{ 2784 return 0.0F; 2785} 2786 2787static float game_flash_length(const game_state *oldstate, 2788 const game_state *newstate, int dir, game_ui *ui) 2789{ 2790 return (!oldstate->solved && newstate->solved && !oldstate->cheated && 2791 !newstate->cheated) ? FLASH_TIME : 0.0F; 2792} 2793 2794static void game_get_cursor_location(const game_ui *ui, 2795 const game_drawstate *ds, 2796 const game_state *state, 2797 const game_params *params, 2798 int *x, int *y, int *w, int *h) 2799{ 2800 if(ui->hshow) { 2801 *x = BORDER + (ui->hx) * TILESIZE; 2802 *y = BORDER + (ui->hy + 1) * TILESIZE; 2803 *w = *h = TILESIZE; 2804 } 2805} 2806 2807static int game_status(const game_state *state) 2808{ 2809 return state->solved; 2810} 2811 2812#ifdef COMBINED 2813#define thegame undead 2814#endif 2815 2816const struct game thegame = { 2817 "Undead", "games.undead", "undead", 2818 default_params, 2819 game_fetch_preset, NULL, 2820 decode_params, 2821 encode_params, 2822 free_params, 2823 dup_params, 2824 true, game_configure, custom_params, 2825 validate_params, 2826 new_game_desc, 2827 validate_desc, 2828 new_game, 2829 dup_game, 2830 free_game, 2831 true, solve_game, 2832 true, game_can_format_as_text_now, game_text_format, 2833 get_prefs, set_prefs, 2834 new_ui, 2835 free_ui, 2836 NULL, /* encode_ui */ 2837 NULL, /* decode_ui */ 2838 game_request_keys, 2839 game_changed_state, 2840 current_key_label, 2841 interpret_move, 2842 execute_move, 2843 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 2844 game_colours, 2845 game_new_drawstate, 2846 game_free_drawstate, 2847 game_redraw, 2848 game_anim_length, 2849 game_flash_length, 2850 game_get_cursor_location, 2851 game_status, 2852 false, false, NULL, NULL, /* print_size, print */ 2853 false, /* wants_statusbar */ 2854 false, NULL, /* timing_state */ 2855 0, /* flags */ 2856};