2 * undead: Implementation of Haunted Mirror Mazes
4 * http://www.janko.at/Raetsel/Spukschloss/index.htm
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)
10 * Example: (Janko puzzle No. 1,
11 * http://www.janko.at/Raetsel/Spukschloss/001.a.htm )
13 * Ghosts: 0 Vampires: 2 Zombies: 6
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
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
60 #define ENUM(upper,title,lower) DIFF_ ## upper,
61 #define TITLE(upper,title,lower) #title,
62 #define ENCODE(upper,title,lower) #lower
63 #define CONFIG(upper,title,lower) ":" #title
64 enum { DIFFLIST(ENUM) DIFFCOUNT };
65 static char const *const undead_diffnames[] = { DIFFLIST(TITLE) "(count)" };
66 static char const undead_diffchars[] = DIFFLIST(ENCODE);
67 #define DIFFCONFIG DIFFLIST(CONFIG)
70 int w; /* Grid width */
71 int h; /* Grid height */
72 int diff; /* Puzzle difficulty */
75 static const struct game_params undead_presets[] = {
77 { 4, 4, DIFF_NORMAL },
78 { 4, 4, DIFF_TRICKY },
80 { 5, 5, DIFF_NORMAL },
81 { 5, 5, DIFF_TRICKY },
86 #define DEFAULT_PRESET 1
88 static game_params *default_params(void) {
89 game_params *ret = snew(game_params);
91 *ret = undead_presets[DEFAULT_PRESET];
95 static int game_fetch_preset(int i, char **name, game_params **params) {
99 if (i < 0 || i >= lenof(undead_presets)) return FALSE;
101 ret = default_params();
102 *ret = undead_presets[i]; /* struct copy */
105 sprintf(buf, "%dx%d %s",
106 undead_presets[i].w, undead_presets[i].h,
107 undead_diffnames[undead_presets[i].diff]);
113 static void free_params(game_params *params) {
117 static game_params *dup_params(const game_params *params)
119 game_params *ret = snew(game_params);
120 *ret = *params; /* structure copy */
124 static void decode_params(game_params *params, char const *string) {
125 params->w = params->h = atoi(string);
127 while (*string && isdigit((unsigned char) *string)) ++string;
128 if (*string == 'x') {
130 params->h = atoi(string);
131 while (*string && isdigit((unsigned char)*string)) string++;
134 params->diff = DIFF_NORMAL;
135 if (*string == 'd') {
138 for (i = 0; i < DIFFCOUNT; i++)
139 if (*string == undead_diffchars[i])
141 if (*string) string++;
147 static char *encode_params(const game_params *params, int full)
150 sprintf(buf, "%dx%d", params->w, params->h);
152 sprintf(buf + strlen(buf), "d%c", undead_diffchars[params->diff]);
156 static config_item *game_configure(const game_params *params)
161 ret = snewn(4, config_item);
163 ret[0].name = "Width";
164 ret[0].type = C_STRING;
165 sprintf(buf, "%d", params->w);
166 ret[0].sval = dupstr(buf);
169 ret[1].name = "Height";
170 ret[1].type = C_STRING;
171 sprintf(buf, "%d", params->h);
172 ret[1].sval = dupstr(buf);
175 ret[2].name = "Difficulty";
176 ret[2].type = C_CHOICES;
177 ret[2].sval = DIFFCONFIG;
178 ret[2].ival = params->diff;
188 static game_params *custom_params(const config_item *cfg)
190 game_params *ret = snew(game_params);
192 ret->w = atoi(cfg[0].sval);
193 ret->h = atoi(cfg[1].sval);
194 ret->diff = cfg[2].ival;
198 static char *validate_params(const game_params *params, int full)
200 if ((params->w * params->h ) > 54) return "Grid is too big";
201 if (params->w < 3) return "Width must be at least 3";
202 if (params->h < 3) return "Height must be at least 3";
203 if (params->diff >= DIFFCOUNT) return "Unknown difficulty rating";
207 /* --------------------------------------------------------------- */
208 /* Game state allocation, deallocation. */
224 struct game_params params;
226 int num_ghosts,num_vampires,num_zombies,num_total;
236 struct game_common *common;
238 unsigned char *pencils;
239 unsigned char *cell_errors;
240 unsigned char *hint_errors;
241 unsigned char *hints_done;
242 unsigned char count_errors[3];
247 static game_state *new_state(const game_params *params) {
249 game_state *state = snew(game_state);
250 state->common = snew(struct game_common);
252 state->common->refcount = 1;
253 state->common->params.w = params->w;
254 state->common->params.h = params->h;
255 state->common->params.diff = params->diff;
257 state->common->wh = (state->common->params.w +2) * (state->common->params.h +2);
259 state->common->num_ghosts = 0;
260 state->common->num_vampires = 0;
261 state->common->num_zombies = 0;
262 state->common->num_total = 0;
264 state->common->grid = snewn(state->common->wh, int);
265 state->common->xinfo = snewn(state->common->wh, int);
266 state->common->fixed = NULL;
267 state->common->solved = FALSE;
269 state->common->num_paths =
270 state->common->params.w + state->common->params.h;
271 state->common->paths = snewn(state->common->num_paths, struct path);
273 for (i=0;i<state->common->num_paths;i++) {
274 state->common->paths[i].length = 0;
275 state->common->paths[i].grid_start = -1;
276 state->common->paths[i].grid_end = -1;
277 state->common->paths[i].num_monsters = 0;
278 state->common->paths[i].sightings_start = 0;
279 state->common->paths[i].sightings_end = 0;
280 state->common->paths[i].p = snewn(state->common->wh,int);
281 state->common->paths[i].xy = snewn(state->common->wh,int);
282 state->common->paths[i].mapping = snewn(state->common->wh,int);
286 state->pencils = NULL;
288 state->cell_errors = snewn(state->common->wh, unsigned char);
289 for (i=0;i<state->common->wh;i++)
290 state->cell_errors[i] = FALSE;
291 state->hint_errors = snewn(2*state->common->num_paths, unsigned char);
292 for (i=0;i<2*state->common->num_paths;i++)
293 state->hint_errors[i] = FALSE;
294 state->hints_done = snewn(2 * state->common->num_paths, unsigned char);
295 memset(state->hints_done, 0,
296 2 * state->common->num_paths * sizeof(unsigned char));
298 state->count_errors[i] = FALSE;
300 state->solved = FALSE;
301 state->cheated = FALSE;
306 static game_state *dup_game(const game_state *state)
308 game_state *ret = snew(game_state);
310 ret->common = state->common;
311 ret->common->refcount++;
313 if (state->guess != NULL) {
314 ret->guess = snewn(ret->common->num_total,int);
315 memcpy(ret->guess, state->guess, ret->common->num_total*sizeof(int));
317 else ret->guess = NULL;
319 if (state->pencils != NULL) {
320 ret->pencils = snewn(ret->common->num_total,unsigned char);
321 memcpy(ret->pencils, state->pencils,
322 ret->common->num_total*sizeof(unsigned char));
324 else ret->pencils = NULL;
326 if (state->cell_errors != NULL) {
327 ret->cell_errors = snewn(ret->common->wh,unsigned char);
328 memcpy(ret->cell_errors, state->cell_errors,
329 ret->common->wh*sizeof(unsigned char));
331 else ret->cell_errors = NULL;
333 if (state->hint_errors != NULL) {
334 ret->hint_errors = snewn(2*ret->common->num_paths,unsigned char);
335 memcpy(ret->hint_errors, state->hint_errors,
336 2*ret->common->num_paths*sizeof(unsigned char));
338 else ret->hint_errors = NULL;
340 if (state->hints_done != NULL) {
341 ret->hints_done = snewn(2 * state->common->num_paths, unsigned char);
342 memcpy(ret->hints_done, state->hints_done,
343 2 * state->common->num_paths * sizeof(unsigned char));
345 else ret->hints_done = NULL;
347 ret->count_errors[0] = state->count_errors[0];
348 ret->count_errors[1] = state->count_errors[1];
349 ret->count_errors[2] = state->count_errors[2];
351 ret->solved = state->solved;
352 ret->cheated = state->cheated;
357 static void free_game(game_state *state) {
360 state->common->refcount--;
361 if (state->common->refcount == 0) {
362 for (i=0;i<state->common->num_paths;i++) {
363 sfree(state->common->paths[i].mapping);
364 sfree(state->common->paths[i].xy);
365 sfree(state->common->paths[i].p);
367 sfree(state->common->paths);
368 sfree(state->common->xinfo);
369 sfree(state->common->grid);
370 if (state->common->fixed != NULL) sfree(state->common->fixed);
371 sfree(state->common);
373 if (state->hints_done != NULL) sfree(state->hints_done);
374 if (state->hint_errors != NULL) sfree(state->hint_errors);
375 if (state->cell_errors != NULL) sfree(state->cell_errors);
376 if (state->pencils != NULL) sfree(state->pencils);
377 if (state->guess != NULL) sfree(state->guess);
383 /* --------------------------------------------------------------- */
384 /* Puzzle generator */
397 /* grid walk directions */
406 int range2grid(int rangeno, int width, int height, int *x, int *y) {
409 *x = 0; *y = 0; return DIRECTION_NONE;
411 if (rangeno < width) {
412 *x = rangeno+1; *y = 0; return DIRECTION_DOWN;
414 rangeno = rangeno - width;
415 if (rangeno < height) {
416 *x = width+1; *y = rangeno+1; return DIRECTION_LEFT;
418 rangeno = rangeno - height;
419 if (rangeno < width) {
420 *x = width-rangeno; *y = height+1; return DIRECTION_UP;
422 rangeno = rangeno - width;
423 if (rangeno < height) {
424 *x = 0; *y = height-rangeno; return DIRECTION_RIGHT;
427 return DIRECTION_NONE;
430 int grid2range(int x, int y, int w, int h) {
431 if (x>0 && x<w+1 && y>0 && y<h+1) return -1;
432 if (x<0 || x>w+1 || y<0 || y>h+1) return -1;
433 if ((x == 0 || x==w+1) && (y==0 || y==h+1)) return -1;
434 if (y==0) return x-1;
435 if (x==(w+1)) return y-1+w;
436 if (y==(h+1)) return 2*w + h - x;
440 void make_paths(game_state *state) {
444 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) {
446 int j,k,num_monsters;
450 /* Check whether inverse path is already in list */
451 for (j=0;j<count;j++) {
452 if (i == state->common->paths[j].grid_end) {
459 /* We found a new path through the mirror maze */
460 state->common->paths[count].grid_start = i;
461 dir = range2grid(i, state->common->params.w,
462 state->common->params.h,&x,&y);
463 state->common->paths[count].sightings_start =
464 state->common->grid[x+y*(state->common->params.w +2)];
468 if (dir == DIRECTION_DOWN) y++;
469 else if (dir == DIRECTION_LEFT) x--;
470 else if (dir == DIRECTION_UP) y--;
471 else if (dir == DIRECTION_RIGHT) x++;
473 r = grid2range(x, y, state->common->params.w,
474 state->common->params.h);
476 state->common->paths[count].grid_end = r;
477 state->common->paths[count].sightings_end =
478 state->common->grid[x+y*(state->common->params.w +2)];
482 c = state->common->grid[x+y*(state->common->params.w+2)];
483 state->common->paths[count].xy[state->common->paths[count].length] =
484 x+y*(state->common->params.w+2);
485 if (c == CELL_MIRROR_L) {
486 state->common->paths[count].p[state->common->paths[count].length] = -1;
487 if (dir == DIRECTION_DOWN) dir = DIRECTION_RIGHT;
488 else if (dir == DIRECTION_LEFT) dir = DIRECTION_UP;
489 else if (dir == DIRECTION_UP) dir = DIRECTION_LEFT;
490 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_DOWN;
492 else if (c == CELL_MIRROR_R) {
493 state->common->paths[count].p[state->common->paths[count].length] = -1;
494 if (dir == DIRECTION_DOWN) dir = DIRECTION_LEFT;
495 else if (dir == DIRECTION_LEFT) dir = DIRECTION_DOWN;
496 else if (dir == DIRECTION_UP) dir = DIRECTION_RIGHT;
497 else if (dir == DIRECTION_RIGHT) dir = DIRECTION_UP;
500 state->common->paths[count].p[state->common->paths[count].length] =
501 state->common->xinfo[x+y*(state->common->params.w+2)];
503 state->common->paths[count].length++;
505 /* Count unique monster entries in each path */
506 state->common->paths[count].num_monsters = 0;
507 for (j=0;j<state->common->num_total;j++) {
509 for (k=0;k<state->common->paths[count].length;k++)
510 if (state->common->paths[count].p[k] == j)
512 if (num_monsters > 0)
513 state->common->paths[count].num_monsters++;
516 /* Generate mapping vector */
518 for (p=0;p<state->common->paths[count].length;p++) {
520 m = state->common->paths[count].p[p];
521 if (m == -1) continue;
524 if (state->common->paths[count].mapping[j] == m) found = TRUE;
525 if (!found) state->common->paths[count].mapping[c++] = m;
538 int next_list(struct guess *g, int pos) {
541 if ((g->guess[pos] == 1 && g->possible[pos] == 1) ||
542 (g->guess[pos] == 2 && (g->possible[pos] == 3 ||
543 g->possible[pos] == 2)) ||
546 if (g->guess[pos] == 1 && (g->possible[pos] == 3 ||
547 g->possible[pos] == 7)) {
548 g->guess[pos] = 2; return TRUE;
550 if (g->guess[pos] == 1 && g->possible[pos] == 5) {
551 g->guess[pos] = 4; return TRUE;
553 if (g->guess[pos] == 2 && (g->possible[pos] == 6 || g->possible[pos] == 7)) {
554 g->guess[pos] = 4; return TRUE;
558 if (g->guess[pos] == 1) {
559 if (g->possible[pos] == 1) {
560 return next_list(g,pos-1);
562 if (g->possible[pos] == 3 || g->possible[pos] == 7) {
563 g->guess[pos] = 2; return TRUE;
565 if (g->possible[pos] == 5) {
566 g->guess[pos] = 4; return TRUE;
570 if (g->guess[pos] == 2) {
571 if (g->possible[pos] == 2) {
572 return next_list(g,pos-1);
574 if (g->possible[pos] == 3) {
575 g->guess[pos] = 1; return next_list(g,pos-1);
577 if (g->possible[pos] == 6 || g->possible[pos] == 7) {
578 g->guess[pos] = 4; return TRUE;
582 if (g->guess[pos] == 4) {
583 if (g->possible[pos] == 5 || g->possible[pos] == 7) {
584 g->guess[pos] = 1; return next_list(g,pos-1);
586 if (g->possible[pos] == 6) {
587 g->guess[pos] = 2; return next_list(g,pos-1);
589 if (g->possible[pos] == 4) {
590 return next_list(g,pos-1);
596 void get_unique(game_state *state, int counter, random_state *rs) {
598 int p,i,c,pathlimit,count_uniques;
599 struct guess path_guess;
612 } views, single_views, test_views;
614 struct entry test_entry;
616 path_guess.length = state->common->paths[counter].num_monsters;
617 path_guess.guess = snewn(path_guess.length,int);
618 path_guess.possible = snewn(path_guess.length,int);
619 for (i=0;i<path_guess.length;i++)
620 path_guess.guess[i] = path_guess.possible[i] = 0;
622 for (p=0;p<path_guess.length;p++) {
623 path_guess.possible[p] =
624 state->guess[state->common->paths[counter].mapping[p]];
625 switch (path_guess.possible[p]) {
626 case 1: path_guess.guess[p] = 1; break;
627 case 2: path_guess.guess[p] = 2; break;
628 case 3: path_guess.guess[p] = 1; break;
629 case 4: path_guess.guess[p] = 4; break;
630 case 5: path_guess.guess[p] = 1; break;
631 case 6: path_guess.guess[p] = 2; break;
632 case 7: path_guess.guess[p] = 1; break;
639 pathlimit = state->common->paths[counter].length + 1;
640 view_count = snewn(pathlimit*pathlimit, int);
641 for (i = 0; i < pathlimit*pathlimit; i++)
645 int mirror, start_view, end_view;
649 for (p=0;p<state->common->paths[counter].length;p++) {
650 if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
652 for (i=0;i<path_guess.length;i++) {
653 if (state->common->paths[counter].p[p] ==
654 state->common->paths[counter].mapping[i]) {
655 if (path_guess.guess[i] == 1 && mirror == TRUE)
657 if (path_guess.guess[i] == 2 && mirror == FALSE)
659 if (path_guess.guess[i] == 4)
668 for (p=state->common->paths[counter].length-1;p>=0;p--) {
669 if (state->common->paths[counter].p[p] == -1) mirror = TRUE;
671 for (i=0;i<path_guess.length;i++) {
672 if (state->common->paths[counter].p[p] ==
673 state->common->paths[counter].mapping[i]) {
674 if (path_guess.guess[i] == 1 && mirror == TRUE)
676 if (path_guess.guess[i] == 2 && mirror == FALSE)
678 if (path_guess.guess[i] == 4)
686 assert(start_view >= 0 && start_view < pathlimit);
687 assert(end_view >= 0 && end_view < pathlimit);
688 i = start_view * pathlimit + end_view;
690 if (view_count[i] == 1) {
691 views.node = snewn(1,struct entry);
692 views.node->link = views.head;
693 views.node->guess = snewn(path_guess.length,int);
694 views.head = views.node;
695 views.node->start_view = start_view;
696 views.node->end_view = end_view;
697 memcpy(views.node->guess, path_guess.guess,
698 path_guess.length*sizeof(int));
700 } while (next_list(&path_guess, path_guess.length-1));
702 /* extract single entries from view list */
704 test_views.head = views.head;
705 test_views.node = views.node;
707 test_entry.guess = snewn(path_guess.length,int);
709 single_views.head = NULL;
710 single_views.node = NULL;
713 while (test_views.head != NULL) {
714 test_views.node = test_views.head;
715 test_views.head = test_views.head->link;
716 i = test_views.node->start_view * pathlimit + test_views.node->end_view;
717 if (view_count[i] == 1) {
718 single_views.node = snewn(1,struct entry);
719 single_views.node->link = single_views.head;
720 single_views.node->guess = snewn(path_guess.length,int);
721 single_views.head = single_views.node;
722 single_views.node->start_view = test_views.node->start_view;
723 single_views.node->end_view = test_views.node->end_view;
724 memcpy(single_views.node->guess, test_views.node->guess,
725 path_guess.length*sizeof(int));
732 if (count_uniques > 0) {
733 test_entry.start_view = 0;
734 test_entry.end_view = 0;
735 /* Choose one unique guess per random */
736 /* While we are busy with looping through single_views, we
737 * conveniently free the linked list single_view */
738 c = random_upto(rs,count_uniques);
739 while(single_views.head != NULL) {
740 single_views.node = single_views.head;
741 single_views.head = single_views.head->link;
743 memcpy(test_entry.guess, single_views.node->guess,
744 path_guess.length*sizeof(int));
745 test_entry.start_view = single_views.node->start_view;
746 test_entry.end_view = single_views.node->end_view;
748 sfree(single_views.node->guess);
749 sfree(single_views.node);
752 /* Modify state_guess according to path_guess.mapping */
753 for (i=0;i<path_guess.length;i++)
754 state->guess[state->common->paths[counter].mapping[i]] =
758 sfree(test_entry.guess);
760 while (views.head != NULL) {
761 views.node = views.head;
762 views.head = views.head->link;
763 sfree(views.node->guess);
767 sfree(path_guess.possible);
768 sfree(path_guess.guess);
773 int count_monsters(game_state *state,
774 int *cGhost, int *cVampire, int *cZombie) {
778 *cGhost = *cVampire = *cZombie = cNone = 0;
780 for (i=0;i<state->common->num_total;i++) {
781 if (state->guess[i] == 1) (*cGhost)++;
782 else if (state->guess[i] == 2) (*cVampire)++;
783 else if (state->guess[i] == 4) (*cZombie)++;
790 int check_numbers(game_state *state, int *guess) {
793 int count_ghosts, count_vampires, count_zombies;
795 count_ghosts = count_vampires = count_zombies = 0;
796 for (i=0;i<state->common->num_total;i++) {
797 if (guess[i] == 1) count_ghosts++;
798 if (guess[i] == 2) count_vampires++;
799 if (guess[i] == 4) count_zombies++;
804 if (count_ghosts > state->common->num_ghosts) valid = FALSE;
805 if (count_vampires > state->common->num_vampires) valid = FALSE;
806 if (count_zombies > state->common->num_zombies) valid = FALSE;
811 int check_solution(int *g, struct path path) {
818 for (i=0;i<path.length;i++) {
819 if (path.p[i] == -1) mirror = TRUE;
821 if (g[path.p[i]] == 1 && mirror) count++;
822 else if (g[path.p[i]] == 2 && !mirror) count++;
823 else if (g[path.p[i]] == 4) count++;
826 if (count != path.sightings_start) return FALSE;
830 for (i=path.length-1;i>=0;i--) {
831 if (path.p[i] == -1) mirror = TRUE;
833 if (g[path.p[i]] == 1 && mirror) count++;
834 else if (g[path.p[i]] == 2 && !mirror) count++;
835 else if (g[path.p[i]] == 4) count++;
838 if (count != path.sightings_end) return FALSE;
843 int solve_iterative(game_state *state, struct path *paths) {
853 loop.length = state->common->num_total;
854 guess = snewn(state->common->num_total,int);
855 possible = snewn(state->common->num_total,int);
857 for (i=0;i<state->common->num_total;i++) {
858 guess[i] = state->guess[i];
862 for (p=0;p<state->common->num_paths;p++) {
863 if (paths[p].num_monsters > 0) {
864 loop.length = paths[p].num_monsters;
865 loop.guess = snewn(paths[p].num_monsters,int);
866 loop.possible = snewn(paths[p].num_monsters,int);
868 for (i=0;i<paths[p].num_monsters;i++) {
869 switch (state->guess[paths[p].mapping[i]]) {
870 case 1: loop.guess[i] = 1; break;
871 case 2: loop.guess[i] = 2; break;
872 case 3: loop.guess[i] = 1; break;
873 case 4: loop.guess[i] = 4; break;
874 case 5: loop.guess[i] = 1; break;
875 case 6: loop.guess[i] = 2; break;
876 case 7: loop.guess[i] = 1; break;
878 loop.possible[i] = state->guess[paths[p].mapping[i]];
879 possible[paths[p].mapping[i]] = 0;
883 for (i=0;i<state->common->num_total;i++) {
884 guess[i] = state->guess[i];
887 for (i=0;i<paths[p].num_monsters;i++)
888 guess[paths[p].mapping[i]] = loop.guess[count++];
889 if (check_numbers(state,guess) &&
890 check_solution(guess,paths[p]))
891 for (j=0;j<paths[p].num_monsters;j++)
892 possible[paths[p].mapping[j]] |= loop.guess[j];
893 if (!next_list(&loop,loop.length-1)) break;
895 for (i=0;i<paths[p].num_monsters;i++)
896 state->guess[paths[p].mapping[i]] &=
897 possible[paths[p].mapping[i]];
898 sfree(loop.possible);
903 for (i=0;i<state->common->num_total;i++) {
904 if (state->guess[i] == 3 || state->guess[i] == 5 ||
905 state->guess[i] == 6 || state->guess[i] == 7) {
906 solved = FALSE; break;
916 int solve_bruteforce(game_state *state, struct path *paths) {
918 int number_solutions;
923 loop.guess = snewn(state->common->num_total,int);
924 loop.possible = snewn(state->common->num_total,int);
926 for (i=0;i<state->common->num_total;i++) {
927 loop.possible[i] = state->guess[i];
928 switch (state->guess[i]) {
929 case 1: loop.guess[i] = 1; break;
930 case 2: loop.guess[i] = 2; break;
931 case 3: loop.guess[i] = 1; break;
932 case 4: loop.guess[i] = 4; break;
933 case 5: loop.guess[i] = 1; break;
934 case 6: loop.guess[i] = 2; break;
935 case 7: loop.guess[i] = 1; break;
940 number_solutions = 0;
945 if (!check_numbers(state,loop.guess)) correct = FALSE;
947 for (p=0;p<state->common->num_paths;p++)
948 if (!check_solution(loop.guess,paths[p])) {
949 correct = FALSE; break;
954 if(number_solutions > 1) {
958 for (i=0;i<state->common->num_total;i++)
959 state->guess[i] = loop.guess[i];
961 if (!next_list(&loop,state->common->num_total -1)) {
966 sfree(loop.possible);
972 int path_cmp(const void *a, const void *b) {
973 const struct path *pa = (const struct path *)a;
974 const struct path *pb = (const struct path *)b;
975 return pa->num_monsters - pb->num_monsters;
978 static char *new_game_desc(const game_params *params, random_state *rs,
979 char **aux, int interactive) {
980 int i,count,c,w,h,r,p,g;
983 /* Variables for puzzle generation algorithm */
986 int count_ghosts, count_vampires, count_zombies;
990 /* Variables for solver algorithm */
991 int solved_iterative, solved_bruteforce, contains_inconsistency,
996 /* Variables for game description generation */
1003 new = new_state(params);
1006 /* Fill grid with random mirrors and (later to be populated)
1007 * empty monster cells */
1009 for (h=1;h<new->common->params.h+1;h++)
1010 for (w=1;w<new->common->params.w+1;w++) {
1011 c = random_upto(rs,5);
1013 new->common->grid[w+h*(new->common->params.w+2)] = CELL_EMPTY;
1014 new->common->xinfo[w+h*(new->common->params.w+2)] = count++;
1017 new->common->grid[w+h*(new->common->params.w+2)] =
1019 new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
1022 new->common->grid[w+h*(new->common->params.w+2)] =
1024 new->common->xinfo[w+h*(new->common->params.w+2)] = -1;
1027 new->common->num_total = count; /* Total number of monsters in maze */
1029 /* Puzzle is boring if it has too few monster cells. Discard
1030 * grid, make new grid */
1031 if (new->common->num_total <= 4) {
1036 /* Monsters / Mirrors ratio should be balanced */
1037 ratio = (float)new->common->num_total /
1038 (float)(new->common->params.w * new->common->params.h);
1039 if (ratio < 0.48 || ratio > 0.78) {
1044 /* Assign clue identifiers */
1045 for (r=0;r<2*(new->common->params.w+new->common->params.h);r++) {
1047 gridno = range2grid(r,new->common->params.w,new->common->params.h,
1049 new->common->grid[x+y*(new->common->params.w +2)] = gridno;
1050 new->common->xinfo[x+y*(new->common->params.w +2)] = 0;
1052 /* The four corners don't matter at all for the game. Set them
1053 * all to zero, just to have a nice data structure */
1054 new->common->grid[0] = 0;
1055 new->common->xinfo[0] = 0;
1056 new->common->grid[new->common->params.w+1] = 0;
1057 new->common->xinfo[new->common->params.w+1] = 0;
1058 new->common->grid[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0;
1059 new->common->xinfo[new->common->params.w+1 + (new->common->params.h+1)*(new->common->params.w+2)] = 0;
1060 new->common->grid[(new->common->params.h+1)*(new->common->params.w+2)] = 0;
1061 new->common->xinfo[(new->common->params.h+1)*(new->common->params.w+2)] = 0;
1063 /* Initialize solution vector */
1064 new->guess = snewn(new->common->num_total,int);
1065 for (g=0;g<new->common->num_total;g++) new->guess[g] = 7;
1067 /* Initialize fixed flag from common. Not needed for the
1068 * puzzle generator; initialize it for having clean code */
1069 new->common->fixed = snewn(new->common->num_total,int);
1070 for (g=0;g<new->common->num_total;g++)
1071 new->common->fixed[g] = FALSE;
1073 /* paths generation */
1076 /* Grid is invalid if max. path length > threshold. Discard
1077 * grid, make new one */
1078 switch (new->common->params.diff) {
1079 case DIFF_EASY: max_length = min(new->common->params.w,new->common->params.h) + 1; break;
1080 case DIFF_NORMAL: max_length = (max(new->common->params.w,new->common->params.h) * 3) / 2; break;
1081 case DIFF_TRICKY: max_length = 9; break;
1082 default: max_length = 9; break;
1085 for (p=0;p<new->common->num_paths;p++) {
1086 if (new->common->paths[p].num_monsters > max_length) {
1095 qsort(new->common->paths, new->common->num_paths,
1096 sizeof(struct path), path_cmp);
1098 /* Grid monster initialization */
1099 /* For easy puzzles, we try to fill nearly the whole grid
1100 with unique solution paths (up to 2) For more difficult
1101 puzzles, we fill only roughly half the grid, and choose
1102 random monsters for the rest For hard puzzles, we fill
1103 even less paths with unique solutions */
1105 switch (new->common->params.diff) {
1106 case DIFF_EASY: filling = 2; break;
1107 case DIFF_NORMAL: filling = min( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
1108 case DIFF_TRICKY: filling = max( (new->common->params.w+new->common->params.h) , (new->common->num_total)/2 ); break;
1109 default: filling = 0; break;
1113 while ( (count_monsters(new, &count_ghosts, &count_vampires,
1114 &count_zombies)) > filling) {
1115 if ((count) >= new->common->num_paths) break;
1116 if (new->common->paths[count].num_monsters == 0) {
1120 get_unique(new,count,rs);
1124 /* Fill any remaining ambiguous entries with random monsters */
1125 for(g=0;g<new->common->num_total;g++) {
1126 if (new->guess[g] == 7) {
1127 r = random_upto(rs,3);
1128 new->guess[g] = (r == 0) ? 1 : ( (r == 1) ? 2 : 4 );
1132 /* Determine all hints */
1133 count_monsters(new, &new->common->num_ghosts,
1134 &new->common->num_vampires, &new->common->num_zombies);
1136 /* Puzzle is trivial if it has only one type of monster. Discard. */
1137 if ((new->common->num_ghosts == 0 && new->common->num_vampires == 0) ||
1138 (new->common->num_ghosts == 0 && new->common->num_zombies == 0) ||
1139 (new->common->num_vampires == 0 && new->common->num_zombies == 0)) {
1144 /* Discard puzzle if difficulty Tricky, and it has only 1
1145 * member of any monster type */
1146 if (new->common->params.diff == DIFF_TRICKY &&
1147 (new->common->num_ghosts <= 1 ||
1148 new->common->num_vampires <= 1 || new->common->num_zombies <= 1)) {
1153 for (w=1;w<new->common->params.w+1;w++)
1154 for (h=1;h<new->common->params.h+1;h++) {
1155 c = new->common->xinfo[w+h*(new->common->params.w+2)];
1157 if (new->guess[c] == 1) new->common->grid[w+h*(new->common->params.w+2)] = CELL_GHOST;
1158 if (new->guess[c] == 2) new->common->grid[w+h*(new->common->params.w+2)] = CELL_VAMPIRE;
1159 if (new->guess[c] == 4) new->common->grid[w+h*(new->common->params.w+2)] = CELL_ZOMBIE;
1163 /* Prepare path information needed by the solver (containing all hints) */
1164 for (p=0;p<new->common->num_paths;p++) {
1167 new->common->paths[p].sightings_start = 0;
1168 new->common->paths[p].sightings_end = 0;
1171 for (g=0;g<new->common->paths[p].length;g++) {
1173 if (new->common->paths[p].p[g] == -1) mirror = TRUE;
1175 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_start)++;
1176 else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_start)++;
1177 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_start)++;
1182 for (g=new->common->paths[p].length-1;g>=0;g--) {
1183 if (new->common->paths[p].p[g] == -1) mirror = TRUE;
1185 if (new->guess[new->common->paths[p].p[g]] == 1 && mirror == TRUE) (new->common->paths[p].sightings_end)++;
1186 else if (new->guess[new->common->paths[p].p[g]] == 2 && mirror == FALSE) (new->common->paths[p].sightings_end)++;
1187 else if (new->guess[new->common->paths[p].p[g]] == 4) (new->common->paths[p].sightings_end)++;
1191 range2grid(new->common->paths[p].grid_start,
1192 new->common->params.w,new->common->params.h,&x,&y);
1193 new->common->grid[x+y*(new->common->params.w +2)] =
1194 new->common->paths[p].sightings_start;
1195 range2grid(new->common->paths[p].grid_end,
1196 new->common->params.w,new->common->params.h,&x,&y);
1197 new->common->grid[x+y*(new->common->params.w +2)] =
1198 new->common->paths[p].sightings_end;
1201 /* Try to solve the puzzle with the iterative solver */
1202 old_guess = snewn(new->common->num_total,int);
1203 for (p=0;p<new->common->num_total;p++) {
1207 iterative_depth = 0;
1208 solved_iterative = FALSE;
1209 contains_inconsistency = FALSE;
1210 count_ambiguous = 0;
1215 solved_iterative = solve_iterative(new,new->common->paths);
1217 for (p=0;p<new->common->num_total;p++) {
1218 if (new->guess[p] != old_guess[p]) no_change = FALSE;
1219 old_guess[p] = new->guess[p];
1220 if (new->guess[p] == 0) contains_inconsistency = TRUE;
1222 if (solved_iterative || no_change) break;
1225 /* If necessary, try to solve the puzzle with the brute-force solver */
1226 solved_bruteforce = FALSE;
1227 if (new->common->params.diff != DIFF_EASY &&
1228 !solved_iterative && !contains_inconsistency) {
1229 for (p=0;p<new->common->num_total;p++)
1230 if (new->guess[p] != 1 && new->guess[p] != 2 &&
1231 new->guess[p] != 4) count_ambiguous++;
1233 solved_bruteforce = solve_bruteforce(new, new->common->paths);
1236 /* Determine puzzle difficulty level */
1237 if (new->common->params.diff == DIFF_EASY && solved_iterative &&
1238 iterative_depth <= 3 && !contains_inconsistency) {
1239 /* printf("Puzzle level: EASY Level %d Ratio %f Ambiguous %d (Found after %i tries)\n",iterative_depth, ratio, count_ambiguous, i); */
1243 if (new->common->params.diff == DIFF_NORMAL &&
1244 ((solved_iterative && iterative_depth > 3) ||
1245 (solved_bruteforce && count_ambiguous < 4)) &&
1246 !contains_inconsistency) {
1247 /* printf("Puzzle level: NORMAL Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */
1250 if (new->common->params.diff == DIFF_TRICKY &&
1251 solved_bruteforce && iterative_depth > 0 &&
1252 count_ambiguous >= 4 && !contains_inconsistency) {
1253 /* printf("Puzzle level: TRICKY Level %d Ratio %f Ambiguous %d (Found after %d tries)\n", iterative_depth, ratio, count_ambiguous, i); */
1257 /* If puzzle is not solvable or does not satisfy the desired
1258 * difficulty level, free memory and start from scratch */
1264 /* We have a valid puzzle! */
1266 desc = snewn(10 + new->common->wh +
1267 6*(new->common->params.w + new->common->params.h), char);
1270 /* Encode monster counts */
1271 e += sprintf(e, "%d,", new->common->num_ghosts);
1272 e += sprintf(e, "%d,", new->common->num_vampires);
1273 e += sprintf(e, "%d,", new->common->num_zombies);
1277 for (y=1;y<new->common->params.h+1;y++)
1278 for (x=1;x<new->common->params.w+1;x++) {
1279 c = new->common->grid[x+y*(new->common->params.w+2)];
1284 if (c != CELL_MIRROR_L && c != CELL_MIRROR_R) {
1287 else if (c == CELL_MIRROR_L) {
1288 if (count > 0) *e++ = (count-1 + 'a');
1293 if (count > 0) *e++ = (count-1 + 'a');
1298 if (count > 0) *e++ = (count-1 + 'a');
1301 for (p=0;p<2*(new->common->params.w + new->common->params.h);p++) {
1302 range2grid(p,new->common->params.w,new->common->params.h,&x,&y);
1303 e += sprintf(e, ",%d", new->common->grid[x+y*(new->common->params.w+2)]);
1307 desc = sresize(desc, e - desc, char);
1315 void num2grid(int num, int width, int height, int *x, int *y) {
1321 static game_state *new_game(midend *me, const game_params *params,
1328 game_state *state = new_state(params);
1330 state->common->num_ghosts = atoi(desc);
1331 while (*desc && isdigit((unsigned char)*desc)) desc++;
1333 state->common->num_vampires = atoi(desc);
1334 while (*desc && isdigit((unsigned char)*desc)) desc++;
1336 state->common->num_zombies = atoi(desc);
1337 while (*desc && isdigit((unsigned char)*desc)) desc++;
1340 state->common->num_total = state->common->num_ghosts + state->common->num_vampires + state->common->num_zombies;
1342 state->guess = snewn(state->common->num_total,int);
1343 state->pencils = snewn(state->common->num_total,unsigned char);
1344 state->common->fixed = snewn(state->common->num_total,int);
1345 for (i=0;i<state->common->num_total;i++) {
1346 state->guess[i] = 7;
1347 state->pencils[i] = 0;
1348 state->common->fixed[i] = FALSE;
1350 for (i=0;i<state->common->wh;i++)
1351 state->cell_errors[i] = FALSE;
1352 for (i=0;i<2*state->common->num_paths;i++)
1353 state->hint_errors[i] = FALSE;
1355 state->count_errors[i] = FALSE;
1359 while (*desc != ',') {
1364 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1365 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_L;
1366 state->common->xinfo[x+y*(state->common->params.w+2)] = -1;
1369 else if (*desc == 'R') {
1370 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1371 state->common->grid[x+y*(state->common->params.w +2)] = CELL_MIRROR_R;
1372 state->common->xinfo[x+y*(state->common->params.w+2)] = -1;
1375 else if (*desc == 'G') {
1376 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1377 state->common->grid[x+y*(state->common->params.w +2)] = CELL_GHOST;
1378 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1379 state->guess[count] = 1;
1380 state->common->fixed[count++] = TRUE;
1383 else if (*desc == 'V') {
1384 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1385 state->common->grid[x+y*(state->common->params.w +2)] = CELL_VAMPIRE;
1386 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1387 state->guess[count] = 2;
1388 state->common->fixed[count++] = TRUE;
1391 else if (*desc == 'Z') {
1392 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1393 state->common->grid[x+y*(state->common->params.w +2)] = CELL_ZOMBIE;
1394 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1395 state->guess[count] = 4;
1396 state->common->fixed[count++] = TRUE;
1400 c = *desc - ('a' -1);
1402 num2grid(n,state->common->params.w,state->common->params.h,&x,&y);
1403 state->common->grid[x+y*(state->common->params.w +2)] = CELL_EMPTY;
1404 state->common->xinfo[x+y*(state->common->params.w+2)] = count;
1405 state->guess[count] = 7;
1406 state->common->fixed[count++] = FALSE;
1414 for (i=0;i<2*(state->common->params.w + state->common->params.h);i++) {
1418 sights = atoi(desc);
1419 while (*desc && isdigit((unsigned char)*desc)) desc++;
1423 range2grid(i,state->common->params.w,state->common->params.h,&x,&y);
1424 state->common->grid[x+y*(state->common->params.w +2)] = sights;
1425 state->common->xinfo[x+y*(state->common->params.w +2)] = -2;
1428 state->common->grid[0] = 0;
1429 state->common->xinfo[0] = -2;
1430 state->common->grid[state->common->params.w+1] = 0;
1431 state->common->xinfo[state->common->params.w+1] = -2;
1432 state->common->grid[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = 0;
1433 state->common->xinfo[state->common->params.w+1 + (state->common->params.h+1)*(state->common->params.w+2)] = -2;
1434 state->common->grid[(state->common->params.h+1)*(state->common->params.w+2)] = 0;
1435 state->common->xinfo[(state->common->params.h+1)*(state->common->params.w+2)] = -2;
1438 qsort(state->common->paths, state->common->num_paths, sizeof(struct path), path_cmp);
1443 static char *validate_desc(const game_params *params, const char *desc)
1446 int w = params->w, h = params->h;
1451 const char *desc_s = desc;
1454 if (!*desc) return "Faulty game description";
1455 while (*desc && isdigit((unsigned char)*desc)) { desc++; }
1456 if (*desc != ',') return "Invalid character in number list";
1461 area = monsters = monster_count = 0;
1463 monster_count += atoi(desc);
1464 while (*desc && isdigit((unsigned char)*desc)) desc++;
1467 while (*desc && *desc != ',') {
1468 if (*desc >= 'a' && *desc <= 'z') {
1469 area += *desc - 'a' +1; monsters += *desc - 'a' +1;
1470 } else if (*desc == 'G' || *desc == 'V' || *desc == 'Z') {
1472 } else if (*desc == 'L' || *desc == 'R') {
1475 return "Invalid character in grid specification";
1478 if (area < wh) return "Not enough data to fill grid";
1479 else if (area > wh) return "Too much data to fill grid";
1480 if (monsters != monster_count)
1481 return "Monster numbers do not match grid spaces";
1483 for (i = 0; i < 2*(w+h); i++) {
1484 if (!*desc) return "Not enough numbers given after grid specification";
1485 else if (*desc != ',') return "Invalid character in number list";
1487 while (*desc && isdigit((unsigned char)*desc)) { desc++; }
1490 if (*desc) return "Unexpected additional data at end of game description";
1495 static char *solve_game(const game_state *state_start, const game_state *currstate,
1496 const char *aux, char **error)
1500 int iterative_depth;
1501 int solved_iterative, solved_bruteforce, contains_inconsistency,
1507 game_state *solve_state = dup_game(currstate);
1509 old_guess = snewn(solve_state->common->num_total,int);
1510 for (p=0;p<solve_state->common->num_total;p++) {
1511 if (solve_state->common->fixed[p]) {
1512 old_guess[p] = solve_state->guess[p] = state_start->guess[p];
1515 old_guess[p] = solve_state->guess[p] = 7;
1518 iterative_depth = 0;
1519 solved_iterative = FALSE;
1520 contains_inconsistency = FALSE;
1521 count_ambiguous = 0;
1523 /* Try to solve the puzzle with the iterative solver */
1528 solve_iterative(solve_state,solve_state->common->paths);
1530 for (p=0;p<solve_state->common->num_total;p++) {
1531 if (solve_state->guess[p] != old_guess[p]) no_change = FALSE;
1532 old_guess[p] = solve_state->guess[p];
1533 if (solve_state->guess[p] == 0) contains_inconsistency = TRUE;
1535 if (solved_iterative || no_change || contains_inconsistency) break;
1538 if (contains_inconsistency) {
1539 *error = "Puzzle is inconsistent";
1541 free_game(solve_state);
1545 /* If necessary, try to solve the puzzle with the brute-force solver */
1546 solved_bruteforce = FALSE;
1547 if (!solved_iterative) {
1548 for (p=0;p<solve_state->common->num_total;p++)
1549 if (solve_state->guess[p] != 1 && solve_state->guess[p] != 2 &&
1550 solve_state->guess[p] != 4) count_ambiguous++;
1552 solve_bruteforce(solve_state, solve_state->common->paths);
1555 if (!solved_iterative && !solved_bruteforce) {
1556 *error = "Puzzle is unsolvable";
1558 free_game(solve_state);
1562 /* printf("Puzzle solved at level %s, iterations %d, ambiguous %d\n", (solved_bruteforce ? "TRICKY" : "NORMAL"), iterative_depth, count_ambiguous); */
1564 move = snewn(solve_state->common->num_total * 4 +2, char);
1567 for (i = 0; i < solve_state->common->num_total; i++) {
1568 if (solve_state->guess[i] == 1) c += sprintf(c, ";G%d", i);
1569 if (solve_state->guess[i] == 2) c += sprintf(c, ";V%d", i);
1570 if (solve_state->guess[i] == 4) c += sprintf(c, ";Z%d", i);
1573 move = sresize(move, c - move, char);
1576 free_game(solve_state);
1580 static int game_can_format_as_text_now(const game_params *params)
1585 static char *game_text_format(const game_state *state)
1591 ret = snewn(50 + 6*(state->common->params.w +2) +
1592 6*(state->common->params.h+2) +
1593 3*(state->common->params.w * state->common->params.h), char);
1595 sprintf(ret,"G: %d V: %d Z: %d\n\n",state->common->num_ghosts,
1596 state->common->num_vampires, state->common->num_zombies);
1598 for (h=0;h<state->common->params.h+2;h++) {
1599 for (w=0;w<state->common->params.w+2;w++) {
1600 c = state->common->grid[w+h*(state->common->params.w+2)];
1601 xi = state->common->xinfo[w+h*(state->common->params.w+2)];
1602 r = grid2range(w,h,state->common->params.w,state->common->params.h);
1604 sprintf(buf,"%2d", c); strcat(ret,buf);
1605 } else if (c == CELL_MIRROR_L) {
1606 sprintf(buf," \\"); strcat(ret,buf);
1607 } else if (c == CELL_MIRROR_R) {
1608 sprintf(buf," /"); strcat(ret,buf);
1609 } else if (xi >= 0) {
1610 g = state->guess[xi];
1611 if (g == 1) { sprintf(buf," G"); strcat(ret,buf); }
1612 else if (g == 2) { sprintf(buf," V"); strcat(ret,buf); }
1613 else if (g == 4) { sprintf(buf," Z"); strcat(ret,buf); }
1614 else { sprintf(buf," ."); strcat(ret,buf); }
1616 sprintf(buf," "); strcat(ret,buf);
1619 sprintf(buf,"\n"); strcat(ret,buf);
1626 int hx, hy; /* as for solo.c, highlight pos */
1627 int hshow, hpencil, hcursor; /* show state, type, and ?cursor. */
1631 static game_ui *new_ui(const game_state *state)
1633 game_ui *ui = snew(game_ui);
1634 ui->hx = ui->hy = 0;
1635 ui->hpencil = ui->hshow = ui->hcursor = 0;
1640 static void free_ui(game_ui *ui) {
1645 static char *encode_ui(const game_ui *ui)
1650 static void decode_ui(game_ui *ui, const char *encoding)
1655 static void game_changed_state(game_ui *ui, const game_state *oldstate,
1656 const game_state *newstate)
1658 /* See solo.c; if we were pencil-mode highlighting and
1659 * somehow a square has just been properly filled, cancel
1661 if (ui->hshow && ui->hpencil && !ui->hcursor) {
1662 int g = newstate->guess[newstate->common->xinfo[ui->hx + ui->hy*(newstate->common->params.w+2)]];
1663 if (g == 1 || g == 2 || g == 4)
1668 struct game_drawstate {
1669 int tilesize, started, solved;
1673 unsigned char *pencils;
1675 unsigned char count_errors[3];
1676 unsigned char *cell_errors;
1677 unsigned char *hint_errors;
1678 unsigned char *hints_done;
1680 int hx, hy, hshow, hpencil; /* as for game_ui. */
1685 static int is_clue(const game_state *state, int x, int y)
1687 int h = state->common->params.h, w = state->common->params.w;
1689 if (((x == 0 || x == w + 1) && y > 0 && y <= h) ||
1690 ((y == 0 || y == h + 1) && x > 0 && x <= w))
1696 static int clue_index(const game_state *state, int x, int y)
1698 int h = state->common->params.h, w = state->common->params.w;
1702 else if (x == w + 1)
1704 else if (y == h + 1)
1705 return 2 * w + h - x;
1707 return 2 * (w + h) - y;
1712 #define TILESIZE (ds->tilesize)
1713 #define BORDER (TILESIZE/4)
1715 static char *interpret_move(const game_state *state, game_ui *ui,
1716 const game_drawstate *ds,
1717 int x, int y, int button)
1723 gx = ((x-BORDER-1) / TILESIZE );
1724 gy = ((y-BORDER-2) / TILESIZE ) - 1;
1726 if (button == 'a' || button == 'A') {
1727 ui->ascii = !ui->ascii;
1731 if (ui->hshow == 1 && ui->hpencil == 0) {
1732 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1733 if (xi >= 0 && !state->common->fixed[xi]) {
1734 if (button == 'g' || button == 'G' || button == '1') {
1735 if (!ui->hcursor) ui->hshow = 0;
1736 sprintf(buf,"G%d",xi);
1739 if (button == 'v' || button == 'V' || button == '2') {
1740 if (!ui->hcursor) ui->hshow = 0;
1741 sprintf(buf,"V%d",xi);
1744 if (button == 'z' || button == 'Z' || button == '3') {
1745 if (!ui->hcursor) ui->hshow = 0;
1746 sprintf(buf,"Z%d",xi);
1749 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1750 button == '0' || button == '\b' ) {
1751 if (!ui->hcursor) ui->hshow = 0;
1752 sprintf(buf,"E%d",xi);
1758 if (IS_CURSOR_MOVE(button)) {
1759 if (ui->hx == 0 && ui->hy == 0) {
1763 else switch (button) {
1764 case CURSOR_UP: ui->hy -= (ui->hy > 1) ? 1 : 0; break;
1765 case CURSOR_DOWN: ui->hy += (ui->hy < ds->h) ? 1 : 0; break;
1766 case CURSOR_RIGHT: ui->hx += (ui->hx < ds->w) ? 1 : 0; break;
1767 case CURSOR_LEFT: ui->hx -= (ui->hx > 1) ? 1 : 0; break;
1769 ui->hshow = ui->hcursor = 1;
1772 if (ui->hshow && button == CURSOR_SELECT) {
1773 ui->hpencil = 1 - ui->hpencil;
1778 if (ui->hshow == 1 && ui->hpencil == 1) {
1779 xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
1780 if (xi >= 0 && !state->common->fixed[xi]) {
1781 if (button == 'g' || button == 'G' || button == '1') {
1782 sprintf(buf,"g%d",xi);
1783 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1786 if (button == 'v' || button == 'V' || button == '2') {
1787 sprintf(buf,"v%d",xi);
1788 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1791 if (button == 'z' || button == 'Z' || button == '3') {
1792 sprintf(buf,"z%d",xi);
1793 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1796 if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
1797 button == '0' || button == '\b') {
1798 sprintf(buf,"E%d",xi);
1799 if (!ui->hcursor) ui->hpencil = ui->hshow = 0;
1805 if (gx > 0 && gx < ds->w+1 && gy > 0 && gy < ds->h+1) {
1806 xi = state->common->xinfo[gx+gy*(state->common->params.w+2)];
1807 if (xi >= 0 && !state->common->fixed[xi]) {
1808 g = state->guess[xi];
1809 if (ui->hshow == 0) {
1810 if (button == LEFT_BUTTON) {
1811 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1812 ui->hx = gx; ui->hy = gy;
1815 else if (button == RIGHT_BUTTON && g == 7) {
1816 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1817 ui->hx = gx; ui->hy = gy;
1821 else if (ui->hshow == 1) {
1822 if (button == LEFT_BUTTON) {
1823 if (ui->hpencil == 0) {
1824 if (gx == ui->hx && gy == ui->hy) {
1825 ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0;
1826 ui->hx = 0; ui->hy = 0;
1830 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1831 ui->hx = gx; ui->hy = gy;
1836 ui->hshow = 1; ui->hpencil = 0; ui->hcursor = 0;
1837 ui->hx = gx; ui->hy = gy;
1841 else if (button == RIGHT_BUTTON) {
1842 if (ui->hpencil == 0 && g == 7) {
1843 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1844 ui->hx = gx; ui->hy = gy;
1848 if (gx == ui->hx && gy == ui->hy) {
1849 ui->hshow = 0; ui->hpencil = 0; ui->hcursor = 0;
1850 ui->hx = 0; ui->hy = 0;
1854 ui->hshow = 1; ui->hpencil = 1; ui->hcursor = 0;
1855 ui->hx = gx; ui->hy = gy;
1862 } else if (button == LEFT_BUTTON) {
1863 if (is_clue(state, gx, gy)) {
1864 sprintf(buf, "D%d,%d", gx, gy);
1872 int check_numbers_draw(game_state *state, int *guess) {
1875 int count_ghosts, count_vampires, count_zombies;
1877 count_ghosts = count_vampires = count_zombies = 0;
1878 for (i=0;i<state->common->num_total;i++) {
1879 if (guess[i] == 1) count_ghosts++;
1880 if (guess[i] == 2) count_vampires++;
1881 if (guess[i] == 4) count_zombies++;
1885 filled = (count_ghosts + count_vampires + count_zombies >=
1886 state->common->num_total);
1888 if (count_ghosts > state->common->num_ghosts ||
1889 (filled && count_ghosts != state->common->num_ghosts) ) {
1891 state->count_errors[0] = TRUE;
1892 for (x=1;x<state->common->params.w+1;x++)
1893 for (y=1;y<state->common->params.h+1;y++) {
1894 xy = x+y*(state->common->params.w+2);
1895 if (state->common->xinfo[xy] >= 0 &&
1896 guess[state->common->xinfo[xy]] == 1)
1897 state->cell_errors[xy] = TRUE;
1900 if (count_vampires > state->common->num_vampires ||
1901 (filled && count_vampires != state->common->num_vampires) ) {
1903 state->count_errors[1] = TRUE;
1904 for (x=1;x<state->common->params.w+1;x++)
1905 for (y=1;y<state->common->params.h+1;y++) {
1906 xy = x+y*(state->common->params.w+2);
1907 if (state->common->xinfo[xy] >= 0 &&
1908 guess[state->common->xinfo[xy]] == 2)
1909 state->cell_errors[xy] = TRUE;
1912 if (count_zombies > state->common->num_zombies ||
1913 (filled && count_zombies != state->common->num_zombies) ) {
1915 state->count_errors[2] = TRUE;
1916 for (x=1;x<state->common->params.w+1;x++)
1917 for (y=1;y<state->common->params.h+1;y++) {
1918 xy = x+y*(state->common->params.w+2);
1919 if (state->common->xinfo[xy] >= 0 &&
1920 guess[state->common->xinfo[xy]] == 4)
1921 state->cell_errors[xy] = TRUE;
1928 int check_path_solution(game_state *state, int p) {
1940 for (i=0;i<state->common->paths[p].length;i++) {
1941 if (state->common->paths[p].p[i] == -1) mirror = TRUE;
1943 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror)
1945 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror)
1947 else if (state->guess[state->common->paths[p].p[i]] == 4)
1949 else if (state->guess[state->common->paths[p].p[i]] == 7)
1954 if (unfilled == 0 && count != state->common->paths[p].sightings_start) {
1956 state->hint_errors[state->common->paths[p].grid_start] = TRUE;
1962 for (i=state->common->paths[p].length-1;i>=0;i--) {
1963 if (state->common->paths[p].p[i] == -1) mirror = TRUE;
1965 if (state->guess[state->common->paths[p].p[i]] == 1 && mirror)
1967 else if (state->guess[state->common->paths[p].p[i]] == 2 && !mirror)
1969 else if (state->guess[state->common->paths[p].p[i]] == 4)
1971 else if (state->guess[state->common->paths[p].p[i]] == 7)
1976 if (unfilled == 0 && count != state->common->paths[p].sightings_end) {
1978 state->hint_errors[state->common->paths[p].grid_end] = TRUE;
1982 for (i=0;i<state->common->paths[p].length;i++)
1983 state->cell_errors[state->common->paths[p].xy[i]] = TRUE;
1989 static game_state *execute_move(const game_state *state, const char *move)
1996 game_state *ret = dup_game(state);
2005 if (c == 'G' || c == 'V' || c == 'Z' || c == 'E' ||
2006 c == 'g' || c == 'v' || c == 'z') {
2008 sscanf(move, "%d%n", &x, &n);
2009 if (c == 'G') ret->guess[x] = 1;
2010 if (c == 'V') ret->guess[x] = 2;
2011 if (c == 'Z') ret->guess[x] = 4;
2012 if (c == 'E') { ret->guess[x] = 7; ret->pencils[x] = 0; }
2013 if (c == 'g') ret->pencils[x] ^= 1;
2014 if (c == 'v') ret->pencils[x] ^= 2;
2015 if (c == 'z') ret->pencils[x] ^= 4;
2018 if (c == 'D' && sscanf(move + 1, "%d,%d%n", &x, &y, &n) == 2 &&
2019 is_clue(ret, x, y)) {
2020 ret->hints_done[clue_index(ret, x, y)] ^= 1;
2023 if (*move == ';') move++;
2028 for (i=0;i<ret->common->wh;i++) ret->cell_errors[i] = FALSE;
2029 for (i=0;i<2*ret->common->num_paths;i++) ret->hint_errors[i] = FALSE;
2030 for (i=0;i<3;i++) ret->count_errors[i] = FALSE;
2032 if (!check_numbers_draw(ret,ret->guess)) correct = FALSE;
2034 for (p=0;p<state->common->num_paths;p++)
2035 if (!check_path_solution(ret,p)) correct = FALSE;
2037 for (i=0;i<state->common->num_total;i++)
2038 if (!(ret->guess[i] == 1 || ret->guess[i] == 2 ||
2039 ret->guess[i] == 4)) correct = FALSE;
2041 if (correct && !solver) ret->solved = TRUE;
2042 if (solver) ret->cheated = TRUE;
2047 /* ----------------------------------------------------------------------
2051 #define PREFERRED_TILE_SIZE 64
2053 static void game_compute_size(const game_params *params, int tilesize,
2056 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2057 struct { int tilesize; } ads, *ds = &ads;
2058 ads.tilesize = tilesize;
2060 *x = 2*BORDER+(params->w+2)*TILESIZE;
2061 *y = 2*BORDER+(params->h+3)*TILESIZE;
2065 static void game_set_size(drawing *dr, game_drawstate *ds,
2066 const game_params *params, int tilesize)
2068 ds->tilesize = tilesize;
2072 #define COLOUR(ret, i, r, g, b) ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
2074 static float *game_colours(frontend *fe, int *ncolours)
2076 float *ret = snewn(3 * NCOLOURS, float);
2078 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
2080 ret[COL_GRID * 3 + 0] = 0.0F;
2081 ret[COL_GRID * 3 + 1] = 0.0F;
2082 ret[COL_GRID * 3 + 2] = 0.0F;
2084 ret[COL_TEXT * 3 + 0] = 0.0F;
2085 ret[COL_TEXT * 3 + 1] = 0.0F;
2086 ret[COL_TEXT * 3 + 2] = 0.0F;
2088 ret[COL_ERROR * 3 + 0] = 1.0F;
2089 ret[COL_ERROR * 3 + 1] = 0.0F;
2090 ret[COL_ERROR * 3 + 2] = 0.0F;
2092 ret[COL_HIGHLIGHT * 3 + 0] = 0.78F * ret[COL_BACKGROUND * 3 + 0];
2093 ret[COL_HIGHLIGHT * 3 + 1] = 0.78F * ret[COL_BACKGROUND * 3 + 1];
2094 ret[COL_HIGHLIGHT * 3 + 2] = 0.78F * ret[COL_BACKGROUND * 3 + 2];
2096 ret[COL_FLASH * 3 + 0] = 1.0F;
2097 ret[COL_FLASH * 3 + 1] = 1.0F;
2098 ret[COL_FLASH * 3 + 2] = 1.0F;
2100 ret[COL_GHOST * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2101 ret[COL_GHOST * 3 + 1] = ret[COL_BACKGROUND * 3 + 0];
2102 ret[COL_GHOST * 3 + 2] = ret[COL_BACKGROUND * 3 + 0];
2104 ret[COL_ZOMBIE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2105 ret[COL_ZOMBIE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0];
2106 ret[COL_ZOMBIE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.5F;
2108 ret[COL_VAMPIRE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
2109 ret[COL_VAMPIRE * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
2110 ret[COL_VAMPIRE * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
2112 ret[COL_DONE * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] / 1.5F;
2113 ret[COL_DONE * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] / 1.5F;
2114 ret[COL_DONE * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] / 1.5F;
2116 *ncolours = NCOLOURS;
2120 static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2123 struct game_drawstate *ds = snew(struct game_drawstate);
2126 ds->started = ds->solved = FALSE;
2127 ds->w = state->common->params.w;
2128 ds->h = state->common->params.h;
2131 ds->count_errors[0] = FALSE;
2132 ds->count_errors[1] = FALSE;
2133 ds->count_errors[2] = FALSE;
2135 ds->monsters = snewn(state->common->num_total,int);
2136 for (i=0;i<(state->common->num_total);i++)
2137 ds->monsters[i] = 7;
2138 ds->pencils = snewn(state->common->num_total,unsigned char);
2139 for (i=0;i<state->common->num_total;i++)
2142 ds->cell_errors = snewn(state->common->wh,unsigned char);
2143 for (i=0;i<state->common->wh;i++)
2144 ds->cell_errors[i] = FALSE;
2145 ds->hint_errors = snewn(2*state->common->num_paths,unsigned char);
2146 for (i=0;i<2*state->common->num_paths;i++)
2147 ds->hint_errors[i] = FALSE;
2148 ds->hints_done = snewn(2 * state->common->num_paths, unsigned char);
2149 memset(ds->hints_done, 0,
2150 2 * state->common->num_paths * sizeof(unsigned char));
2152 ds->hshow = ds->hpencil = ds->hflash = 0;
2153 ds->hx = ds->hy = 0;
2157 static void game_free_drawstate(drawing *dr, game_drawstate *ds) {
2158 sfree(ds->hints_done);
2159 sfree(ds->hint_errors);
2160 sfree(ds->cell_errors);
2162 sfree(ds->monsters);
2167 static void draw_cell_background(drawing *dr, game_drawstate *ds,
2168 const game_state *state, const game_ui *ui,
2173 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2174 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2176 hon = (ui->hshow && x == ui->hx && y == ui->hy);
2177 draw_rect(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1,(hon && !ui->hpencil) ? COL_HIGHLIGHT : COL_BACKGROUND);
2179 if (hon && ui->hpencil) {
2181 coords[0] = dx-(TILESIZE/2)+1;
2182 coords[1] = dy-(TILESIZE/2)+1;
2183 coords[2] = coords[0] + TILESIZE/2;
2184 coords[3] = coords[1];
2185 coords[4] = coords[0];
2186 coords[5] = coords[1] + TILESIZE/2;
2187 draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
2190 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1);
2195 static void draw_circle_or_point(drawing *dr, int cx, int cy, int radius,
2199 draw_circle(dr, cx, cy, radius, colour, colour);
2201 draw_rect(dr, cx, cy, 1, 1, colour);
2204 static void draw_monster(drawing *dr, game_drawstate *ds, int x, int y,
2205 int tilesize, int hflash, int monster)
2207 int black = (hflash ? COL_FLASH : COL_TEXT);
2209 if (monster == 1) { /* ghost */
2212 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2+1);
2213 draw_circle(dr,x,y,2*tilesize/5, COL_GHOST,black);
2217 poly[i++] = x - 2*tilesize/5;
2219 poly[i++] = x - 2*tilesize/5;
2220 poly[i++] = y + 2*tilesize/5;
2222 for (j = 0; j < 3; j++) {
2223 int total = (2*tilesize/5) * 2;
2224 int before = total * j / 3;
2225 int after = total * (j+1) / 3;
2226 int mid = (before + after) / 2;
2227 poly[i++] = x - 2*tilesize/5 + mid;
2228 poly[i++] = y + 2*tilesize/5 - (total / 6);
2229 poly[i++] = x - 2*tilesize/5 + after;
2230 poly[i++] = y + 2*tilesize/5;
2233 poly[i++] = x + 2*tilesize/5;
2236 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize-(tilesize/2)-1);
2237 draw_polygon(dr, poly, i/2, COL_GHOST, black);
2240 draw_circle(dr,x-tilesize/6,y-tilesize/12,tilesize/10,
2241 COL_BACKGROUND,black);
2242 draw_circle(dr,x+tilesize/6,y-tilesize/12,tilesize/10,
2243 COL_BACKGROUND,black);
2245 draw_circle_or_point(dr,x-tilesize/6+1+tilesize/48,y-tilesize/12,
2247 draw_circle_or_point(dr,x+tilesize/6+1+tilesize/48,y-tilesize/12,
2250 } else if (monster == 2) { /* vampire */
2253 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize/2);
2254 draw_circle(dr,x,y,2*tilesize/5,black,black);
2257 clip(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize/2+1,tilesize/2);
2258 draw_circle(dr,x-tilesize/7,y,2*tilesize/5-tilesize/7,
2261 clip(dr,x,y-(tilesize/2)+2,tilesize/2+1,tilesize/2);
2262 draw_circle(dr,x+tilesize/7,y,2*tilesize/5-tilesize/7,
2266 clip(dr,x-(tilesize/2)+2,y,tilesize-3,tilesize/2);
2267 draw_circle(dr,x,y,2*tilesize/5, COL_VAMPIRE,black);
2270 draw_circle(dr, x-tilesize/7, y-tilesize/16, tilesize/16,
2271 COL_BACKGROUND, black);
2272 draw_circle(dr, x+tilesize/7, y-tilesize/16, tilesize/16,
2273 COL_BACKGROUND, black);
2274 draw_circle_or_point(dr, x-tilesize/7, y-tilesize/16, tilesize/48,
2276 draw_circle_or_point(dr, x+tilesize/7, y-tilesize/16, tilesize/48,
2279 clip(dr, x-(tilesize/2)+2, y+tilesize/8, tilesize-3, tilesize/4);
2282 poly[i++] = x-3*tilesize/16;
2283 poly[i++] = y+1*tilesize/8;
2284 poly[i++] = x-2*tilesize/16;
2285 poly[i++] = y+7*tilesize/24;
2286 poly[i++] = x-1*tilesize/16;
2287 poly[i++] = y+1*tilesize/8;
2288 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black);
2290 poly[i++] = x+3*tilesize/16;
2291 poly[i++] = y+1*tilesize/8;
2292 poly[i++] = x+2*tilesize/16;
2293 poly[i++] = y+7*tilesize/24;
2294 poly[i++] = x+1*tilesize/16;
2295 poly[i++] = y+1*tilesize/8;
2296 draw_polygon(dr, poly, i/2, COL_BACKGROUND, black);
2298 draw_circle(dr, x, y-tilesize/5, 2*tilesize/5, COL_VAMPIRE, black);
2301 } else if (monster == 4) { /* zombie */
2302 draw_circle(dr,x,y,2*tilesize/5, COL_ZOMBIE,black);
2305 x-tilesize/7-tilesize/16, y-tilesize/12-tilesize/16,
2306 x-tilesize/7+tilesize/16, y-tilesize/12+tilesize/16,
2309 x-tilesize/7+tilesize/16, y-tilesize/12-tilesize/16,
2310 x-tilesize/7-tilesize/16, y-tilesize/12+tilesize/16,
2313 x+tilesize/7-tilesize/16, y-tilesize/12-tilesize/16,
2314 x+tilesize/7+tilesize/16, y-tilesize/12+tilesize/16,
2317 x+tilesize/7+tilesize/16, y-tilesize/12-tilesize/16,
2318 x+tilesize/7-tilesize/16, y-tilesize/12+tilesize/16,
2321 clip(dr, x-tilesize/5, y+tilesize/6, 2*tilesize/5+1, tilesize/2);
2322 draw_circle(dr, x-tilesize/15, y+tilesize/6, tilesize/12,
2323 COL_BACKGROUND, black);
2326 draw_line(dr, x-tilesize/5, y+tilesize/6, x+tilesize/5, y+tilesize/6,
2330 draw_update(dr,x-(tilesize/2)+2,y-(tilesize/2)+2,tilesize-3,tilesize-3);
2333 static void draw_monster_count(drawing *dr, game_drawstate *ds,
2334 const game_state *state, int c, int hflash) {
2340 dx = BORDER+(ds->w+2)*TILESIZE/2+TILESIZE/4;
2343 sprintf(buf,"%d",state->common->num_ghosts);
2348 sprintf(buf,"%d",state->common->num_vampires);
2352 sprintf(buf,"%d",state->common->num_zombies);
2358 draw_rect(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE,
2361 draw_monster(dr, ds, dx-TILESIZE/3, dy+TILESIZE/2,
2362 2*TILESIZE/3, hflash, 1<<c);
2364 draw_text(dr, dx-TILESIZE/3,dy+TILESIZE/2,FONT_VARIABLE,TILESIZE/2,
2365 ALIGN_HCENTRE|ALIGN_VCENTRE,
2366 hflash ? COL_FLASH : COL_TEXT, bufm);
2368 draw_text(dr, dx, dy+TILESIZE/2, FONT_VARIABLE, TILESIZE/2,
2369 ALIGN_HLEFT|ALIGN_VCENTRE,
2370 (state->count_errors[c] ? COL_ERROR :
2371 hflash ? COL_FLASH : COL_TEXT), buf);
2372 draw_update(dr, dx-2*TILESIZE/3, dy, 3*TILESIZE/2, TILESIZE);
2377 static void draw_path_hint(drawing *dr, game_drawstate *ds,
2378 const struct game_params *params,
2379 int hint_index, int hflash, int hint) {
2380 int x, y, color, dx, dy, text_dx, text_dy, text_size;
2383 if (ds->hint_errors[hint_index])
2387 else if (ds->hints_done[hint_index])
2392 range2grid(hint_index, params->w, params->h, &x, &y);
2393 /* Upper-left corner of the "tile" */
2394 dx = BORDER + x * TILESIZE;
2395 dy = BORDER + y * TILESIZE + TILESIZE;
2396 /* Center of the "tile" */
2397 text_dx = dx + TILESIZE / 2;
2398 text_dy = dy + TILESIZE / 2;
2399 /* Avoid wiping out the borders of the puzzle */
2402 text_size = TILESIZE - 3;
2404 sprintf(buf,"%d", hint);
2405 draw_rect(dr, dx, dy, text_size, text_size, COL_BACKGROUND);
2406 draw_text(dr, text_dx, text_dy, FONT_FIXED, TILESIZE / 2,
2407 ALIGN_HCENTRE | ALIGN_VCENTRE, color, buf);
2408 draw_update(dr, dx, dy, text_size, text_size);
2413 static void draw_mirror(drawing *dr, game_drawstate *ds,
2414 const game_state *state, int x, int y,
2415 int hflash, int mirror) {
2416 int dx,dy,mx1,my1,mx2,my2;
2417 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2418 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2420 if (mirror == CELL_MIRROR_L) {
2421 mx1 = dx-(TILESIZE/4);
2422 my1 = dy-(TILESIZE/4);
2423 mx2 = dx+(TILESIZE/4);
2424 my2 = dy+(TILESIZE/4);
2427 mx1 = dx-(TILESIZE/4);
2428 my1 = dy+(TILESIZE/4);
2429 mx2 = dx+(TILESIZE/4);
2430 my2 = dy-(TILESIZE/4);
2432 draw_thick_line(dr,(float)(TILESIZE/16),mx1,my1,mx2,my2,
2433 hflash ? COL_FLASH : COL_TEXT);
2434 draw_update(dr,dx-(TILESIZE/2)+1,dy-(TILESIZE/2)+1,TILESIZE-1,TILESIZE-1);
2439 static void draw_big_monster(drawing *dr, game_drawstate *ds,
2440 const game_state *state, int x, int y,
2441 int hflash, int monster)
2445 dx = BORDER+(x* ds->tilesize)+(TILESIZE/2);
2446 dy = BORDER+(y* ds->tilesize)+(TILESIZE/2)+TILESIZE;
2448 if (monster == 1) sprintf(buf,"G");
2449 else if (monster == 2) sprintf(buf,"V");
2450 else if (monster == 4) sprintf(buf,"Z");
2451 else sprintf(buf," ");
2452 draw_text(dr,dx,dy,FONT_FIXED,TILESIZE/2,ALIGN_HCENTRE|ALIGN_VCENTRE,
2453 hflash ? COL_FLASH : COL_TEXT,buf);
2454 draw_update(dr,dx-(TILESIZE/2)+2,dy-(TILESIZE/2)+2,TILESIZE-3,
2458 draw_monster(dr, ds, dx, dy, 3*TILESIZE/4, hflash, monster);
2463 static void draw_pencils(drawing *dr, game_drawstate *ds,
2464 const game_state *state, int x, int y, int pencil)
2470 dx = BORDER+(x* ds->tilesize)+(TILESIZE/4);
2471 dy = BORDER+(y* ds->tilesize)+(TILESIZE/4)+TILESIZE;
2473 for (i = 0, j = 1; j < 8; j *= 2)
2479 for (py = 0; py < 2; py++)
2480 for (px = 0; px < 2; px++)
2481 if (monsters[py*2+px]) {
2483 draw_monster(dr, ds,
2484 dx + TILESIZE/2 * px, dy + TILESIZE/2 * py,
2485 TILESIZE/2, 0, monsters[py*2+px]);
2488 switch (monsters[py*2+px]) {
2489 case 1: sprintf(buf,"G"); break;
2490 case 2: sprintf(buf,"V"); break;
2491 case 4: sprintf(buf,"Z"); break;
2493 draw_text(dr,dx + TILESIZE/2 * px,dy + TILESIZE/2 * py,
2494 FONT_FIXED,TILESIZE/4,ALIGN_HCENTRE|ALIGN_VCENTRE,
2498 draw_update(dr,dx-(TILESIZE/4)+2,dy-(TILESIZE/4)+2,
2499 (TILESIZE/2)-3,(TILESIZE/2)-3);
2504 #define FLASH_TIME 0.7F
2506 static int is_hint_stale(const game_drawstate *ds, int hflash,
2507 const game_state *state, int index)
2510 if (!ds->started) ret = TRUE;
2511 if (ds->hflash != hflash) ret = TRUE;
2513 if (ds->hint_errors[index] != state->hint_errors[index]) {
2514 ds->hint_errors[index] = state->hint_errors[index];
2518 if (ds->hints_done[index] != state->hints_done[index]) {
2519 ds->hints_done[index] = state->hints_done[index];
2526 static void game_redraw(drawing *dr, game_drawstate *ds,
2527 const game_state *oldstate, const game_state *state,
2528 int dir, const game_ui *ui,
2529 float animtime, float flashtime)
2532 int stale, xi, c, hflash, hchanged, changed_ascii;
2534 hflash = (int)(flashtime * 5 / FLASH_TIME) % 2;
2536 /* Draw static grid components at startup */
2538 draw_rect(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
2539 2*BORDER+(ds->h+3)*TILESIZE, COL_BACKGROUND);
2540 draw_rect(dr, BORDER+TILESIZE-1, BORDER+2*TILESIZE-1,
2541 (ds->w)*TILESIZE +3, (ds->h)*TILESIZE +3, COL_GRID);
2542 for (i=0;i<ds->w;i++)
2543 for (j=0;j<ds->h;j++)
2544 draw_rect(dr, BORDER+(ds->tilesize*(i+1))+1,
2545 BORDER+(ds->tilesize*(j+2))+1, ds->tilesize-1,
2546 ds->tilesize-1, COL_BACKGROUND);
2547 draw_update(dr, 0, 0, 2*BORDER+(ds->w+2)*TILESIZE,
2548 2*BORDER+(ds->h+3)*TILESIZE);
2552 if (ds->hx != ui->hx || ds->hy != ui->hy ||
2553 ds->hshow != ui->hshow || ds->hpencil != ui->hpencil)
2556 if (ds->ascii != ui->ascii) {
2557 ds->ascii = ui->ascii;
2558 changed_ascii = TRUE;
2560 changed_ascii = FALSE;
2562 /* Draw monster count hints */
2566 if (!ds->started) stale = TRUE;
2567 if (ds->hflash != hflash) stale = TRUE;
2568 if (changed_ascii) stale = TRUE;
2569 if (ds->count_errors[i] != state->count_errors[i]) {
2571 ds->count_errors[i] = state->count_errors[i];
2575 draw_monster_count(dr, ds, state, i, hflash);
2579 /* Draw path count hints */
2580 for (i=0;i<state->common->num_paths;i++) {
2581 struct path *path = &state->common->paths[i];
2583 if (is_hint_stale(ds, hflash, state, path->grid_start)) {
2584 draw_path_hint(dr, ds, &state->common->params, path->grid_start,
2585 hflash, path->sightings_start);
2588 if (is_hint_stale(ds, hflash, state, path->grid_end)) {
2589 draw_path_hint(dr, ds, &state->common->params, path->grid_end,
2590 hflash, path->sightings_end);
2594 /* Draw puzzle grid contents */
2595 for (x = 1; x < ds->w+1; x++)
2596 for (y = 1; y < ds->h+1; y++) {
2598 xy = x+y*(state->common->params.w+2);
2599 xi = state->common->xinfo[xy];
2600 c = state->common->grid[xy];
2602 if (!ds->started) stale = TRUE;
2603 if (ds->hflash != hflash) stale = TRUE;
2604 if (changed_ascii) stale = TRUE;
2607 if ((x == ui->hx && y == ui->hy) ||
2608 (x == ds->hx && y == ds->hy))
2612 if (xi >= 0 && (state->guess[xi] != ds->monsters[xi]) ) {
2614 ds->monsters[xi] = state->guess[xi];
2617 if (xi >= 0 && (state->pencils[xi] != ds->pencils[xi]) ) {
2619 ds->pencils[xi] = state->pencils[xi];
2622 if (state->cell_errors[xy] != ds->cell_errors[xy]) {
2624 ds->cell_errors[xy] = state->cell_errors[xy];
2628 draw_cell_background(dr, ds, state, ui, x, y);
2630 draw_mirror(dr, ds, state, x, y, hflash, c);
2631 else if (state->guess[xi] == 1 || state->guess[xi] == 2 ||
2632 state->guess[xi] == 4)
2633 draw_big_monster(dr, ds, state, x, y, hflash, state->guess[xi]);
2635 draw_pencils(dr, ds, state, x, y, state->pencils[xi]);
2639 ds->hx = ui->hx; ds->hy = ui->hy;
2640 ds->hshow = ui->hshow;
2641 ds->hpencil = ui->hpencil;
2642 ds->hflash = hflash;
2647 static float game_anim_length(const game_state *oldstate,
2648 const game_state *newstate, int dir, game_ui *ui)
2653 static float game_flash_length(const game_state *oldstate,
2654 const game_state *newstate, int dir, game_ui *ui)
2656 return (!oldstate->solved && newstate->solved && !oldstate->cheated &&
2657 !newstate->cheated) ? FLASH_TIME : 0.0F;
2660 static int game_status(const game_state *state)
2662 return state->solved;
2665 static int game_timing_state(const game_state *state, game_ui *ui)
2670 static void game_print_size(const game_params *params, float *x, float *y)
2674 static void game_print(drawing *dr, const game_state *state, int tilesize)
2679 #define thegame undead
2682 const struct game thegame = {
2683 "Undead", "games.undead", "undead",
2690 TRUE, game_configure, custom_params,
2698 TRUE, game_can_format_as_text_now, game_text_format,
2706 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2709 game_free_drawstate,
2714 FALSE, FALSE, game_print_size, game_print,
2715 FALSE, /* wants_statusbar */
2716 FALSE, game_timing_state,