diff options
Diffstat (limited to 'apps/plugins/puzzles/src/filling.c')
-rw-r--r-- | apps/plugins/puzzles/src/filling.c | 198 |
1 files changed, 105 insertions, 93 deletions
diff --git a/apps/plugins/puzzles/src/filling.c b/apps/plugins/puzzles/src/filling.c index 6d9beb5c28..a0c144714b 100644 --- a/apps/plugins/puzzles/src/filling.c +++ b/apps/plugins/puzzles/src/filling.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* -*- tab-width: 8; indent-tabs-mode: t -*- | 1 | /* |
2 | * filling.c: An implementation of the Nikoli game fillomino. | 2 | * filling.c: An implementation of the Nikoli game fillomino. |
3 | * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license. | 3 | * Copyright (C) 2007 Jonas Kölker. See LICENSE for the license. |
4 | */ | 4 | */ |
@@ -58,7 +58,11 @@ | |||
58 | 58 | ||
59 | #include <assert.h> | 59 | #include <assert.h> |
60 | #include <ctype.h> | 60 | #include <ctype.h> |
61 | #include <math.h> | 61 | #ifdef NO_TGMATH_H |
62 | # include <math.h> | ||
63 | #else | ||
64 | # include <tgmath.h> | ||
65 | #endif | ||
62 | #include <stdarg.h> | 66 | #include <stdarg.h> |
63 | #include <stdio.h> | 67 | #include <stdio.h> |
64 | #include <stdlib.h> | 68 | #include <stdlib.h> |
@@ -68,16 +72,11 @@ | |||
68 | 72 | ||
69 | static bool verbose; | 73 | static bool verbose; |
70 | 74 | ||
71 | static void printv(const char *fmt, ...) { | 75 | #ifdef STANDALONE_SOLVER |
72 | #if !defined(PALM) && !defined(ROCKBOX) | 76 | #define printv if (!verbose); else printf |
73 | if (verbose) { | 77 | #else |
74 | va_list va; | 78 | #define printv(...) |
75 | va_start(va, fmt); | ||
76 | vprintf(fmt, va); | ||
77 | va_end(va); | ||
78 | } | ||
79 | #endif | 79 | #endif |
80 | } | ||
81 | 80 | ||
82 | /***************************************************************************** | 81 | /***************************************************************************** |
83 | * GAME CONFIGURATION AND PARAMETERS * | 82 | * GAME CONFIGURATION AND PARAMETERS * |
@@ -188,6 +187,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
188 | { | 187 | { |
189 | if (params->w < 1) return "Width must be at least one"; | 188 | if (params->w < 1) return "Width must be at least one"; |
190 | if (params->h < 1) return "Height must be at least one"; | 189 | if (params->h < 1) return "Height must be at least one"; |
190 | if (params->w > INT_MAX / params->h) | ||
191 | return "Width times height must not be unreasonably large"; | ||
191 | 192 | ||
192 | return NULL; | 193 | return NULL; |
193 | } | 194 | } |
@@ -289,14 +290,15 @@ static const int dy[4] = {0, 0, -1, 1}; | |||
289 | 290 | ||
290 | struct solver_state | 291 | struct solver_state |
291 | { | 292 | { |
292 | int *dsf; | 293 | DSF *dsf; |
293 | int *board; | 294 | int *board; |
294 | int *connected; | 295 | int *connected; |
295 | int nempty; | 296 | int nempty; |
296 | 297 | ||
297 | /* Used internally by learn_bitmap_deductions; kept here to avoid | 298 | /* Used internally by learn_bitmap_deductions; kept here to avoid |
298 | * mallocing/freeing them every time that function is called. */ | 299 | * mallocing/freeing them every time that function is called. */ |
299 | int *bm, *bmdsf, *bmminsize; | 300 | int *bm, *bmminsize; |
301 | DSF *bmdsf; | ||
300 | }; | 302 | }; |
301 | 303 | ||
302 | static void print_board(int *board, int w, int h) { | 304 | static void print_board(int *board, int w, int h) { |
@@ -310,7 +312,7 @@ static void print_board(int *board, int w, int h) { | |||
310 | static game_state *new_game(midend *, const game_params *, const char *); | 312 | static game_state *new_game(midend *, const game_params *, const char *); |
311 | static void free_game(game_state *); | 313 | static void free_game(game_state *); |
312 | 314 | ||
313 | #define SENTINEL sz | 315 | #define SENTINEL (sz+1) |
314 | 316 | ||
315 | static bool mark_region(int *board, int w, int h, int i, int n, int m) { | 317 | static bool mark_region(int *board, int w, int h, int i, int n, int m) { |
316 | int j; | 318 | int j; |
@@ -390,7 +392,8 @@ static void make_board(int *board, int w, int h, random_state *rs) { | |||
390 | /* Note that if 1 in {w, h} then it's impossible to have a region | 392 | /* Note that if 1 in {w, h} then it's impossible to have a region |
391 | * of size > w*h, so the special case only affects w=h=2. */ | 393 | * of size > w*h, so the special case only affects w=h=2. */ |
392 | 394 | ||
393 | int i, *dsf; | 395 | int i; |
396 | DSF *dsf; | ||
394 | bool change; | 397 | bool change; |
395 | 398 | ||
396 | assert(w >= 1); | 399 | assert(w >= 1); |
@@ -401,9 +404,9 @@ static void make_board(int *board, int w, int h, random_state *rs) { | |||
401 | * contains a shuffled list of numbers {0, ..., sz-1}. */ | 404 | * contains a shuffled list of numbers {0, ..., sz-1}. */ |
402 | for (i = 0; i < sz; ++i) board[i] = i; | 405 | for (i = 0; i < sz; ++i) board[i] = i; |
403 | 406 | ||
404 | dsf = snewn(sz, int); | 407 | dsf = dsf_new(sz); |
405 | retry: | 408 | retry: |
406 | dsf_init(dsf, sz); | 409 | dsf_reinit(dsf); |
407 | shuffle(board, sz, sizeof (int), rs); | 410 | shuffle(board, sz, sizeof (int), rs); |
408 | 411 | ||
409 | do { | 412 | do { |
@@ -414,10 +417,15 @@ retry: | |||
414 | int merge = SENTINEL, min = maxsize - size + 1; | 417 | int merge = SENTINEL, min = maxsize - size + 1; |
415 | bool error = false; | 418 | bool error = false; |
416 | int neighbour, neighbour_size, j; | 419 | int neighbour, neighbour_size, j; |
420 | int directions[4]; | ||
421 | |||
422 | for (j = 0; j < 4; ++j) | ||
423 | directions[j] = j; | ||
424 | shuffle(directions, 4, sizeof(int), rs); | ||
417 | 425 | ||
418 | for (j = 0; j < 4; ++j) { | 426 | for (j = 0; j < 4; ++j) { |
419 | const int x = (board[i] % w) + dx[j]; | 427 | const int x = (board[i] % w) + dx[directions[j]]; |
420 | const int y = (board[i] / w) + dy[j]; | 428 | const int y = (board[i] / w) + dy[directions[j]]; |
421 | if (x < 0 || x >= w || y < 0 || y >= h) continue; | 429 | if (x < 0 || x >= w || y < 0 || y >= h) continue; |
422 | 430 | ||
423 | neighbour = dsf_canonify(dsf, w*y + x); | 431 | neighbour = dsf_canonify(dsf, w*y + x); |
@@ -429,7 +437,7 @@ retry: | |||
429 | /* find the smallest neighbour to merge with, which | 437 | /* find the smallest neighbour to merge with, which |
430 | * wouldn't make the region too large. (This is | 438 | * wouldn't make the region too large. (This is |
431 | * guaranteed by the initial value of `min'.) */ | 439 | * guaranteed by the initial value of `min'.) */ |
432 | if (neighbour_size < min) { | 440 | if (neighbour_size < min && random_upto(rs, 10)) { |
433 | min = neighbour_size; | 441 | min = neighbour_size; |
434 | merge = neighbour; | 442 | merge = neighbour; |
435 | } | 443 | } |
@@ -453,10 +461,10 @@ retry: | |||
453 | for (i = 0; i < sz; ++i) board[i] = dsf_size(dsf, i); | 461 | for (i = 0; i < sz; ++i) board[i] = dsf_size(dsf, i); |
454 | merge_ones(board, w, h); | 462 | merge_ones(board, w, h); |
455 | 463 | ||
456 | sfree(dsf); | 464 | dsf_free(dsf); |
457 | } | 465 | } |
458 | 466 | ||
459 | static void merge(int *dsf, int *connected, int a, int b) { | 467 | static void merge(DSF *dsf, int *connected, int a, int b) { |
460 | int c; | 468 | int c; |
461 | assert(dsf); | 469 | assert(dsf); |
462 | assert(connected); | 470 | assert(connected); |
@@ -532,7 +540,7 @@ static bool check_capacity(int *board, int w, int h, int i) { | |||
532 | return n == 0; | 540 | return n == 0; |
533 | } | 541 | } |
534 | 542 | ||
535 | static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) { | 543 | static int expandsize(const int *board, DSF *dsf, int w, int h, int i, int n) { |
536 | int j; | 544 | int j; |
537 | int nhits = 0; | 545 | int nhits = 0; |
538 | int hits[4]; | 546 | int hits[4]; |
@@ -548,7 +556,7 @@ static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) { | |||
548 | root = dsf_canonify(dsf, idx); | 556 | root = dsf_canonify(dsf, idx); |
549 | for (m = 0; m < nhits && root != hits[m]; ++m); | 557 | for (m = 0; m < nhits && root != hits[m]; ++m); |
550 | if (m < nhits) continue; | 558 | if (m < nhits) continue; |
551 | printv("\t (%d, %d) contrib %d to size\n", x, y, dsf[root] >> 2); | 559 | printv("\t (%d, %d) contrib %d to size\n", x, y, dsf_size(dsf, root)); |
552 | size += dsf_size(dsf, root); | 560 | size += dsf_size(dsf, root); |
553 | assert(dsf_size(dsf, root) >= 1); | 561 | assert(dsf_size(dsf, root) >= 1); |
554 | hits[nhits++] = root; | 562 | hits[nhits++] = root; |
@@ -833,7 +841,7 @@ static bool learn_bitmap_deductions(struct solver_state *s, int w, int h) | |||
833 | { | 841 | { |
834 | const int sz = w * h; | 842 | const int sz = w * h; |
835 | int *bm = s->bm; | 843 | int *bm = s->bm; |
836 | int *dsf = s->bmdsf; | 844 | DSF *dsf = s->bmdsf; |
837 | int *minsize = s->bmminsize; | 845 | int *minsize = s->bmminsize; |
838 | int x, y, i, j, n; | 846 | int x, y, i, j, n; |
839 | bool learn = false; | 847 | bool learn = false; |
@@ -933,7 +941,7 @@ static bool learn_bitmap_deductions(struct solver_state *s, int w, int h) | |||
933 | * have a completely new n-region in it. | 941 | * have a completely new n-region in it. |
934 | */ | 942 | */ |
935 | for (n = 1; n <= 9; n++) { | 943 | for (n = 1; n <= 9; n++) { |
936 | dsf_init(dsf, sz); | 944 | dsf_reinit(dsf); |
937 | 945 | ||
938 | /* Build the dsf */ | 946 | /* Build the dsf */ |
939 | for (y = 0; y < h; y++) | 947 | for (y = 0; y < h; y++) |
@@ -1076,12 +1084,12 @@ static bool solver(const int *orig, int w, int h, char **solution) { | |||
1076 | 1084 | ||
1077 | struct solver_state ss; | 1085 | struct solver_state ss; |
1078 | ss.board = memdup(orig, sz, sizeof (int)); | 1086 | ss.board = memdup(orig, sz, sizeof (int)); |
1079 | ss.dsf = snew_dsf(sz); /* eqv classes: connected components */ | 1087 | ss.dsf = dsf_new(sz); /* eqv classes: connected components */ |
1080 | ss.connected = snewn(sz, int); /* connected[n] := n.next; */ | 1088 | ss.connected = snewn(sz, int); /* connected[n] := n.next; */ |
1081 | /* cyclic disjoint singly linked lists, same partitioning as dsf. | 1089 | /* cyclic disjoint singly linked lists, same partitioning as dsf. |
1082 | * The lists lets you iterate over a partition given any member */ | 1090 | * The lists lets you iterate over a partition given any member */ |
1083 | ss.bm = snewn(sz, int); | 1091 | ss.bm = snewn(sz, int); |
1084 | ss.bmdsf = snew_dsf(sz); | 1092 | ss.bmdsf = dsf_new(sz); |
1085 | ss.bmminsize = snewn(sz, int); | 1093 | ss.bmminsize = snewn(sz, int); |
1086 | 1094 | ||
1087 | printv("trying to solve this:\n"); | 1095 | printv("trying to solve this:\n"); |
@@ -1105,28 +1113,26 @@ static bool solver(const int *orig, int w, int h, char **solution) { | |||
1105 | **solution = 's'; | 1113 | **solution = 's'; |
1106 | for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0'; | 1114 | for (i = 0; i < sz; ++i) (*solution)[i + 1] = ss.board[i] + '0'; |
1107 | (*solution)[sz + 1] = '\0'; | 1115 | (*solution)[sz + 1] = '\0'; |
1108 | /* We don't need the \0 for execute_move (the only user) | ||
1109 | * I'm just being printf-friendly in case I wanna print */ | ||
1110 | } | 1116 | } |
1111 | 1117 | ||
1112 | sfree(ss.dsf); | 1118 | dsf_free(ss.dsf); |
1113 | sfree(ss.board); | 1119 | sfree(ss.board); |
1114 | sfree(ss.connected); | 1120 | sfree(ss.connected); |
1115 | sfree(ss.bm); | 1121 | sfree(ss.bm); |
1116 | sfree(ss.bmdsf); | 1122 | dsf_free(ss.bmdsf); |
1117 | sfree(ss.bmminsize); | 1123 | sfree(ss.bmminsize); |
1118 | 1124 | ||
1119 | return !ss.nempty; | 1125 | return !ss.nempty; |
1120 | } | 1126 | } |
1121 | 1127 | ||
1122 | static int *make_dsf(int *dsf, int *board, const int w, const int h) { | 1128 | static DSF *make_dsf(DSF *dsf, int *board, const int w, const int h) { |
1123 | const int sz = w * h; | 1129 | const int sz = w * h; |
1124 | int i; | 1130 | int i; |
1125 | 1131 | ||
1126 | if (!dsf) | 1132 | if (!dsf) |
1127 | dsf = snew_dsf(w * h); | 1133 | dsf = dsf_new_min(w * h); |
1128 | else | 1134 | else |
1129 | dsf_init(dsf, w * h); | 1135 | dsf_reinit(dsf); |
1130 | 1136 | ||
1131 | for (i = 0; i < sz; ++i) { | 1137 | for (i = 0; i < sz; ++i) { |
1132 | int j; | 1138 | int j; |
@@ -1145,7 +1151,8 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs) | |||
1145 | { | 1151 | { |
1146 | const int sz = w * h; | 1152 | const int sz = w * h; |
1147 | int *shuf = snewn(sz, int), i; | 1153 | int *shuf = snewn(sz, int), i; |
1148 | int *dsf, *next; | 1154 | DSF *dsf; |
1155 | int *next; | ||
1149 | 1156 | ||
1150 | for (i = 0; i < sz; ++i) shuf[i] = i; | 1157 | for (i = 0; i < sz; ++i) shuf[i] = i; |
1151 | shuffle(shuf, sz, sizeof (int), rs); | 1158 | shuffle(shuf, sz, sizeof (int), rs); |
@@ -1162,14 +1169,14 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs) | |||
1162 | dsf = make_dsf(NULL, board, w, h); | 1169 | dsf = make_dsf(NULL, board, w, h); |
1163 | next = snewn(sz, int); | 1170 | next = snewn(sz, int); |
1164 | for (i = 0; i < sz; ++i) { | 1171 | for (i = 0; i < sz; ++i) { |
1165 | int j = dsf_canonify(dsf, i); | 1172 | int j = dsf_minimal(dsf, i); |
1166 | if (i == j) { | 1173 | if (i == j) { |
1167 | /* First cell of a region; set next[i] = -1 to indicate | 1174 | /* First cell of a region; set next[i] = -1 to indicate |
1168 | * end-of-list. */ | 1175 | * end-of-list. */ |
1169 | next[i] = -1; | 1176 | next[i] = -1; |
1170 | } else { | 1177 | } else { |
1171 | /* Add this cell to a region which already has a | 1178 | /* Add this cell to a region which already has a |
1172 | * linked-list head, by pointing the canonical element j | 1179 | * linked-list head, by pointing the minimal element j |
1173 | * at this one, and pointing this one in turn at wherever | 1180 | * at this one, and pointing this one in turn at wherever |
1174 | * j previously pointed. (This should end up with the | 1181 | * j previously pointed. (This should end up with the |
1175 | * elements linked in the order 1,n,n-1,n-2,...,2, which | 1182 | * elements linked in the order 1,n,n-1,n-2,...,2, which |
@@ -1197,7 +1204,7 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs) | |||
1197 | * if we can. | 1204 | * if we can. |
1198 | */ | 1205 | */ |
1199 | for (i = 0; i < sz; ++i) { | 1206 | for (i = 0; i < sz; ++i) { |
1200 | int j = dsf_canonify(dsf, shuf[i]); | 1207 | int j = dsf_minimal(dsf, shuf[i]); |
1201 | if (next[j] != -2) { | 1208 | if (next[j] != -2) { |
1202 | int tmp = board[j]; | 1209 | int tmp = board[j]; |
1203 | int k; | 1210 | int k; |
@@ -1217,7 +1224,7 @@ static void minimize_clue_set(int *board, int w, int h, random_state *rs) | |||
1217 | } | 1224 | } |
1218 | } | 1225 | } |
1219 | sfree(next); | 1226 | sfree(next); |
1220 | sfree(dsf); | 1227 | dsf_free(dsf); |
1221 | 1228 | ||
1222 | /* | 1229 | /* |
1223 | * Now go through individual cells, in the same shuffled order, | 1230 | * Now go through individual cells, in the same shuffled order, |
@@ -1391,7 +1398,7 @@ static game_ui *new_ui(const game_state *state) | |||
1391 | 1398 | ||
1392 | ui->sel = NULL; | 1399 | ui->sel = NULL; |
1393 | ui->cur_x = ui->cur_y = 0; | 1400 | ui->cur_x = ui->cur_y = 0; |
1394 | ui->cur_visible = false; | 1401 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1395 | ui->keydragging = false; | 1402 | ui->keydragging = false; |
1396 | 1403 | ||
1397 | return ui; | 1404 | return ui; |
@@ -1404,15 +1411,6 @@ static void free_ui(game_ui *ui) | |||
1404 | sfree(ui); | 1411 | sfree(ui); |
1405 | } | 1412 | } |
1406 | 1413 | ||
1407 | static char *encode_ui(const game_ui *ui) | ||
1408 | { | ||
1409 | return NULL; | ||
1410 | } | ||
1411 | |||
1412 | static void decode_ui(game_ui *ui, const char *encoding) | ||
1413 | { | ||
1414 | } | ||
1415 | |||
1416 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1414 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
1417 | const game_state *newstate) | 1415 | const game_state *newstate) |
1418 | { | 1416 | { |
@@ -1424,6 +1422,23 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
1424 | ui->keydragging = false; | 1422 | ui->keydragging = false; |
1425 | } | 1423 | } |
1426 | 1424 | ||
1425 | static const char *current_key_label(const game_ui *ui, | ||
1426 | const game_state *state, int button) | ||
1427 | { | ||
1428 | const int w = state->shared->params.w; | ||
1429 | |||
1430 | if (IS_CURSOR_SELECT(button) && ui->cur_visible) { | ||
1431 | if (button == CURSOR_SELECT) { | ||
1432 | if (ui->keydragging) return "Stop"; | ||
1433 | return "Multiselect"; | ||
1434 | } | ||
1435 | if (button == CURSOR_SELECT2 && | ||
1436 | !state->shared->clues[w*ui->cur_y + ui->cur_x]) | ||
1437 | return (ui->sel[w*ui->cur_y + ui->cur_x]) ? "Deselect" : "Select"; | ||
1438 | } | ||
1439 | return ""; | ||
1440 | } | ||
1441 | |||
1427 | #define PREFERRED_TILE_SIZE 32 | 1442 | #define PREFERRED_TILE_SIZE 32 |
1428 | #define TILE_SIZE (ds->tilesize) | 1443 | #define TILE_SIZE (ds->tilesize) |
1429 | #define BORDER (TILE_SIZE / 2) | 1444 | #define BORDER (TILE_SIZE / 2) |
@@ -1434,7 +1449,8 @@ struct game_drawstate { | |||
1434 | int tilesize; | 1449 | int tilesize; |
1435 | bool started; | 1450 | bool started; |
1436 | int *v, *flags; | 1451 | int *v, *flags; |
1437 | int *dsf_scratch, *border_scratch; | 1452 | DSF *dsf_scratch; |
1453 | int *border_scratch; | ||
1438 | }; | 1454 | }; |
1439 | 1455 | ||
1440 | static char *interpret_move(const game_state *state, game_ui *ui, | 1456 | static char *interpret_move(const game_state *state, game_ui *ui, |
@@ -1453,7 +1469,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1453 | assert(ui); | 1469 | assert(ui); |
1454 | assert(ds); | 1470 | assert(ds); |
1455 | 1471 | ||
1456 | button &= ~MOD_MASK; | 1472 | button = STRIP_BUTTON_MODIFIERS(button); |
1457 | 1473 | ||
1458 | if (button == LEFT_BUTTON || button == LEFT_DRAG) { | 1474 | if (button == LEFT_BUTTON || button == LEFT_DRAG) { |
1459 | /* A left-click anywhere will clear the current selection. */ | 1475 | /* A left-click anywhere will clear the current selection. */ |
@@ -1472,22 +1488,22 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1472 | ui->sel[w*ty+tx] = true; | 1488 | ui->sel[w*ty+tx] = true; |
1473 | } | 1489 | } |
1474 | ui->cur_visible = false; | 1490 | ui->cur_visible = false; |
1475 | return UI_UPDATE; | 1491 | return MOVE_UI_UPDATE; |
1476 | } | 1492 | } |
1477 | 1493 | ||
1478 | if (IS_CURSOR_MOVE(button)) { | 1494 | if (IS_CURSOR_MOVE(button)) { |
1479 | ui->cur_visible = true; | 1495 | ui->cur_visible = true; |
1480 | move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false); | 1496 | move_cursor(button, &ui->cur_x, &ui->cur_y, w, h, false, NULL); |
1481 | if (ui->keydragging) goto select_square; | 1497 | if (ui->keydragging) goto select_square; |
1482 | return UI_UPDATE; | 1498 | return MOVE_UI_UPDATE; |
1483 | } | 1499 | } |
1484 | if (button == CURSOR_SELECT) { | 1500 | if (button == CURSOR_SELECT) { |
1485 | if (!ui->cur_visible) { | 1501 | if (!ui->cur_visible) { |
1486 | ui->cur_visible = true; | 1502 | ui->cur_visible = true; |
1487 | return UI_UPDATE; | 1503 | return MOVE_UI_UPDATE; |
1488 | } | 1504 | } |
1489 | ui->keydragging = !ui->keydragging; | 1505 | ui->keydragging = !ui->keydragging; |
1490 | if (!ui->keydragging) return UI_UPDATE; | 1506 | if (!ui->keydragging) return MOVE_UI_UPDATE; |
1491 | 1507 | ||
1492 | select_square: | 1508 | select_square: |
1493 | if (!ui->sel) { | 1509 | if (!ui->sel) { |
@@ -1496,12 +1512,12 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1496 | } | 1512 | } |
1497 | if (!state->shared->clues[w*ui->cur_y + ui->cur_x]) | 1513 | if (!state->shared->clues[w*ui->cur_y + ui->cur_x]) |
1498 | ui->sel[w*ui->cur_y + ui->cur_x] = true; | 1514 | ui->sel[w*ui->cur_y + ui->cur_x] = true; |
1499 | return UI_UPDATE; | 1515 | return MOVE_UI_UPDATE; |
1500 | } | 1516 | } |
1501 | if (button == CURSOR_SELECT2) { | 1517 | if (button == CURSOR_SELECT2) { |
1502 | if (!ui->cur_visible) { | 1518 | if (!ui->cur_visible) { |
1503 | ui->cur_visible = true; | 1519 | ui->cur_visible = true; |
1504 | return UI_UPDATE; | 1520 | return MOVE_UI_UPDATE; |
1505 | } | 1521 | } |
1506 | if (!ui->sel) { | 1522 | if (!ui->sel) { |
1507 | ui->sel = snewn(w*h, bool); | 1523 | ui->sel = snewn(w*h, bool); |
@@ -1515,19 +1531,19 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1515 | sfree(ui->sel); | 1531 | sfree(ui->sel); |
1516 | ui->sel = NULL; | 1532 | ui->sel = NULL; |
1517 | } | 1533 | } |
1518 | return UI_UPDATE; | 1534 | return MOVE_UI_UPDATE; |
1519 | } | 1535 | } |
1520 | 1536 | ||
1521 | if (button == '\b' || button == 27) { | 1537 | if (button == '\b' || button == 27) { |
1522 | sfree(ui->sel); | 1538 | sfree(ui->sel); |
1523 | ui->sel = NULL; | 1539 | ui->sel = NULL; |
1524 | ui->keydragging = false; | 1540 | ui->keydragging = false; |
1525 | return UI_UPDATE; | 1541 | return MOVE_UI_UPDATE; |
1526 | } | 1542 | } |
1527 | 1543 | ||
1528 | if (button < '0' || button > '9') return NULL; | 1544 | if (button < '0' || button > '9') return MOVE_UNUSED; |
1529 | button -= '0'; | 1545 | button -= '0'; |
1530 | if (button > (w == 2 && h == 2 ? 3 : max(w, h))) return NULL; | 1546 | if (button > (w == 2 && h == 2 ? 3 : max(w, h))) return MOVE_UNUSED; |
1531 | ui->keydragging = false; | 1547 | ui->keydragging = false; |
1532 | 1548 | ||
1533 | for (i = 0; i < w*h; i++) { | 1549 | for (i = 0; i < w*h; i++) { |
@@ -1553,11 +1569,11 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1553 | move = srealloc(move, strlen(move)+strlen(buf)+1); | 1569 | move = srealloc(move, strlen(move)+strlen(buf)+1); |
1554 | strcat(move, buf); | 1570 | strcat(move, buf); |
1555 | } | 1571 | } |
1556 | if (!ui->sel) return move ? move : NULL; | 1572 | if (!ui->sel) return move ? move : MOVE_NO_EFFECT; |
1557 | sfree(ui->sel); | 1573 | sfree(ui->sel); |
1558 | ui->sel = NULL; | 1574 | ui->sel = NULL; |
1559 | /* Need to update UI at least, as we cleared the selection */ | 1575 | /* Need to update UI at least, as we cleared the selection */ |
1560 | return move ? move : UI_UPDATE; | 1576 | return move ? move : MOVE_UI_UPDATE; |
1561 | } | 1577 | } |
1562 | 1578 | ||
1563 | static game_state *execute_move(const game_state *state, const char *move) | 1579 | static game_state *execute_move(const game_state *state, const char *move) |
@@ -1567,6 +1583,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
1567 | 1583 | ||
1568 | if (*move == 's') { | 1584 | if (*move == 's') { |
1569 | int i = 0; | 1585 | int i = 0; |
1586 | if (strlen(move) != sz + 1) return NULL; | ||
1570 | new_state = dup_game(state); | 1587 | new_state = dup_game(state); |
1571 | for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0'; | 1588 | for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0'; |
1572 | new_state->cheated = true; | 1589 | new_state->cheated = true; |
@@ -1596,10 +1613,10 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
1596 | const int w = new_state->shared->params.w; | 1613 | const int w = new_state->shared->params.w; |
1597 | const int h = new_state->shared->params.h; | 1614 | const int h = new_state->shared->params.h; |
1598 | const int sz = w * h; | 1615 | const int sz = w * h; |
1599 | int *dsf = make_dsf(NULL, new_state->board, w, h); | 1616 | DSF *dsf = make_dsf(NULL, new_state->board, w, h); |
1600 | int i; | 1617 | int i; |
1601 | for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i); | 1618 | for (i = 0; i < sz && new_state->board[i] == dsf_size(dsf, i); ++i); |
1602 | sfree(dsf); | 1619 | dsf_free(dsf); |
1603 | if (i == sz) | 1620 | if (i == sz) |
1604 | new_state->completed = true; | 1621 | new_state->completed = true; |
1605 | } | 1622 | } |
@@ -1630,7 +1647,7 @@ enum { | |||
1630 | }; | 1647 | }; |
1631 | 1648 | ||
1632 | static void game_compute_size(const game_params *params, int tilesize, | 1649 | static void game_compute_size(const game_params *params, int tilesize, |
1633 | int *x, int *y) | 1650 | const game_ui *ui, int *x, int *y) |
1634 | { | 1651 | { |
1635 | *x = (params->w + 1) * tilesize; | 1652 | *x = (params->w + 1) * tilesize; |
1636 | *y = (params->h + 1) * tilesize; | 1653 | *y = (params->h + 1) * tilesize; |
@@ -1652,9 +1669,9 @@ static float *game_colours(frontend *fe, int *ncolours) | |||
1652 | ret[COL_GRID * 3 + 1] = 0.0F; | 1669 | ret[COL_GRID * 3 + 1] = 0.0F; |
1653 | ret[COL_GRID * 3 + 2] = 0.0F; | 1670 | ret[COL_GRID * 3 + 2] = 0.0F; |
1654 | 1671 | ||
1655 | ret[COL_HIGHLIGHT * 3 + 0] = 0.85F * ret[COL_BACKGROUND * 3 + 0]; | 1672 | ret[COL_HIGHLIGHT * 3 + 0] = 0.7F * ret[COL_BACKGROUND * 3 + 0]; |
1656 | ret[COL_HIGHLIGHT * 3 + 1] = 0.85F * ret[COL_BACKGROUND * 3 + 1]; | 1673 | ret[COL_HIGHLIGHT * 3 + 1] = 0.7F * ret[COL_BACKGROUND * 3 + 1]; |
1657 | ret[COL_HIGHLIGHT * 3 + 2] = 0.85F * ret[COL_BACKGROUND * 3 + 2]; | 1674 | ret[COL_HIGHLIGHT * 3 + 2] = 0.7F * ret[COL_BACKGROUND * 3 + 2]; |
1658 | 1675 | ||
1659 | ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0]; | 1676 | ret[COL_CORRECT * 3 + 0] = 0.9F * ret[COL_BACKGROUND * 3 + 0]; |
1660 | ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1]; | 1677 | ret[COL_CORRECT * 3 + 1] = 0.9F * ret[COL_BACKGROUND * 3 + 1]; |
@@ -1699,7 +1716,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds) | |||
1699 | sfree(ds->v); | 1716 | sfree(ds->v); |
1700 | sfree(ds->flags); | 1717 | sfree(ds->flags); |
1701 | sfree(ds->border_scratch); | 1718 | sfree(ds->border_scratch); |
1702 | sfree(ds->dsf_scratch); | 1719 | dsf_free(ds->dsf_scratch); |
1703 | sfree(ds); | 1720 | sfree(ds); |
1704 | } | 1721 | } |
1705 | 1722 | ||
@@ -2016,17 +2033,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2016 | (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3); | 2033 | (flashtime <= FLASH_TIME/3 || flashtime >= FLASH_TIME*2/3); |
2017 | 2034 | ||
2018 | if (!ds->started) { | 2035 | if (!ds->started) { |
2019 | /* | ||
2020 | * The initial contents of the window are not guaranteed and | ||
2021 | * can vary with front ends. To be on the safe side, all games | ||
2022 | * should start by drawing a big background-colour rectangle | ||
2023 | * covering the whole window. | ||
2024 | */ | ||
2025 | draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER, | ||
2026 | COL_BACKGROUND); | ||
2027 | |||
2028 | /* | 2036 | /* |
2029 | * Smaller black rectangle which is the main grid. | 2037 | * Black rectangle which is the main grid. |
2030 | */ | 2038 | */ |
2031 | draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, | 2039 | draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH, |
2032 | w*TILE_SIZE + 2*BORDER_WIDTH + 1, | 2040 | w*TILE_SIZE + 2*BORDER_WIDTH + 1, |
@@ -2079,24 +2087,21 @@ static int game_status(const game_state *state) | |||
2079 | return state->completed ? +1 : 0; | 2087 | return state->completed ? +1 : 0; |
2080 | } | 2088 | } |
2081 | 2089 | ||
2082 | static bool game_timing_state(const game_state *state, game_ui *ui) | 2090 | static void game_print_size(const game_params *params, const game_ui *ui, |
2083 | { | 2091 | float *x, float *y) |
2084 | return true; | ||
2085 | } | ||
2086 | |||
2087 | static void game_print_size(const game_params *params, float *x, float *y) | ||
2088 | { | 2092 | { |
2089 | int pw, ph; | 2093 | int pw, ph; |
2090 | 2094 | ||
2091 | /* | 2095 | /* |
2092 | * I'll use 6mm squares by default. | 2096 | * I'll use 6mm squares by default. |
2093 | */ | 2097 | */ |
2094 | game_compute_size(params, 600, &pw, &ph); | 2098 | game_compute_size(params, 600, ui, &pw, &ph); |
2095 | *x = pw / 100.0F; | 2099 | *x = pw / 100.0F; |
2096 | *y = ph / 100.0F; | 2100 | *y = ph / 100.0F; |
2097 | } | 2101 | } |
2098 | 2102 | ||
2099 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 2103 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
2104 | int tilesize) | ||
2100 | { | 2105 | { |
2101 | const int w = state->shared->params.w; | 2106 | const int w = state->shared->params.w; |
2102 | const int h = state->shared->params.h; | 2107 | const int h = state->shared->params.h; |
@@ -2164,12 +2169,14 @@ const struct game thegame = { | |||
2164 | free_game, | 2169 | free_game, |
2165 | true, solve_game, | 2170 | true, solve_game, |
2166 | true, game_can_format_as_text_now, game_text_format, | 2171 | true, game_can_format_as_text_now, game_text_format, |
2172 | NULL, NULL, /* get_prefs, set_prefs */ | ||
2167 | new_ui, | 2173 | new_ui, |
2168 | free_ui, | 2174 | free_ui, |
2169 | encode_ui, | 2175 | NULL, /* encode_ui */ |
2170 | decode_ui, | 2176 | NULL, /* decode_ui */ |
2171 | game_request_keys, | 2177 | game_request_keys, |
2172 | game_changed_state, | 2178 | game_changed_state, |
2179 | current_key_label, | ||
2173 | interpret_move, | 2180 | interpret_move, |
2174 | execute_move, | 2181 | execute_move, |
2175 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 2182 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -2183,13 +2190,18 @@ const struct game thegame = { | |||
2183 | game_status, | 2190 | game_status, |
2184 | true, false, game_print_size, game_print, | 2191 | true, false, game_print_size, game_print, |
2185 | false, /* wants_statusbar */ | 2192 | false, /* wants_statusbar */ |
2186 | false, game_timing_state, | 2193 | false, NULL, /* timing_state */ |
2187 | REQUIRE_NUMPAD, /* flags */ | 2194 | REQUIRE_NUMPAD, /* flags */ |
2188 | }; | 2195 | }; |
2189 | 2196 | ||
2190 | #ifdef STANDALONE_SOLVER /* solver? hah! */ | 2197 | #ifdef STANDALONE_SOLVER /* solver? hah! */ |
2191 | 2198 | ||
2192 | int main(int argc, char **argv) { | 2199 | int main(int argc, char **argv) { |
2200 | if (!strcmp(argv[1], "--verbose")) { | ||
2201 | verbose = true; | ||
2202 | argv++; | ||
2203 | } | ||
2204 | |||
2193 | while (*++argv) { | 2205 | while (*++argv) { |
2194 | game_params *params; | 2206 | game_params *params; |
2195 | game_state *state; | 2207 | game_state *state; |