diff options
Diffstat (limited to 'apps/plugins/puzzles/src/loopy.c')
-rw-r--r-- | apps/plugins/puzzles/src/loopy.c | 576 |
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) { | |||
420 | static void free_solver_state(solver_state *sstate) { | 427 | static 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 | ||
682 | static const char *validate_params(const game_params *params, bool full) | 693 | static 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 | ||
875 | struct 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 | |||
896 | static 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 | |||
853 | static game_ui *new_ui(const game_state *state) | 924 | static 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 | ||
858 | static void free_ui(game_ui *ui) | 933 | static void free_ui(game_ui *ui) |
859 | { | 934 | { |
935 | sfree(ui); | ||
860 | } | 936 | } |
861 | 937 | ||
862 | static char *encode_ui(const game_ui *ui) | 938 | static 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 | ||
867 | static void decode_ui(game_ui *ui, const char *encoding) | 963 | static 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 | ||
871 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 969 | static 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 | ||
876 | static void game_compute_size(const game_params *params, int tilesize, | 974 | static 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 | ||
977 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
978 | { | ||
979 | return true; | ||
980 | } | ||
981 | |||
982 | static float game_anim_length(const game_state *oldstate, | 1075 | static 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, | |||
3159 | static void face_text_pos(const game_drawstate *ds, const grid *g, | 3234 | static 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 | ||
3255 | static const int loopy_line_redraw_phases[] = { | 3336 | static 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 | ||
3260 | static void game_redraw_line(drawing *dr, game_drawstate *ds, | 3341 | static 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 | ||
3313 | static bool boxes_intersect(int x0, int y0, int w0, int h0, | 3391 | static 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 | ||
3324 | static void game_redraw_in_rect(drawing *dr, game_drawstate *ds, | 3402 | static 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 | ||
3553 | static void game_print_size(const game_params *params, float *x, float *y) | 3631 | static 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 | ||
3565 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 3644 | static 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 | ||