summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/loopy.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/loopy.c')
-rw-r--r--apps/plugins/puzzles/src/loopy.c576
1 files changed, 329 insertions, 247 deletions
diff --git a/apps/plugins/puzzles/src/loopy.c b/apps/plugins/puzzles/src/loopy.c
index 176b56285c..b30dd68f4d 100644
--- a/apps/plugins/puzzles/src/loopy.c
+++ b/apps/plugins/puzzles/src/loopy.c
@@ -25,28 +25,28 @@
25 * outside respectively. So if you can track this for all 25 * outside respectively. So if you can track this for all
26 * faces, you figure out the state of the line between a pair 26 * faces, you figure out the state of the line between a pair
27 * once their relative insideness is known. 27 * once their relative insideness is known.
28 * + The way I envisage this working is simply to keep an edsf 28 * + The way I envisage this working is simply to keep a flip dsf
29 * of all _faces_, which indicates whether they're on 29 * of all _faces_, which indicates whether they're on
30 * opposite sides of the loop from one another. We also 30 * opposite sides of the loop from one another. We also
31 * include a special entry in the edsf for the infinite 31 * include a special entry in the dsf for the infinite
32 * exterior "face". 32 * exterior "face".
33 * + So, the simple way to do this is to just go through the 33 * + So, the simple way to do this is to just go through the
34 * edges: every time we see an edge in a state other than 34 * edges: every time we see an edge in a state other than
35 * LINE_UNKNOWN which separates two faces that aren't in the 35 * LINE_UNKNOWN which separates two faces that aren't in the
36 * same edsf class, we can rectify that by merging the 36 * same dsf class, we can rectify that by merging the
37 * classes. Then, conversely, an edge in LINE_UNKNOWN state 37 * classes. Then, conversely, an edge in LINE_UNKNOWN state
38 * which separates two faces that _are_ in the same edsf 38 * which separates two faces that _are_ in the same dsf
39 * class can immediately have its state determined. 39 * class can immediately have its state determined.
40 * + But you can go one better, if you're prepared to loop 40 * + But you can go one better, if you're prepared to loop
41 * over all _pairs_ of edges. Suppose we have edges A and B, 41 * over all _pairs_ of edges. Suppose we have edges A and B,
42 * which respectively separate faces A1,A2 and B1,B2. 42 * which respectively separate faces A1,A2 and B1,B2.
43 * Suppose that A,B are in the same edge-edsf class and that 43 * Suppose that A,B are in the same edge-dsf class and that
44 * A1,B1 (wlog) are in the same face-edsf class; then we can 44 * A1,B1 (wlog) are in the same face-dsf class; then we can
45 * immediately place A2,B2 into the same face-edsf class (as 45 * immediately place A2,B2 into the same face-dsf class (as
46 * each other, not as A1 and A2) one way round or the other. 46 * each other, not as A1 and A2) one way round or the other.
47 * And conversely again, if A1,B1 are in the same face-edsf 47 * And conversely again, if A1,B1 are in the same face-dsf
48 * class and so are A2,B2, then we can put A,B into the same 48 * class and so are A2,B2, then we can put A,B into the same
49 * face-edsf class. 49 * face-dsf class.
50 * * Of course, this deduction requires a quadratic-time 50 * * Of course, this deduction requires a quadratic-time
51 * loop over all pairs of edges in the grid, so it should 51 * loop over all pairs of edges in the grid, so it should
52 * be reserved until there's nothing easier left to be 52 * be reserved until there's nothing easier left to be
@@ -77,7 +77,11 @@
77#include <string.h> 77#include <string.h>
78#include <assert.h> 78#include <assert.h>
79#include <ctype.h> 79#include <ctype.h>
80#include <math.h> 80#ifdef NO_TGMATH_H
81# include <math.h>
82#else
83# include <tgmath.h>
84#endif
81 85
82#include "puzzles.h" 86#include "puzzles.h"
83#include "tree234.h" 87#include "tree234.h"
@@ -153,7 +157,7 @@ typedef struct solver_state {
153 char *face_yes_count; 157 char *face_yes_count;
154 char *face_no_count; 158 char *face_no_count;
155 bool *dot_solved, *face_solved; 159 bool *dot_solved, *face_solved;
156 int *dotdsf; 160 DSF *dotdsf;
157 161
158 /* Information for Normal level deductions: 162 /* Information for Normal level deductions:
159 * For each dline, store a bitmask for whether we know: 163 * For each dline, store a bitmask for whether we know:
@@ -162,7 +166,7 @@ typedef struct solver_state {
162 char *dlines; 166 char *dlines;
163 167
164 /* Hard level information */ 168 /* Hard level information */
165 int *linedsf; 169 DSF *linedsf;
166} solver_state; 170} solver_state;
167 171
168/* 172/*
@@ -279,6 +283,9 @@ static void check_caches(const solver_state* sstate);
279 A("Penrose (rhombs)",PENROSE_P3,3,3) \ 283 A("Penrose (rhombs)",PENROSE_P3,3,3) \
280 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \ 284 A("Great-Great-Dodecagonal",GREATGREATDODECAGONAL,2,2) \
281 A("Kagome",KAGOME,3,3) \ 285 A("Kagome",KAGOME,3,3) \
286 A("Compass-Dodecagonal",COMPASSDODECAGONAL,2,2) \
287 A("Hats",HATS,6,6) \
288 A("Spectres",SPECTRES,6,6) \
282 /* end of list */ 289 /* end of list */
283 290
284#define GRID_NAME(title,type,amin,omin) title, 291#define GRID_NAME(title,type,amin,omin) title,
@@ -380,7 +387,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
380 ret->solver_status = SOLVER_INCOMPLETE; 387 ret->solver_status = SOLVER_INCOMPLETE;
381 ret->diff = diff; 388 ret->diff = diff;
382 389
383 ret->dotdsf = snew_dsf(num_dots); 390 ret->dotdsf = dsf_new(num_dots);
384 ret->looplen = snewn(num_dots, int); 391 ret->looplen = snewn(num_dots, int);
385 392
386 for (i = 0; i < num_dots; i++) { 393 for (i = 0; i < num_dots; i++) {
@@ -411,7 +418,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
411 if (diff < DIFF_HARD) { 418 if (diff < DIFF_HARD) {
412 ret->linedsf = NULL; 419 ret->linedsf = NULL;
413 } else { 420 } else {
414 ret->linedsf = snew_dsf(state->game_grid->num_edges); 421 ret->linedsf = dsf_new_flip(state->game_grid->num_edges);
415 } 422 }
416 423
417 return ret; 424 return ret;
@@ -420,7 +427,7 @@ static solver_state *new_solver_state(const game_state *state, int diff) {
420static void free_solver_state(solver_state *sstate) { 427static void free_solver_state(solver_state *sstate) {
421 if (sstate) { 428 if (sstate) {
422 free_game(sstate->state); 429 free_game(sstate->state);
423 sfree(sstate->dotdsf); 430 dsf_free(sstate->dotdsf);
424 sfree(sstate->looplen); 431 sfree(sstate->looplen);
425 sfree(sstate->dot_solved); 432 sfree(sstate->dot_solved);
426 sfree(sstate->face_solved); 433 sfree(sstate->face_solved);
@@ -431,7 +438,7 @@ static void free_solver_state(solver_state *sstate) {
431 438
432 /* OK, because sfree(NULL) is a no-op */ 439 /* OK, because sfree(NULL) is a no-op */
433 sfree(sstate->dlines); 440 sfree(sstate->dlines);
434 sfree(sstate->linedsf); 441 dsf_free(sstate->linedsf);
435 442
436 sfree(sstate); 443 sfree(sstate);
437 } 444 }
@@ -449,10 +456,9 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
449 ret->solver_status = sstate->solver_status; 456 ret->solver_status = sstate->solver_status;
450 ret->diff = sstate->diff; 457 ret->diff = sstate->diff;
451 458
452 ret->dotdsf = snewn(num_dots, int); 459 ret->dotdsf = dsf_new(num_dots);
453 ret->looplen = snewn(num_dots, int); 460 ret->looplen = snewn(num_dots, int);
454 memcpy(ret->dotdsf, sstate->dotdsf, 461 dsf_copy(ret->dotdsf, sstate->dotdsf);
455 num_dots * sizeof(int));
456 memcpy(ret->looplen, sstate->looplen, 462 memcpy(ret->looplen, sstate->looplen,
457 num_dots * sizeof(int)); 463 num_dots * sizeof(int));
458 464
@@ -480,9 +486,8 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
480 } 486 }
481 487
482 if (sstate->linedsf) { 488 if (sstate->linedsf) {
483 ret->linedsf = snewn(num_edges, int); 489 ret->linedsf = dsf_new_flip(num_edges);
484 memcpy(ret->linedsf, sstate->linedsf, 490 dsf_copy(ret->linedsf, sstate->linedsf);
485 num_edges * sizeof(int));
486 } else { 491 } else {
487 ret->linedsf = NULL; 492 ret->linedsf = NULL;
488 } 493 }
@@ -552,6 +557,9 @@ static const game_params loopy_presets_more[] = {
552 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 557 { 3, 3, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
553 { 3, 3, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL }, 558 { 3, 3, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
554 { 3, 2, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL }, 559 { 3, 2, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
560 { 3, 3, DIFF_HARD, LOOPY_GRID_COMPASSDODECAGONAL },
561 { 6, 6, DIFF_HARD, LOOPY_GRID_HATS },
562 { 6, 6, DIFF_HARD, LOOPY_GRID_SPECTRES },
555#else 563#else
556 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB }, 564 { 10, 10, DIFF_HARD, LOOPY_GRID_HONEYCOMB },
557 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL }, 565 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATHEXAGONAL },
@@ -561,6 +569,9 @@ static const game_params loopy_presets_more[] = {
561 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL }, 569 { 5, 4, DIFF_HARD, LOOPY_GRID_DODECAGONAL },
562 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL }, 570 { 5, 4, DIFF_HARD, LOOPY_GRID_GREATDODECAGONAL },
563 { 5, 3, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL }, 571 { 5, 3, DIFF_HARD, LOOPY_GRID_GREATGREATDODECAGONAL },
572 { 5, 4, DIFF_HARD, LOOPY_GRID_COMPASSDODECAGONAL },
573 { 10, 10, DIFF_HARD, LOOPY_GRID_HATS },
574 { 10, 10, DIFF_HARD, LOOPY_GRID_SPECTRES },
564#endif 575#endif
565}; 576};
566 577
@@ -681,6 +692,7 @@ static game_params *custom_params(const config_item *cfg)
681 692
682static const char *validate_params(const game_params *params, bool full) 693static const char *validate_params(const game_params *params, bool full)
683{ 694{
695 const char *err;
684 if (params->type < 0 || params->type >= NUM_GRID_TYPES) 696 if (params->type < 0 || params->type >= NUM_GRID_TYPES)
685 return "Illegal grid type"; 697 return "Illegal grid type";
686 if (params->w < grid_size_limits[params->type].amin || 698 if (params->w < grid_size_limits[params->type].amin ||
@@ -689,6 +701,8 @@ static const char *validate_params(const game_params *params, bool full)
689 if (params->w < grid_size_limits[params->type].omin && 701 if (params->w < grid_size_limits[params->type].omin &&
690 params->h < grid_size_limits[params->type].omin) 702 params->h < grid_size_limits[params->type].omin)
691 return grid_size_limits[params->type].oerr; 703 return grid_size_limits[params->type].oerr;
704 err = grid_validate_params(grid_types[params->type], params->w, params->h);
705 if (err != NULL) return err;
692 706
693 /* 707 /*
694 * This shouldn't be able to happen at all, since decode_params 708 * This shouldn't be able to happen at all, since decode_params
@@ -771,10 +785,13 @@ static const char *validate_desc(const game_params *params, const char *desc)
771 * know is the precise number of faces. */ 785 * know is the precise number of faces. */
772 grid_desc = extract_grid_desc(&desc); 786 grid_desc = extract_grid_desc(&desc);
773 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc); 787 ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
774 if (ret) return ret; 788 if (ret) {
789 sfree(grid_desc);
790 return ret;
791 }
775 792
776 g = loopy_generate_grid(params, grid_desc); 793 g = loopy_generate_grid(params, grid_desc);
777 if (grid_desc) sfree(grid_desc); 794 sfree(grid_desc);
778 795
779 for (; *desc; ++desc) { 796 for (; *desc; ++desc) {
780 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) { 797 if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
@@ -785,13 +802,18 @@ static const char *validate_desc(const game_params *params, const char *desc)
785 count += *desc - 'a' + 1; 802 count += *desc - 'a' + 1;
786 continue; 803 continue;
787 } 804 }
805 grid_free(g);
788 return "Unknown character in description"; 806 return "Unknown character in description";
789 } 807 }
790 808
791 if (count < g->num_faces) 809 if (count < g->num_faces) {
810 grid_free(g);
792 return "Description too short for board size"; 811 return "Description too short for board size";
793 if (count > g->num_faces) 812 }
813 if (count > g->num_faces) {
814 grid_free(g);
794 return "Description too long for board size"; 815 return "Description too long for board size";
816 }
795 817
796 grid_free(g); 818 grid_free(g);
797 819
@@ -850,22 +872,98 @@ static char *encode_solve_move(const game_state *state)
850 return ret; 872 return ret;
851} 873}
852 874
875struct game_ui {
876 /*
877 * User preference: should grid lines in LINE_NO state be drawn
878 * very faintly so users can still see where they are, or should
879 * they be completely invisible?
880 */
881 bool draw_faint_lines;
882
883 /*
884 * User preference: when clicking an edge that has only one
885 * possible edge connecting to one (or both) of its ends, should
886 * that edge also change to the same state as the edge we just
887 * clicked?
888 */
889 enum {
890 AF_OFF, /* no, all grid edges are independent in the UI */
891 AF_FIXED, /* yes, but only based on the grid itself */
892 AF_ADAPTIVE /* yes, and consider edges user has already set to NO */
893 } autofollow;
894};
895
896static void legacy_prefs_override(struct game_ui *ui_out)
897{
898 static bool initialised = false;
899 static int draw_faint_lines = -1;
900 static int autofollow = -1;
901
902 if (!initialised) {
903 char *env;
904
905 initialised = true;
906 draw_faint_lines = getenv_bool("LOOPY_FAINT_LINES", -1);
907
908 if ((env = getenv("LOOPY_AUTOFOLLOW")) != NULL) {
909 if (!strcmp(env, "off"))
910 autofollow = AF_OFF;
911 else if (!strcmp(env, "fixed"))
912 autofollow = AF_FIXED;
913 else if (!strcmp(env, "adaptive"))
914 autofollow = AF_ADAPTIVE;
915 }
916 }
917
918 if (draw_faint_lines != -1)
919 ui_out->draw_faint_lines = draw_faint_lines;
920 if (autofollow != -1)
921 ui_out->autofollow = autofollow;
922}
923
853static game_ui *new_ui(const game_state *state) 924static game_ui *new_ui(const game_state *state)
854{ 925{
855 return NULL; 926 game_ui *ui = snew(game_ui);
927 ui->draw_faint_lines = true;
928 ui->autofollow = AF_OFF;
929 legacy_prefs_override(ui);
930 return ui;
856} 931}
857 932
858static void free_ui(game_ui *ui) 933static void free_ui(game_ui *ui)
859{ 934{
935 sfree(ui);
860} 936}
861 937
862static char *encode_ui(const game_ui *ui) 938static config_item *get_prefs(game_ui *ui)
863{ 939{
864 return NULL; 940 config_item *ret;
941
942 ret = snewn(3, config_item);
943
944 ret[0].name = "Draw excluded grid lines faintly";
945 ret[0].kw = "draw-faint-lines";
946 ret[0].type = C_BOOLEAN;
947 ret[0].u.boolean.bval = ui->draw_faint_lines;
948
949 ret[1].name = "Auto-follow unique paths of edges";
950 ret[1].kw = "auto-follow";
951 ret[1].type = C_CHOICES;
952 ret[1].u.choices.choicenames =
953 ":No:Based on grid only:Based on grid and game state";
954 ret[1].u.choices.choicekws = ":off:fixed:adaptive";
955 ret[1].u.choices.selected = ui->autofollow;
956
957 ret[2].name = NULL;
958 ret[2].type = C_END;
959
960 return ret;
865} 961}
866 962
867static void decode_ui(game_ui *ui, const char *encoding) 963static void set_prefs(game_ui *ui, const config_item *cfg)
868{ 964{
965 ui->draw_faint_lines = cfg[0].u.boolean.bval;
966 ui->autofollow = cfg[1].u.choices.selected;
869} 967}
870 968
871static void game_changed_state(game_ui *ui, const game_state *oldstate, 969static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -874,7 +972,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
874} 972}
875 973
876static void game_compute_size(const game_params *params, int tilesize, 974static void game_compute_size(const game_params *params, int tilesize,
877 int *x, int *y) 975 const game_ui *ui, int *x, int *y)
878{ 976{
879 int grid_width, grid_height, rendered_width, rendered_height; 977 int grid_width, grid_height, rendered_width, rendered_height;
880 int g_tilesize; 978 int g_tilesize;
@@ -974,11 +1072,6 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
974 sfree(ds); 1072 sfree(ds);
975} 1073}
976 1074
977static bool game_timing_state(const game_state *state, game_ui *ui)
978{
979 return true;
980}
981
982static float game_anim_length(const game_state *oldstate, 1075static float game_anim_length(const game_state *oldstate,
983 const game_state *newstate, int dir, game_ui *ui) 1076 const game_state *newstate, int dir, game_ui *ui)
984{ 1077{
@@ -1004,7 +1097,7 @@ static char *game_text_format(const game_state *state)
1004 assert(state->grid_type == 0); 1097 assert(state->grid_type == 0);
1005 1098
1006 /* Work out the basic size unit */ 1099 /* Work out the basic size unit */
1007 f = g->faces; /* first face */ 1100 f = g->faces[0]; /* first face */
1008 assert(f->order == 4); 1101 assert(f->order == 4);
1009 /* The dots are ordered clockwise, so the two opposite 1102 /* The dots are ordered clockwise, so the two opposite
1010 * corners are guaranteed to span the square */ 1103 * corners are guaranteed to span the square */
@@ -1027,7 +1120,7 @@ static char *game_text_format(const game_state *state)
1027 1120
1028 /* Fill in edge info */ 1121 /* Fill in edge info */
1029 for (i = 0; i < g->num_edges; i++) { 1122 for (i = 0; i < g->num_edges; i++) {
1030 grid_edge *e = g->edges + i; 1123 grid_edge *e = g->edges[i];
1031 /* Cell coordinates, from (0,0) to (w-1,h-1) */ 1124 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1032 int x1 = (e->dot1->x - g->lowest_x) / cell_size; 1125 int x1 = (e->dot1->x - g->lowest_x) / cell_size;
1033 int x2 = (e->dot2->x - g->lowest_x) / cell_size; 1126 int x2 = (e->dot2->x - g->lowest_x) / cell_size;
@@ -1055,7 +1148,7 @@ static char *game_text_format(const game_state *state)
1055 for (i = 0; i < g->num_faces; i++) { 1148 for (i = 0; i < g->num_faces; i++) {
1056 int x1, x2, y1, y2; 1149 int x1, x2, y1, y2;
1057 1150
1058 f = g->faces + i; 1151 f = g->faces[i];
1059 assert(f->order == 4); 1152 assert(f->order == 4);
1060 /* Cell coordinates, from (0,0) to (w-1,h-1) */ 1153 /* Cell coordinates, from (0,0) to (w-1,h-1) */
1061 x1 = (f->dots[0]->x - g->lowest_x) / cell_size; 1154 x1 = (f->dots[0]->x - g->lowest_x) / cell_size;
@@ -1135,26 +1228,26 @@ static bool solver_set_line(solver_state *sstate, int i,
1135#endif 1228#endif
1136 1229
1137 g = state->game_grid; 1230 g = state->game_grid;
1138 e = g->edges + i; 1231 e = g->edges[i];
1139 1232
1140 /* Update the cache for both dots and both faces affected by this. */ 1233 /* Update the cache for both dots and both faces affected by this. */
1141 if (line_new == LINE_YES) { 1234 if (line_new == LINE_YES) {
1142 sstate->dot_yes_count[e->dot1 - g->dots]++; 1235 sstate->dot_yes_count[e->dot1->index]++;
1143 sstate->dot_yes_count[e->dot2 - g->dots]++; 1236 sstate->dot_yes_count[e->dot2->index]++;
1144 if (e->face1) { 1237 if (e->face1) {
1145 sstate->face_yes_count[e->face1 - g->faces]++; 1238 sstate->face_yes_count[e->face1->index]++;
1146 } 1239 }
1147 if (e->face2) { 1240 if (e->face2) {
1148 sstate->face_yes_count[e->face2 - g->faces]++; 1241 sstate->face_yes_count[e->face2->index]++;
1149 } 1242 }
1150 } else { 1243 } else {
1151 sstate->dot_no_count[e->dot1 - g->dots]++; 1244 sstate->dot_no_count[e->dot1->index]++;
1152 sstate->dot_no_count[e->dot2 - g->dots]++; 1245 sstate->dot_no_count[e->dot2->index]++;
1153 if (e->face1) { 1246 if (e->face1) {
1154 sstate->face_no_count[e->face1 - g->faces]++; 1247 sstate->face_no_count[e->face1->index]++;
1155 } 1248 }
1156 if (e->face2) { 1249 if (e->face2) {
1157 sstate->face_no_count[e->face2 - g->faces]++; 1250 sstate->face_no_count[e->face2->index]++;
1158 } 1251 }
1159 } 1252 }
1160 1253
@@ -1178,10 +1271,10 @@ static bool merge_dots(solver_state *sstate, int edge_index)
1178{ 1271{
1179 int i, j, len; 1272 int i, j, len;
1180 grid *g = sstate->state->game_grid; 1273 grid *g = sstate->state->game_grid;
1181 grid_edge *e = g->edges + edge_index; 1274 grid_edge *e = g->edges[edge_index];
1182 1275
1183 i = e->dot1 - g->dots; 1276 i = e->dot1->index;
1184 j = e->dot2 - g->dots; 1277 j = e->dot2->index;
1185 1278
1186 i = dsf_canonify(sstate->dotdsf, i); 1279 i = dsf_canonify(sstate->dotdsf, i);
1187 j = dsf_canonify(sstate->dotdsf, j); 1280 j = dsf_canonify(sstate->dotdsf, j);
@@ -1211,12 +1304,12 @@ static bool merge_lines(solver_state *sstate, int i, int j, bool inverse
1211 assert(i < sstate->state->game_grid->num_edges); 1304 assert(i < sstate->state->game_grid->num_edges);
1212 assert(j < sstate->state->game_grid->num_edges); 1305 assert(j < sstate->state->game_grid->num_edges);
1213 1306
1214 i = edsf_canonify(sstate->linedsf, i, &inv_tmp); 1307 i = dsf_canonify_flip(sstate->linedsf, i, &inv_tmp);
1215 inverse ^= inv_tmp; 1308 inverse ^= inv_tmp;
1216 j = edsf_canonify(sstate->linedsf, j, &inv_tmp); 1309 j = dsf_canonify_flip(sstate->linedsf, j, &inv_tmp);
1217 inverse ^= inv_tmp; 1310 inverse ^= inv_tmp;
1218 1311
1219 edsf_merge(sstate->linedsf, i, j, inverse); 1312 dsf_merge_flip(sstate->linedsf, i, j, inverse);
1220 1313
1221#ifdef SHOW_WORKING 1314#ifdef SHOW_WORKING
1222 if (i != j) { 1315 if (i != j) {
@@ -1239,12 +1332,12 @@ static int dot_order(const game_state* state, int dot, char line_type)
1239{ 1332{
1240 int n = 0; 1333 int n = 0;
1241 grid *g = state->game_grid; 1334 grid *g = state->game_grid;
1242 grid_dot *d = g->dots + dot; 1335 grid_dot *d = g->dots[dot];
1243 int i; 1336 int i;
1244 1337
1245 for (i = 0; i < d->order; i++) { 1338 for (i = 0; i < d->order; i++) {
1246 grid_edge *e = d->edges[i]; 1339 grid_edge *e = d->edges[i];
1247 if (state->lines[e - g->edges] == line_type) 1340 if (state->lines[e->index] == line_type)
1248 ++n; 1341 ++n;
1249 } 1342 }
1250 return n; 1343 return n;
@@ -1256,12 +1349,12 @@ static int face_order(const game_state* state, int face, char line_type)
1256{ 1349{
1257 int n = 0; 1350 int n = 0;
1258 grid *g = state->game_grid; 1351 grid *g = state->game_grid;
1259 grid_face *f = g->faces + face; 1352 grid_face *f = g->faces[face];
1260 int i; 1353 int i;
1261 1354
1262 for (i = 0; i < f->order; i++) { 1355 for (i = 0; i < f->order; i++) {
1263 grid_edge *e = f->edges[i]; 1356 grid_edge *e = f->edges[i];
1264 if (state->lines[e - g->edges] == line_type) 1357 if (state->lines[e->index] == line_type)
1265 ++n; 1358 ++n;
1266 } 1359 }
1267 return n; 1360 return n;
@@ -1282,10 +1375,10 @@ static bool dot_setall(solver_state *sstate, int dot,
1282 return false; 1375 return false;
1283 1376
1284 g = state->game_grid; 1377 g = state->game_grid;
1285 d = g->dots + dot; 1378 d = g->dots[dot];
1286 1379
1287 for (i = 0; i < d->order; i++) { 1380 for (i = 0; i < d->order; i++) {
1288 int line_index = d->edges[i] - g->edges; 1381 int line_index = d->edges[i]->index;
1289 if (state->lines[line_index] == old_type) { 1382 if (state->lines[line_index] == old_type) {
1290 r = solver_set_line(sstate, line_index, new_type); 1383 r = solver_set_line(sstate, line_index, new_type);
1291 assert(r); 1384 assert(r);
@@ -1309,10 +1402,10 @@ static bool face_setall(solver_state *sstate, int face,
1309 return false; 1402 return false;
1310 1403
1311 g = state->game_grid; 1404 g = state->game_grid;
1312 f = g->faces + face; 1405 f = g->faces[face];
1313 1406
1314 for (i = 0; i < f->order; i++) { 1407 for (i = 0; i < f->order; i++) {
1315 int line_index = f->edges[i] - g->edges; 1408 int line_index = f->edges[i]->index;
1316 if (state->lines[line_index] == old_type) { 1409 if (state->lines[line_index] == old_type) {
1317 r = solver_set_line(sstate, line_index, new_type); 1410 r = solver_set_line(sstate, line_index, new_type);
1318 assert(r); 1411 assert(r);
@@ -1341,7 +1434,7 @@ static void add_full_clues(game_state *state, random_state *rs)
1341 * algorithm does work, and there aren't any GREY faces still there. */ 1434 * algorithm does work, and there aren't any GREY faces still there. */
1342 memset(clues, 0, g->num_faces); 1435 memset(clues, 0, g->num_faces);
1343 for (i = 0; i < g->num_edges; i++) { 1436 for (i = 0; i < g->num_edges; i++) {
1344 grid_edge *e = g->edges + i; 1437 grid_edge *e = g->edges[i];
1345 grid_face *f1 = e->face1; 1438 grid_face *f1 = e->face1;
1346 grid_face *f2 = e->face2; 1439 grid_face *f2 = e->face2;
1347 enum face_colour c1 = FACE_COLOUR(f1); 1440 enum face_colour c1 = FACE_COLOUR(f1);
@@ -1349,8 +1442,8 @@ static void add_full_clues(game_state *state, random_state *rs)
1349 assert(c1 != FACE_GREY); 1442 assert(c1 != FACE_GREY);
1350 assert(c2 != FACE_GREY); 1443 assert(c2 != FACE_GREY);
1351 if (c1 != c2) { 1444 if (c1 != c2) {
1352 if (f1) clues[f1 - g->faces]++; 1445 if (f1) clues[f1->index]++;
1353 if (f2) clues[f2 - g->faces]++; 1446 if (f2) clues[f2->index]++;
1354 } 1447 }
1355 } 1448 }
1356 sfree(board); 1449 sfree(board);
@@ -1361,7 +1454,7 @@ static bool game_has_unique_soln(const game_state *state, int diff)
1361{ 1454{
1362 bool ret; 1455 bool ret;
1363 solver_state *sstate_new; 1456 solver_state *sstate_new;
1364 solver_state *sstate = new_solver_state((game_state *)state, diff); 1457 solver_state *sstate = new_solver_state(state, diff);
1365 1458
1366 sstate_new = solve_game_rec(sstate); 1459 sstate_new = solve_game_rec(sstate);
1367 1460
@@ -1541,7 +1634,8 @@ static bool check_completion(game_state *state)
1541 grid *g = state->game_grid; 1634 grid *g = state->game_grid;
1542 int i; 1635 int i;
1543 bool ret; 1636 bool ret;
1544 int *dsf, *component_state; 1637 DSF *dsf;
1638 int *component_state;
1545 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize; 1639 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1546 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY }; 1640 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1547 1641
@@ -1626,13 +1720,13 @@ static bool check_completion(game_state *state)
1626 * leave that one unhighlighted, and light the rest up in red. 1720 * leave that one unhighlighted, and light the rest up in red.
1627 */ 1721 */
1628 1722
1629 dsf = snew_dsf(g->num_dots); 1723 dsf = dsf_new(g->num_dots);
1630 1724
1631 /* Build the dsf. */ 1725 /* Build the dsf. */
1632 for (i = 0; i < g->num_edges; i++) { 1726 for (i = 0; i < g->num_edges; i++) {
1633 if (state->lines[i] == LINE_YES) { 1727 if (state->lines[i] == LINE_YES) {
1634 grid_edge *e = g->edges + i; 1728 grid_edge *e = g->edges[i];
1635 int d1 = e->dot1 - g->dots, d2 = e->dot2 - g->dots; 1729 int d1 = e->dot1->index, d2 = e->dot2->index;
1636 dsf_merge(dsf, d1, d2); 1730 dsf_merge(dsf, d1, d2);
1637 } 1731 }
1638 } 1732 }
@@ -1657,10 +1751,10 @@ static bool check_completion(game_state *state)
1657 int unknown = dot_order(state, i, LINE_UNKNOWN); 1751 int unknown = dot_order(state, i, LINE_UNKNOWN);
1658 if ((yes == 1 && unknown == 0) || (yes >= 3)) { 1752 if ((yes == 1 && unknown == 0) || (yes >= 3)) {
1659 /* violation, so mark all YES edges as errors */ 1753 /* violation, so mark all YES edges as errors */
1660 grid_dot *d = g->dots + i; 1754 grid_dot *d = g->dots[i];
1661 int j; 1755 int j;
1662 for (j = 0; j < d->order; j++) { 1756 for (j = 0; j < d->order; j++) {
1663 int e = d->edges[j] - g->edges; 1757 int e = d->edges[j]->index;
1664 if (state->lines[e] == LINE_YES) 1758 if (state->lines[e] == LINE_YES)
1665 state->line_errors[e] = true; 1759 state->line_errors[e] = true;
1666 } 1760 }
@@ -1723,8 +1817,8 @@ static bool check_completion(game_state *state)
1723 */ 1817 */
1724 for (i = 0; i < g->num_edges; i++) { 1818 for (i = 0; i < g->num_edges; i++) {
1725 if (state->lines[i] == LINE_YES) { 1819 if (state->lines[i] == LINE_YES) {
1726 grid_edge *e = g->edges + i; 1820 grid_edge *e = g->edges[i];
1727 int d1 = e->dot1 - g->dots; /* either endpoint is good enough */ 1821 int d1 = e->dot1->index; /* either endpoint is good enough */
1728 int comp = dsf_canonify(dsf, d1); 1822 int comp = dsf_canonify(dsf, d1);
1729 if ((component_state[comp] == COMP_PATH && 1823 if ((component_state[comp] == COMP_PATH &&
1730 -1 != largest_comp) || 1824 -1 != largest_comp) ||
@@ -1763,7 +1857,7 @@ static bool check_completion(game_state *state)
1763 } 1857 }
1764 1858
1765 sfree(component_state); 1859 sfree(component_state);
1766 sfree(dsf); 1860 dsf_free(dsf);
1767 1861
1768 return ret; 1862 return ret;
1769} 1863}
@@ -1782,7 +1876,7 @@ static bool check_completion(game_state *state)
1782 * know both or neither is on that's already stored more directly.) 1876 * know both or neither is on that's already stored more directly.)
1783 * 1877 *
1784 * Advanced Mode 1878 * Advanced Mode
1785 * Use edsf data structure to make equivalence classes of lines that are 1879 * Use flip dsf data structure to make equivalence classes of lines that are
1786 * known identical to or opposite to one another. 1880 * known identical to or opposite to one another.
1787 */ 1881 */
1788 1882
@@ -1822,11 +1916,10 @@ static int dline_index_from_dot(grid *g, grid_dot *d, int i)
1822 if (i2 == d->order) i2 = 0; 1916 if (i2 == d->order) i2 = 0;
1823 e2 = d->edges[i2]; 1917 e2 = d->edges[i2];
1824#endif 1918#endif
1825 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); 1919 ret = 2 * (e->index) + ((e->dot1 == d) ? 1 : 0);
1826#ifdef DEBUG_DLINES 1920#ifdef DEBUG_DLINES
1827 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n", 1921 printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n",
1828 (int)(d - g->dots), i, (int)(e - g->edges), 1922 (int)(d->index), i, (int)(e->index), (int)(e2 ->index), ret);
1829 (int)(e2 - g->edges), ret);
1830#endif 1923#endif
1831 return ret; 1924 return ret;
1832} 1925}
@@ -1845,11 +1938,10 @@ static int dline_index_from_face(grid *g, grid_face *f, int i)
1845 if (i2 < 0) i2 += f->order; 1938 if (i2 < 0) i2 += f->order;
1846 e2 = f->edges[i2]; 1939 e2 = f->edges[i2];
1847#endif 1940#endif
1848 ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0); 1941 ret = 2 * (e->index) + ((e->dot1 == d) ? 1 : 0);
1849#ifdef DEBUG_DLINES 1942#ifdef DEBUG_DLINES
1850 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n", 1943 printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n",
1851 (int)(f - g->faces), i, (int)(e - g->edges), 1944 (int)(f->index), i, (int)(e->index), (int)(e2->index), ret);
1852 (int)(e2 - g->edges), ret);
1853#endif 1945#endif
1854 return ret; 1946 return ret;
1855} 1947}
@@ -1907,9 +1999,9 @@ static bool dline_set_opp_atleastone(solver_state *sstate,
1907 opp2 = opp + 1; 1999 opp2 = opp + 1;
1908 if (opp2 == N) opp2 = 0; 2000 if (opp2 == N) opp2 = 0;
1909 /* Check if opp, opp2 point to LINE_UNKNOWNs */ 2001 /* Check if opp, opp2 point to LINE_UNKNOWNs */
1910 if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN) 2002 if (state->lines[d->edges[opp]->index] != LINE_UNKNOWN)
1911 continue; 2003 continue;
1912 if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN) 2004 if (state->lines[d->edges[opp2]->index] != LINE_UNKNOWN)
1913 continue; 2005 continue;
1914 /* Found opposite UNKNOWNS and they're next to each other */ 2006 /* Found opposite UNKNOWNS and they're next to each other */
1915 opp_dline_index = dline_index_from_dot(g, d, opp); 2007 opp_dline_index = dline_index_from_dot(g, d, opp);
@@ -1931,24 +2023,24 @@ static bool face_setall_identical(solver_state *sstate, int face_index,
1931 bool retval = false; 2023 bool retval = false;
1932 game_state *state = sstate->state; 2024 game_state *state = sstate->state;
1933 grid *g = state->game_grid; 2025 grid *g = state->game_grid;
1934 grid_face *f = g->faces + face_index; 2026 grid_face *f = g->faces[face_index];
1935 int N = f->order; 2027 int N = f->order;
1936 int i, j; 2028 int i, j;
1937 int can1, can2; 2029 int can1, can2;
1938 bool inv1, inv2; 2030 bool inv1, inv2;
1939 2031
1940 for (i = 0; i < N; i++) { 2032 for (i = 0; i < N; i++) {
1941 int line1_index = f->edges[i] - g->edges; 2033 int line1_index = f->edges[i]->index;
1942 if (state->lines[line1_index] != LINE_UNKNOWN) 2034 if (state->lines[line1_index] != LINE_UNKNOWN)
1943 continue; 2035 continue;
1944 for (j = i + 1; j < N; j++) { 2036 for (j = i + 1; j < N; j++) {
1945 int line2_index = f->edges[j] - g->edges; 2037 int line2_index = f->edges[j]->index;
1946 if (state->lines[line2_index] != LINE_UNKNOWN) 2038 if (state->lines[line2_index] != LINE_UNKNOWN)
1947 continue; 2039 continue;
1948 2040
1949 /* Found two UNKNOWNS */ 2041 /* Found two UNKNOWNS */
1950 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); 2042 can1 = dsf_canonify_flip(sstate->linedsf, line1_index, &inv1);
1951 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); 2043 can2 = dsf_canonify_flip(sstate->linedsf, line2_index, &inv2);
1952 if (can1 == can2 && inv1 == inv2) { 2044 if (can1 == can2 && inv1 == inv2) {
1953 solver_set_line(sstate, line1_index, line_new); 2045 solver_set_line(sstate, line1_index, line_new);
1954 solver_set_line(sstate, line2_index, line_new); 2046 solver_set_line(sstate, line2_index, line_new);
@@ -1966,9 +2058,8 @@ static void find_unknowns(game_state *state,
1966 int *e /* Returned edge indices */) 2058 int *e /* Returned edge indices */)
1967{ 2059{
1968 int c = 0; 2060 int c = 0;
1969 grid *g = state->game_grid;
1970 while (c < expected_count) { 2061 while (c < expected_count) {
1971 int line_index = *edge_list - g->edges; 2062 int line_index = (*edge_list)->index;
1972 if (state->lines[line_index] == LINE_UNKNOWN) { 2063 if (state->lines[line_index] == LINE_UNKNOWN) {
1973 e[c] = line_index; 2064 e[c] = line_index;
1974 c++; 2065 c++;
@@ -1989,7 +2080,7 @@ static int parity_deductions(solver_state *sstate,
1989{ 2080{
1990 game_state *state = sstate->state; 2081 game_state *state = sstate->state;
1991 int diff = DIFF_MAX; 2082 int diff = DIFF_MAX;
1992 int *linedsf = sstate->linedsf; 2083 DSF *linedsf = sstate->linedsf;
1993 2084
1994 if (unknown_count == 2) { 2085 if (unknown_count == 2) {
1995 /* Lines are known alike/opposite, depending on inv. */ 2086 /* Lines are known alike/opposite, depending on inv. */
@@ -2002,9 +2093,9 @@ static int parity_deductions(solver_state *sstate,
2002 int can[3]; /* canonical edges */ 2093 int can[3]; /* canonical edges */
2003 bool inv[3]; /* whether can[x] is inverse to e[x] */ 2094 bool inv[3]; /* whether can[x] is inverse to e[x] */
2004 find_unknowns(state, edge_list, 3, e); 2095 find_unknowns(state, edge_list, 3, e);
2005 can[0] = edsf_canonify(linedsf, e[0], inv); 2096 can[0] = dsf_canonify_flip(linedsf, e[0], inv);
2006 can[1] = edsf_canonify(linedsf, e[1], inv+1); 2097 can[1] = dsf_canonify_flip(linedsf, e[1], inv+1);
2007 can[2] = edsf_canonify(linedsf, e[2], inv+2); 2098 can[2] = dsf_canonify_flip(linedsf, e[2], inv+2);
2008 if (can[0] == can[1]) { 2099 if (can[0] == can[1]) {
2009 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ? 2100 if (solver_set_line(sstate, e[2], (total_parity^inv[0]^inv[1]) ?
2010 LINE_YES : LINE_NO)) 2101 LINE_YES : LINE_NO))
@@ -2025,10 +2116,10 @@ static int parity_deductions(solver_state *sstate,
2025 int can[4]; /* canonical edges */ 2116 int can[4]; /* canonical edges */
2026 bool inv[4]; /* whether can[x] is inverse to e[x] */ 2117 bool inv[4]; /* whether can[x] is inverse to e[x] */
2027 find_unknowns(state, edge_list, 4, e); 2118 find_unknowns(state, edge_list, 4, e);
2028 can[0] = edsf_canonify(linedsf, e[0], inv); 2119 can[0] = dsf_canonify_flip(linedsf, e[0], inv);
2029 can[1] = edsf_canonify(linedsf, e[1], inv+1); 2120 can[1] = dsf_canonify_flip(linedsf, e[1], inv+1);
2030 can[2] = edsf_canonify(linedsf, e[2], inv+2); 2121 can[2] = dsf_canonify_flip(linedsf, e[2], inv+2);
2031 can[3] = edsf_canonify(linedsf, e[3], inv+3); 2122 can[3] = dsf_canonify_flip(linedsf, e[3], inv+3);
2032 if (can[0] == can[1]) { 2123 if (can[0] == can[1]) {
2033 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1])) 2124 if (merge_lines(sstate, e[2], e[3], total_parity^inv[0]^inv[1]))
2034 diff = min(diff, DIFF_HARD); 2125 diff = min(diff, DIFF_HARD);
@@ -2097,7 +2188,7 @@ static int trivial_deductions(solver_state *sstate)
2097 2188
2098 /* Per-face deductions */ 2189 /* Per-face deductions */
2099 for (i = 0; i < g->num_faces; i++) { 2190 for (i = 0; i < g->num_faces; i++) {
2100 grid_face *f = g->faces + i; 2191 grid_face *f = g->faces[i];
2101 2192
2102 if (sstate->face_solved[i]) 2193 if (sstate->face_solved[i])
2103 continue; 2194 continue;
@@ -2155,22 +2246,22 @@ static int trivial_deductions(solver_state *sstate)
2155 int j, k, e1, e2, e, d; 2246 int j, k, e1, e2, e, d;
2156 2247
2157 for (j = 0; j < f->order; j++) { 2248 for (j = 0; j < f->order; j++) {
2158 e1 = f->edges[j] - g->edges; 2249 e1 = f->edges[j]->index;
2159 e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges; 2250 e2 = f->edges[j+1 < f->order ? j+1 : 0]->index;
2160 2251
2161 if (g->edges[e1].dot1 == g->edges[e2].dot1 || 2252 if (g->edges[e1]->dot1 == g->edges[e2]->dot1 ||
2162 g->edges[e1].dot1 == g->edges[e2].dot2) { 2253 g->edges[e1]->dot1 == g->edges[e2]->dot2) {
2163 d = g->edges[e1].dot1 - g->dots; 2254 d = g->edges[e1]->dot1->index;
2164 } else { 2255 } else {
2165 assert(g->edges[e1].dot2 == g->edges[e2].dot1 || 2256 assert(g->edges[e1]->dot2 == g->edges[e2]->dot1 ||
2166 g->edges[e1].dot2 == g->edges[e2].dot2); 2257 g->edges[e1]->dot2 == g->edges[e2]->dot2);
2167 d = g->edges[e1].dot2 - g->dots; 2258 d = g->edges[e1]->dot2->index;
2168 } 2259 }
2169 2260
2170 if (state->lines[e1] == LINE_UNKNOWN && 2261 if (state->lines[e1] == LINE_UNKNOWN &&
2171 state->lines[e2] == LINE_UNKNOWN) { 2262 state->lines[e2] == LINE_UNKNOWN) {
2172 for (k = 0; k < g->dots[d].order; k++) { 2263 for (k = 0; k < g->dots[d]->order; k++) {
2173 int e = g->dots[d].edges[k] - g->edges; 2264 int e = g->dots[d]->edges[k]->index;
2174 if (state->lines[e] == LINE_YES) 2265 if (state->lines[e] == LINE_YES)
2175 goto found; /* multi-level break */ 2266 goto found; /* multi-level break */
2176 } 2267 }
@@ -2184,7 +2275,7 @@ static int trivial_deductions(solver_state *sstate)
2184 * they're e1 and e2. 2275 * they're e1 and e2.
2185 */ 2276 */
2186 for (j = 0; j < f->order; j++) { 2277 for (j = 0; j < f->order; j++) {
2187 e = f->edges[j] - g->edges; 2278 e = f->edges[j]->index;
2188 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) { 2279 if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) {
2189 bool r = solver_set_line(sstate, e, LINE_YES); 2280 bool r = solver_set_line(sstate, e, LINE_YES);
2190 assert(r); 2281 assert(r);
@@ -2198,7 +2289,7 @@ static int trivial_deductions(solver_state *sstate)
2198 2289
2199 /* Per-dot deductions */ 2290 /* Per-dot deductions */
2200 for (i = 0; i < g->num_dots; i++) { 2291 for (i = 0; i < g->num_dots; i++) {
2201 grid_dot *d = g->dots + i; 2292 grid_dot *d = g->dots[i];
2202 int yes, no, unknown; 2293 int yes, no, unknown;
2203 2294
2204 if (sstate->dot_solved[i]) 2295 if (sstate->dot_solved[i])
@@ -2290,12 +2381,12 @@ static int dline_deductions(solver_state *sstate)
2290 * on that. We check this with an assertion, in case someone decides to 2381 * on that. We check this with an assertion, in case someone decides to
2291 * make a grid which has larger faces than this. Note, this algorithm 2382 * make a grid which has larger faces than this. Note, this algorithm
2292 * could get quite expensive if there are many large faces. */ 2383 * could get quite expensive if there are many large faces. */
2293#define MAX_FACE_SIZE 12 2384#define MAX_FACE_SIZE 14
2294 2385
2295 for (i = 0; i < g->num_faces; i++) { 2386 for (i = 0; i < g->num_faces; i++) {
2296 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE]; 2387 int maxs[MAX_FACE_SIZE][MAX_FACE_SIZE];
2297 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE]; 2388 int mins[MAX_FACE_SIZE][MAX_FACE_SIZE];
2298 grid_face *f = g->faces + i; 2389 grid_face *f = g->faces[i];
2299 int N = f->order; 2390 int N = f->order;
2300 int j,m; 2391 int j,m;
2301 int clue = state->clues[i]; 2392 int clue = state->clues[i];
@@ -2306,7 +2397,7 @@ static int dline_deductions(solver_state *sstate)
2306 2397
2307 /* Calculate the (j,j+1) entries */ 2398 /* Calculate the (j,j+1) entries */
2308 for (j = 0; j < N; j++) { 2399 for (j = 0; j < N; j++) {
2309 int edge_index = f->edges[j] - g->edges; 2400 int edge_index = f->edges[j]->index;
2310 int dline_index; 2401 int dline_index;
2311 enum line_state line1 = state->lines[edge_index]; 2402 enum line_state line1 = state->lines[edge_index];
2312 enum line_state line2; 2403 enum line_state line2;
@@ -2317,7 +2408,7 @@ static int dline_deductions(solver_state *sstate)
2317 mins[j][k] = (line1 == LINE_YES) ? 1 : 0; 2408 mins[j][k] = (line1 == LINE_YES) ? 1 : 0;
2318 /* Calculate the (j,j+2) entries */ 2409 /* Calculate the (j,j+2) entries */
2319 dline_index = dline_index_from_face(g, f, k); 2410 dline_index = dline_index_from_face(g, f, k);
2320 edge_index = f->edges[k] - g->edges; 2411 edge_index = f->edges[k]->index;
2321 line2 = state->lines[edge_index]; 2412 line2 = state->lines[edge_index];
2322 k++; 2413 k++;
2323 if (k >= N) k = 0; 2414 if (k >= N) k = 0;
@@ -2362,7 +2453,7 @@ static int dline_deductions(solver_state *sstate)
2362 for (j = 0; j < N; j++) { 2453 for (j = 0; j < N; j++) {
2363 int k; 2454 int k;
2364 grid_edge *e = f->edges[j]; 2455 grid_edge *e = f->edges[j];
2365 int line_index = e - g->edges; 2456 int line_index = e->index;
2366 int dline_index; 2457 int dline_index;
2367 2458
2368 if (state->lines[line_index] != LINE_UNKNOWN) 2459 if (state->lines[line_index] != LINE_UNKNOWN)
@@ -2397,7 +2488,7 @@ static int dline_deductions(solver_state *sstate)
2397 if (sstate->diff >= DIFF_TRICKY) { 2488 if (sstate->diff >= DIFF_TRICKY) {
2398 /* Now see if we can make dline deduction for edges{j,j+1} */ 2489 /* Now see if we can make dline deduction for edges{j,j+1} */
2399 e = f->edges[k]; 2490 e = f->edges[k];
2400 if (state->lines[e - g->edges] != LINE_UNKNOWN) 2491 if (state->lines[e->index] != LINE_UNKNOWN)
2401 /* Only worth doing this for an UNKNOWN,UNKNOWN pair. 2492 /* Only worth doing this for an UNKNOWN,UNKNOWN pair.
2402 * Dlines where one of the edges is known, are handled in the 2493 * Dlines where one of the edges is known, are handled in the
2403 * dot-deductions */ 2494 * dot-deductions */
@@ -2429,7 +2520,7 @@ static int dline_deductions(solver_state *sstate)
2429 /* ------ Dot deductions ------ */ 2520 /* ------ Dot deductions ------ */
2430 2521
2431 for (i = 0; i < g->num_dots; i++) { 2522 for (i = 0; i < g->num_dots; i++) {
2432 grid_dot *d = g->dots + i; 2523 grid_dot *d = g->dots[i];
2433 int N = d->order; 2524 int N = d->order;
2434 int yes, no, unknown; 2525 int yes, no, unknown;
2435 int j; 2526 int j;
@@ -2447,8 +2538,8 @@ static int dline_deductions(solver_state *sstate)
2447 k = j + 1; 2538 k = j + 1;
2448 if (k >= N) k = 0; 2539 if (k >= N) k = 0;
2449 dline_index = dline_index_from_dot(g, d, j); 2540 dline_index = dline_index_from_dot(g, d, j);
2450 line1_index = d->edges[j] - g->edges; 2541 line1_index = d->edges[j]->index;
2451 line2_index = d->edges[k] - g->edges; 2542 line2_index = d->edges[k] ->index;
2452 line1 = state->lines[line1_index]; 2543 line1 = state->lines[line1_index];
2453 line2 = state->lines[line2_index]; 2544 line2 = state->lines[line2_index];
2454 2545
@@ -2545,7 +2636,7 @@ static int dline_deductions(solver_state *sstate)
2545 int opp_index; 2636 int opp_index;
2546 if (opp == j || opp == k) 2637 if (opp == j || opp == k)
2547 continue; 2638 continue;
2548 opp_index = d->edges[opp] - g->edges; 2639 opp_index = d->edges[opp]->index;
2549 if (state->lines[opp_index] == LINE_UNKNOWN) { 2640 if (state->lines[opp_index] == LINE_UNKNOWN) {
2550 solver_set_line(sstate, opp_index, 2641 solver_set_line(sstate, opp_index,
2551 LINE_YES); 2642 LINE_YES);
@@ -2596,7 +2687,7 @@ static int linedsf_deductions(solver_state *sstate)
2596 if (clue < 0) 2687 if (clue < 0)
2597 continue; 2688 continue;
2598 2689
2599 N = g->faces[i].order; 2690 N = g->faces[i]->order;
2600 yes = sstate->face_yes_count[i]; 2691 yes = sstate->face_yes_count[i];
2601 if (yes + 1 == clue) { 2692 if (yes + 1 == clue) {
2602 if (face_setall_identical(sstate, i, LINE_NO)) 2693 if (face_setall_identical(sstate, i, LINE_NO))
@@ -2614,14 +2705,14 @@ static int linedsf_deductions(solver_state *sstate)
2614 2705
2615 /* Deductions with small number of LINE_UNKNOWNs, based on overall 2706 /* Deductions with small number of LINE_UNKNOWNs, based on overall
2616 * parity of lines. */ 2707 * parity of lines. */
2617 diff_tmp = parity_deductions(sstate, g->faces[i].edges, 2708 diff_tmp = parity_deductions(sstate, g->faces[i]->edges,
2618 (clue - yes) % 2, unknown); 2709 (clue - yes) % 2, unknown);
2619 diff = min(diff, diff_tmp); 2710 diff = min(diff, diff_tmp);
2620 } 2711 }
2621 2712
2622 /* ------ Dot deductions ------ */ 2713 /* ------ Dot deductions ------ */
2623 for (i = 0; i < g->num_dots; i++) { 2714 for (i = 0; i < g->num_dots; i++) {
2624 grid_dot *d = g->dots + i; 2715 grid_dot *d = g->dots[i];
2625 int N = d->order; 2716 int N = d->order;
2626 int j; 2717 int j;
2627 int yes, no, unknown; 2718 int yes, no, unknown;
@@ -2634,17 +2725,17 @@ static int linedsf_deductions(solver_state *sstate)
2634 int can1, can2; 2725 int can1, can2;
2635 bool inv1, inv2; 2726 bool inv1, inv2;
2636 int j2; 2727 int j2;
2637 line1_index = d->edges[j] - g->edges; 2728 line1_index = d->edges[j]->index;
2638 if (state->lines[line1_index] != LINE_UNKNOWN) 2729 if (state->lines[line1_index] != LINE_UNKNOWN)
2639 continue; 2730 continue;
2640 j2 = j + 1; 2731 j2 = j + 1;
2641 if (j2 == N) j2 = 0; 2732 if (j2 == N) j2 = 0;
2642 line2_index = d->edges[j2] - g->edges; 2733 line2_index = d->edges[j2]->index;
2643 if (state->lines[line2_index] != LINE_UNKNOWN) 2734 if (state->lines[line2_index] != LINE_UNKNOWN)
2644 continue; 2735 continue;
2645 /* Infer dline flags from linedsf */ 2736 /* Infer dline flags from linedsf */
2646 can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1); 2737 can1 = dsf_canonify_flip(sstate->linedsf, line1_index, &inv1);
2647 can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2); 2738 can2 = dsf_canonify_flip(sstate->linedsf, line2_index, &inv2);
2648 if (can1 == can2 && inv1 != inv2) { 2739 if (can1 == can2 && inv1 != inv2) {
2649 /* These are opposites, so set dline atmostone/atleastone */ 2740 /* These are opposites, so set dline atmostone/atleastone */
2650 if (set_atmostone(dlines, dline_index)) 2741 if (set_atmostone(dlines, dline_index))
@@ -2679,7 +2770,7 @@ static int linedsf_deductions(solver_state *sstate)
2679 int can; 2770 int can;
2680 bool inv; 2771 bool inv;
2681 enum line_state s; 2772 enum line_state s;
2682 can = edsf_canonify(sstate->linedsf, i, &inv); 2773 can = dsf_canonify_flip(sstate->linedsf, i, &inv);
2683 if (can == i) 2774 if (can == i)
2684 continue; 2775 continue;
2685 s = sstate->state->lines[can]; 2776 s = sstate->state->lines[can];
@@ -2704,7 +2795,6 @@ static int loop_deductions(solver_state *sstate)
2704 game_state *state = sstate->state; 2795 game_state *state = sstate->state;
2705 grid *g = state->game_grid; 2796 grid *g = state->game_grid;
2706 int shortest_chainlen = g->num_dots; 2797 int shortest_chainlen = g->num_dots;
2707 bool loop_found = false;
2708 int dots_connected; 2798 int dots_connected;
2709 bool progress = false; 2799 bool progress = false;
2710 int i; 2800 int i;
@@ -2717,7 +2807,7 @@ static int loop_deductions(solver_state *sstate)
2717 */ 2807 */
2718 for (i = 0; i < g->num_edges; i++) { 2808 for (i = 0; i < g->num_edges; i++) {
2719 if (state->lines[i] == LINE_YES) { 2809 if (state->lines[i] == LINE_YES) {
2720 loop_found |= merge_dots(sstate, i); 2810 merge_dots(sstate, i);
2721 edgecount++; 2811 edgecount++;
2722 } 2812 }
2723 } 2813 }
@@ -2762,9 +2852,9 @@ static int loop_deductions(solver_state *sstate)
2762 * loop it would create is a solution. 2852 * loop it would create is a solution.
2763 */ 2853 */
2764 for (i = 0; i < g->num_edges; i++) { 2854 for (i = 0; i < g->num_edges; i++) {
2765 grid_edge *e = g->edges + i; 2855 grid_edge *e = g->edges[i];
2766 int d1 = e->dot1 - g->dots; 2856 int d1 = e->dot1->index;
2767 int d2 = e->dot2 - g->dots; 2857 int d2 = e->dot2->index;
2768 int eqclass, val; 2858 int eqclass, val;
2769 if (state->lines[i] != LINE_UNKNOWN) 2859 if (state->lines[i] != LINE_UNKNOWN)
2770 continue; 2860 continue;
@@ -2801,13 +2891,13 @@ static int loop_deductions(solver_state *sstate)
2801 */ 2891 */
2802 sm1_nearby = 0; 2892 sm1_nearby = 0;
2803 if (e->face1) { 2893 if (e->face1) {
2804 int f = e->face1 - g->faces; 2894 int f = e->face1->index;
2805 int c = state->clues[f]; 2895 int c = state->clues[f];
2806 if (c >= 0 && sstate->face_yes_count[f] == c - 1) 2896 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2807 sm1_nearby++; 2897 sm1_nearby++;
2808 } 2898 }
2809 if (e->face2) { 2899 if (e->face2) {
2810 int f = e->face2 - g->faces; 2900 int f = e->face2->index;
2811 int c = state->clues[f]; 2901 int c = state->clues[f];
2812 if (c >= 0 && sstate->face_yes_count[f] == c - 1) 2902 if (c >= 0 && sstate->face_yes_count[f] == c - 1)
2813 sm1_nearby++; 2903 sm1_nearby++;
@@ -2958,7 +3048,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2958 char button_char = ' '; 3048 char button_char = ' ';
2959 enum line_state old_state; 3049 enum line_state old_state;
2960 3050
2961 button &= ~MOD_MASK; 3051 button = STRIP_BUTTON_MODIFIERS(button);
2962 3052
2963 /* Convert mouse-click (x,y) to grid coordinates */ 3053 /* Convert mouse-click (x,y) to grid coordinates */
2964 x -= BORDER(ds->tilesize); 3054 x -= BORDER(ds->tilesize);
@@ -2972,7 +3062,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2972 if (e == NULL) 3062 if (e == NULL)
2973 return NULL; 3063 return NULL;
2974 3064
2975 i = e - g->edges; 3065 i = e->index;
2976 3066
2977 /* I think it's only possible to play this game with mouse clicks, sorry */ 3067 /* I think it's only possible to play this game with mouse clicks, sorry */
2978 /* Maybe will add mouse drag support some time */ 3068 /* Maybe will add mouse drag support some time */
@@ -3020,73 +3110,58 @@ static char *interpret_move(const game_state *state, game_ui *ui,
3020 movesize = 80; 3110 movesize = 80;
3021 movebuf = snewn(movesize, char); 3111 movebuf = snewn(movesize, char);
3022 movelen = sprintf(movebuf, "%d%c", i, (int)button_char); 3112 movelen = sprintf(movebuf, "%d%c", i, (int)button_char);
3023 {
3024 static enum { OFF, FIXED, ADAPTIVE, DUNNO } autofollow = DUNNO;
3025 if (autofollow == DUNNO) {
3026 const char *env = getenv("LOOPY_AUTOFOLLOW");
3027 if (env && !strcmp(env, "off"))
3028 autofollow = OFF;
3029 else if (env && !strcmp(env, "fixed"))
3030 autofollow = FIXED;
3031 else if (env && !strcmp(env, "adaptive"))
3032 autofollow = ADAPTIVE;
3033 else
3034 autofollow = OFF;
3035 }
3036 3113
3037 if (autofollow != OFF) { 3114 if (ui->autofollow != AF_OFF) {
3038 int dotid; 3115 int dotid;
3039 for (dotid = 0; dotid < 2; dotid++) { 3116 for (dotid = 0; dotid < 2; dotid++) {
3040 grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2); 3117 grid_dot *dot = (dotid == 0 ? e->dot1 : e->dot2);
3041 grid_edge *e_this = e; 3118 grid_edge *e_this = e;
3042 3119
3043 while (1) { 3120 while (1) {
3044 int j, n_found; 3121 int j, n_found;
3045 grid_edge *e_next = NULL; 3122 grid_edge *e_next = NULL;
3046 3123
3047 for (j = n_found = 0; j < dot->order; j++) { 3124 for (j = n_found = 0; j < dot->order; j++) {
3048 grid_edge *e_candidate = dot->edges[j]; 3125 grid_edge *e_candidate = dot->edges[j];
3049 int i_candidate = e_candidate - g->edges; 3126 int i_candidate = e_candidate->index;
3050 if (e_candidate != e_this && 3127 if (e_candidate != e_this &&
3051 (autofollow == FIXED || 3128 (ui->autofollow == AF_FIXED ||
3052 state->lines[i] == LINE_NO || 3129 state->lines[i] == LINE_NO ||
3053 state->lines[i_candidate] != LINE_NO)) { 3130 state->lines[i_candidate] != LINE_NO)) {
3054 e_next = e_candidate; 3131 e_next = e_candidate;
3055 n_found++; 3132 n_found++;
3056 }
3057 } 3133 }
3134 }
3058 3135
3059 if (n_found != 1 || 3136 if (n_found != 1 ||
3060 state->lines[e_next - g->edges] != state->lines[i]) 3137 state->lines[e_next->index] != state->lines[i])
3061 break; 3138 break;
3062 3139
3063 if (e_next == e) { 3140 if (e_next == e) {
3064 /* 3141 /*
3065 * Special case: we might have come all the 3142 * Special case: we might have come all the way
3066 * way round a loop and found our way back to 3143 * round a loop and found our way back to the same
3067 * the same edge we started from. In that 3144 * edge we started from. In that situation, we
3068 * situation, we must terminate not only this 3145 * must terminate not only this while loop, but
3069 * while loop, but the 'for' outside it that 3146 * the 'for' outside it that was tracing in both
3070 * was tracing in both directions from the 3147 * directions from the starting edge, because if
3071 * starting edge, because if we let it trace 3148 * we let it trace in the second direction then
3072 * in the second direction then we'll only 3149 * we'll only find ourself traversing the same
3073 * find ourself traversing the same loop in 3150 * loop in the other order and generate an encoded
3074 * the other order and generate an encoded 3151 * move string that mentions the same set of edges
3075 * move string that mentions the same set of 3152 * twice.
3076 * edges twice. 3153 */
3077 */ 3154 goto autofollow_done;
3078 goto autofollow_done; 3155 }
3079 }
3080 3156
3081 dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2); 3157 dot = (e_next->dot1 != dot ? e_next->dot1 : e_next->dot2);
3082 if (movelen > movesize - 40) { 3158 if (movelen > movesize - 40) {
3083 movesize = movesize * 5 / 4 + 128; 3159 movesize = movesize * 5 / 4 + 128;
3084 movebuf = sresize(movebuf, movesize, char); 3160 movebuf = sresize(movebuf, movesize, char);
3085 }
3086 e_this = e_next;
3087 movelen += sprintf(movebuf+movelen, "%d%c",
3088 (int)(e_this - g->edges), button_char);
3089 } 3161 }
3162 e_this = e_next;
3163 movelen += sprintf(movebuf+movelen, "%d%c",
3164 (int)(e_this->index), button_char);
3090 } 3165 }
3091 autofollow_done:; 3166 autofollow_done:;
3092 } 3167 }
@@ -3159,7 +3234,7 @@ static void grid_to_screen(const game_drawstate *ds, const grid *g,
3159static void face_text_pos(const game_drawstate *ds, const grid *g, 3234static void face_text_pos(const game_drawstate *ds, const grid *g,
3160 grid_face *f, int *xret, int *yret) 3235 grid_face *f, int *xret, int *yret)
3161{ 3236{
3162 int faceindex = f - g->faces; 3237 int faceindex = f->index;
3163 3238
3164 /* 3239 /*
3165 * Return the cached position for this face, if we've already 3240 * Return the cached position for this face, if we've already
@@ -3192,9 +3267,9 @@ static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
3192 /* There seems to be a certain amount of trial-and-error involved 3267 /* There seems to be a certain amount of trial-and-error involved
3193 * in working out the correct bounding-box for the text. */ 3268 * in working out the correct bounding-box for the text. */
3194 3269
3195 *x = xx - ds->tilesize/4 - 1; 3270 *x = xx - ds->tilesize * 5 / 4 - 1;
3196 *y = yy - ds->tilesize/4 - 3; 3271 *y = yy - ds->tilesize/4 - 3;
3197 *w = ds->tilesize/2 + 2; 3272 *w = ds->tilesize * 5 / 2 + 2;
3198 *h = ds->tilesize/2 + 5; 3273 *h = ds->tilesize/2 + 5;
3199} 3274}
3200 3275
@@ -3202,7 +3277,7 @@ static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3202 const game_state *state, int i) 3277 const game_state *state, int i)
3203{ 3278{
3204 grid *g = state->game_grid; 3279 grid *g = state->game_grid;
3205 grid_face *f = g->faces + i; 3280 grid_face *f = g->faces[i];
3206 int x, y; 3281 int x, y;
3207 char c[20]; 3282 char c[20];
3208 3283
@@ -3228,10 +3303,10 @@ static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
3228 grid_to_screen(ds, g, x1, y1, &x1, &y1); 3303 grid_to_screen(ds, g, x1, y1, &x1, &y1);
3229 grid_to_screen(ds, g, x2, y2, &x2, &y2); 3304 grid_to_screen(ds, g, x2, y2, &x2, &y2);
3230 /* Allow extra margin for dots, and thickness of lines */ 3305 /* Allow extra margin for dots, and thickness of lines */
3231 xmin = min(x1, x2) - 2; 3306 xmin = min(x1, x2) - (ds->tilesize + 15) / 16;
3232 xmax = max(x1, x2) + 2; 3307 xmax = max(x1, x2) + (ds->tilesize + 15) / 16;
3233 ymin = min(y1, y2) - 2; 3308 ymin = min(y1, y2) - (ds->tilesize + 15) / 16;
3234 ymax = max(y1, y2) + 2; 3309 ymax = max(y1, y2) + (ds->tilesize + 15) / 16;
3235 3310
3236 *x = xmin; 3311 *x = xmin;
3237 *y = ymin; 3312 *y = ymin;
@@ -3243,13 +3318,19 @@ static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
3243 int *x, int *y, int *w, int *h) 3318 int *x, int *y, int *w, int *h)
3244{ 3319{
3245 int x1, y1; 3320 int x1, y1;
3321 int xmin, xmax, ymin, ymax;
3246 3322
3247 grid_to_screen(ds, g, d->x, d->y, &x1, &y1); 3323 grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
3248 3324
3249 *x = x1 - 2; 3325 xmin = x1 - (ds->tilesize * 5 + 63) / 64;
3250 *y = y1 - 2; 3326 xmax = x1 + (ds->tilesize * 5 + 63) / 64;
3251 *w = 5; 3327 ymin = y1 - (ds->tilesize * 5 + 63) / 64;
3252 *h = 5; 3328 ymax = y1 + (ds->tilesize * 5 + 63) / 64;
3329
3330 *x = xmin;
3331 *y = ymin;
3332 *w = xmax - xmin + 1;
3333 *h = ymax - ymin + 1;
3253} 3334}
3254 3335
3255static const int loopy_line_redraw_phases[] = { 3336static const int loopy_line_redraw_phases[] = {
@@ -3257,11 +3338,11 @@ static const int loopy_line_redraw_phases[] = {
3257}; 3338};
3258#define NPHASES lenof(loopy_line_redraw_phases) 3339#define NPHASES lenof(loopy_line_redraw_phases)
3259 3340
3260static void game_redraw_line(drawing *dr, game_drawstate *ds, 3341static void game_redraw_line(drawing *dr, game_drawstate *ds,const game_ui *ui,
3261 const game_state *state, int i, int phase) 3342 const game_state *state, int i, int phase)
3262{ 3343{
3263 grid *g = state->game_grid; 3344 grid *g = state->game_grid;
3264 grid_edge *e = g->edges + i; 3345 grid_edge *e = g->edges[i];
3265 int x1, x2, y1, y2; 3346 int x1, x2, y1, y2;
3266 int line_colour; 3347 int line_colour;
3267 3348
@@ -3283,16 +3364,13 @@ static void game_redraw_line(drawing *dr, game_drawstate *ds,
3283 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); 3364 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3284 3365
3285 if (line_colour == COL_FAINT) { 3366 if (line_colour == COL_FAINT) {
3286 static int draw_faint_lines = -1; 3367 if (ui->draw_faint_lines)
3287 if (draw_faint_lines < 0) { 3368 draw_thick_line(dr, ds->tilesize/24.0,
3288 char *env = getenv("LOOPY_FAINT_LINES"); 3369 x1 + 0.5, y1 + 0.5,
3289 draw_faint_lines = (!env || (env[0] == 'y' || 3370 x2 + 0.5, y2 + 0.5,
3290 env[0] == 'Y')); 3371 line_colour);
3291 }
3292 if (draw_faint_lines)
3293 draw_line(dr, x1, y1, x2, y2, line_colour);
3294 } else { 3372 } else {
3295 draw_thick_line(dr, 3.0, 3373 draw_thick_line(dr, ds->tilesize*3/32.0,
3296 x1 + 0.5, y1 + 0.5, 3374 x1 + 0.5, y1 + 0.5,
3297 x2 + 0.5, y2 + 0.5, 3375 x2 + 0.5, y2 + 0.5,
3298 line_colour); 3376 line_colour);
@@ -3303,11 +3381,11 @@ static void game_redraw_dot(drawing *dr, game_drawstate *ds,
3303 const game_state *state, int i) 3381 const game_state *state, int i)
3304{ 3382{
3305 grid *g = state->game_grid; 3383 grid *g = state->game_grid;
3306 grid_dot *d = g->dots + i; 3384 grid_dot *d = g->dots[i];
3307 int x, y; 3385 int x, y;
3308 3386
3309 grid_to_screen(ds, g, d->x, d->y, &x, &y); 3387 grid_to_screen(ds, g, d->x, d->y, &x, &y);
3310 draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND); 3388 draw_circle(dr, x, y, ds->tilesize*2.5/32.0, COL_FOREGROUND, COL_FOREGROUND);
3311} 3389}
3312 3390
3313static bool boxes_intersect(int x0, int y0, int w0, int h0, 3391static bool boxes_intersect(int x0, int y0, int w0, int h0,
@@ -3322,7 +3400,7 @@ static bool boxes_intersect(int x0, int y0, int w0, int h0,
3322} 3400}
3323 3401
3324static void game_redraw_in_rect(drawing *dr, game_drawstate *ds, 3402static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3325 const game_state *state, 3403 const game_ui *ui, const game_state *state,
3326 int x, int y, int w, int h) 3404 int x, int y, int w, int h)
3327{ 3405{
3328 grid *g = state->game_grid; 3406 grid *g = state->game_grid;
@@ -3334,20 +3412,20 @@ static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3334 3412
3335 for (i = 0; i < g->num_faces; i++) { 3413 for (i = 0; i < g->num_faces; i++) {
3336 if (state->clues[i] >= 0) { 3414 if (state->clues[i] >= 0) {
3337 face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh); 3415 face_text_bbox(ds, g, g->faces[i], &bx, &by, &bw, &bh);
3338 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3416 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3339 game_redraw_clue(dr, ds, state, i); 3417 game_redraw_clue(dr, ds, state, i);
3340 } 3418 }
3341 } 3419 }
3342 for (phase = 0; phase < NPHASES; phase++) { 3420 for (phase = 0; phase < NPHASES; phase++) {
3343 for (i = 0; i < g->num_edges; i++) { 3421 for (i = 0; i < g->num_edges; i++) {
3344 edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh); 3422 edge_bbox(ds, g, g->edges[i], &bx, &by, &bw, &bh);
3345 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3423 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3346 game_redraw_line(dr, ds, state, i, phase); 3424 game_redraw_line(dr, ds, ui, state, i, phase);
3347 } 3425 }
3348 } 3426 }
3349 for (i = 0; i < g->num_dots; i++) { 3427 for (i = 0; i < g->num_dots; i++) {
3350 dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh); 3428 dot_bbox(ds, g, g->dots[i], &bx, &by, &bw, &bh);
3351 if (boxes_intersect(x, y, w, h, bx, by, bw, bh)) 3429 if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3352 game_redraw_dot(dr, ds, state, i); 3430 game_redraw_dot(dr, ds, state, i);
3353 } 3431 }
@@ -3407,7 +3485,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3407 3485
3408 /* First, trundle through the faces. */ 3486 /* First, trundle through the faces. */
3409 for (i = 0; i < g->num_faces; i++) { 3487 for (i = 0; i < g->num_faces; i++) {
3410 grid_face *f = g->faces + i; 3488 grid_face *f = g->faces[i];
3411 int sides = f->order; 3489 int sides = f->order;
3412 int yes_order, no_order; 3490 int yes_order, no_order;
3413 bool clue_mistake; 3491 bool clue_mistake;
@@ -3500,26 +3578,26 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3500 int w = grid_width * ds->tilesize / g->tilesize; 3578 int w = grid_width * ds->tilesize / g->tilesize;
3501 int h = grid_height * ds->tilesize / g->tilesize; 3579 int h = grid_height * ds->tilesize / g->tilesize;
3502 3580
3503 game_redraw_in_rect(dr, ds, state, 3581 game_redraw_in_rect(dr, ds, ui, state,
3504 0, 0, w + 2*border + 1, h + 2*border + 1); 3582 0, 0, w + 2*border + 1, h + 2*border + 1);
3505 } else { 3583 } else {
3506 3584
3507 /* Right. Now we roll up our sleeves. */ 3585 /* Right. Now we roll up our sleeves. */
3508 3586
3509 for (i = 0; i < nfaces; i++) { 3587 for (i = 0; i < nfaces; i++) {
3510 grid_face *f = g->faces + faces[i]; 3588 grid_face *f = g->faces[faces[i]];
3511 int x, y, w, h; 3589 int x, y, w, h;
3512 3590
3513 face_text_bbox(ds, g, f, &x, &y, &w, &h); 3591 face_text_bbox(ds, g, f, &x, &y, &w, &h);
3514 game_redraw_in_rect(dr, ds, state, x, y, w, h); 3592 game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
3515 } 3593 }
3516 3594
3517 for (i = 0; i < nedges; i++) { 3595 for (i = 0; i < nedges; i++) {
3518 grid_edge *e = g->edges + edges[i]; 3596 grid_edge *e = g->edges[edges[i]];
3519 int x, y, w, h; 3597 int x, y, w, h;
3520 3598
3521 edge_bbox(ds, g, e, &x, &y, &w, &h); 3599 edge_bbox(ds, g, e, &x, &y, &w, &h);
3522 game_redraw_in_rect(dr, ds, state, x, y, w, h); 3600 game_redraw_in_rect(dr, ds, ui, state, x, y, w, h);
3523 } 3601 }
3524 } 3602 }
3525 3603
@@ -3550,19 +3628,21 @@ static int game_status(const game_state *state)
3550 return state->solved ? +1 : 0; 3628 return state->solved ? +1 : 0;
3551} 3629}
3552 3630
3553static void game_print_size(const game_params *params, float *x, float *y) 3631static void game_print_size(const game_params *params, const game_ui *ui,
3632 float *x, float *y)
3554{ 3633{
3555 int pw, ph; 3634 int pw, ph;
3556 3635
3557 /* 3636 /*
3558 * I'll use 7mm "squares" by default. 3637 * I'll use 7mm "squares" by default.
3559 */ 3638 */
3560 game_compute_size(params, 700, &pw, &ph); 3639 game_compute_size(params, 700, ui, &pw, &ph);
3561 *x = pw / 100.0F; 3640 *x = pw / 100.0F;
3562 *y = ph / 100.0F; 3641 *y = ph / 100.0F;
3563} 3642}
3564 3643
3565static void game_print(drawing *dr, const game_state *state, int tilesize) 3644static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3645 int tilesize)
3566{ 3646{
3567 int ink = print_mono_colour(dr, 0); 3647 int ink = print_mono_colour(dr, 0);
3568 int i; 3648 int i;
@@ -3577,7 +3657,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3577 3657
3578 for (i = 0; i < g->num_dots; i++) { 3658 for (i = 0; i < g->num_dots; i++) {
3579 int x, y; 3659 int x, y;
3580 grid_to_screen(ds, g, g->dots[i].x, g->dots[i].y, &x, &y); 3660 grid_to_screen(ds, g, g->dots[i]->x, g->dots[i]->y, &x, &y);
3581 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink); 3661 draw_circle(dr, x, y, ds->tilesize / 15, ink, ink);
3582 } 3662 }
3583 3663
@@ -3585,7 +3665,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3585 * Clues. 3665 * Clues.
3586 */ 3666 */
3587 for (i = 0; i < g->num_faces; i++) { 3667 for (i = 0; i < g->num_faces; i++) {
3588 grid_face *f = g->faces + i; 3668 grid_face *f = g->faces[i];
3589 int clue = state->clues[i]; 3669 int clue = state->clues[i];
3590 if (clue >= 0) { 3670 if (clue >= 0) {
3591 char c[20]; 3671 char c[20];
@@ -3603,7 +3683,7 @@ static void game_print(drawing *dr, const game_state *state, int tilesize)
3603 */ 3683 */
3604 for (i = 0; i < g->num_edges; i++) { 3684 for (i = 0; i < g->num_edges; i++) {
3605 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150; 3685 int thickness = (state->lines[i] == LINE_YES) ? 30 : 150;
3606 grid_edge *e = g->edges + i; 3686 grid_edge *e = g->edges[i];
3607 int x1, y1, x2, y2; 3687 int x1, y1, x2, y2;
3608 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1); 3688 grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3609 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2); 3689 grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
@@ -3666,14 +3746,16 @@ const struct game thegame = {
3666 new_game, 3746 new_game,
3667 dup_game, 3747 dup_game,
3668 free_game, 3748 free_game,
3669 1, solve_game, 3749 true, solve_game,
3670 true, game_can_format_as_text_now, game_text_format, 3750 true, game_can_format_as_text_now, game_text_format,
3751 get_prefs, set_prefs,
3671 new_ui, 3752 new_ui,
3672 free_ui, 3753 free_ui,
3673 encode_ui, 3754 NULL, /* encode_ui */
3674 decode_ui, 3755 NULL, /* decode_ui */
3675 NULL, /* game_request_keys */ 3756 NULL, /* game_request_keys */
3676 game_changed_state, 3757 game_changed_state,
3758 NULL, /* current_key_label */
3677 interpret_move, 3759 interpret_move,
3678 execute_move, 3760 execute_move,
3679 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3761 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3687,7 +3769,7 @@ const struct game thegame = {
3687 game_status, 3769 game_status,
3688 true, false, game_print_size, game_print, 3770 true, false, game_print_size, game_print,
3689 false /* wants_statusbar */, 3771 false /* wants_statusbar */,
3690 false, game_timing_state, 3772 false, NULL, /* timing_state */
3691 0, /* mouse_priorities */ 3773 0, /* mouse_priorities */
3692}; 3774};
3693 3775