summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/filling.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/filling.c')
-rw-r--r--apps/plugins/puzzles/src/filling.c198
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
69static bool verbose; 73static bool verbose;
70 74
71static 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
290struct solver_state 291struct 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
302static void print_board(int *board, int w, int h) { 304static void print_board(int *board, int w, int h) {
@@ -310,7 +312,7 @@ static void print_board(int *board, int w, int h) {
310static game_state *new_game(midend *, const game_params *, const char *); 312static game_state *new_game(midend *, const game_params *, const char *);
311static void free_game(game_state *); 313static void free_game(game_state *);
312 314
313#define SENTINEL sz 315#define SENTINEL (sz+1)
314 316
315static bool mark_region(int *board, int w, int h, int i, int n, int m) { 317static 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);
405retry: 408retry:
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
459static void merge(int *dsf, int *connected, int a, int b) { 467static 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
535static int expandsize(const int *board, int *dsf, int w, int h, int i, int n) { 543static 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
1122static int *make_dsf(int *dsf, int *board, const int w, const int h) { 1128static 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
1407static char *encode_ui(const game_ui *ui)
1408{
1409 return NULL;
1410}
1411
1412static void decode_ui(game_ui *ui, const char *encoding)
1413{
1414}
1415
1416static void game_changed_state(game_ui *ui, const game_state *oldstate, 1414static 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
1425static 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
1440static char *interpret_move(const game_state *state, game_ui *ui, 1456static 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
1563static game_state *execute_move(const game_state *state, const char *move) 1579static 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
1632static void game_compute_size(const game_params *params, int tilesize, 1649static 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
2082static bool game_timing_state(const game_state *state, game_ui *ui) 2090static void game_print_size(const game_params *params, const game_ui *ui,
2083{ 2091 float *x, float *y)
2084 return true;
2085}
2086
2087static 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
2099static void game_print(drawing *dr, const game_state *state, int tilesize) 2103static 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
2192int main(int argc, char **argv) { 2199int 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;