summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/flood.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/flood.c')
-rw-r--r--apps/plugins/puzzles/src/flood.c184
1 files changed, 92 insertions, 92 deletions
diff --git a/apps/plugins/puzzles/src/flood.c b/apps/plugins/puzzles/src/flood.c
index 74214a50b6..fe809745db 100644
--- a/apps/plugins/puzzles/src/flood.c
+++ b/apps/plugins/puzzles/src/flood.c
@@ -31,7 +31,12 @@
31#include <string.h> 31#include <string.h>
32#include <assert.h> 32#include <assert.h>
33#include <ctype.h> 33#include <ctype.h>
34#include <math.h> 34#include <limits.h>
35#ifdef NO_TGMATH_H
36# include <math.h>
37#else
38# include <tgmath.h>
39#endif
35 40
36#include "puzzles.h" 41#include "puzzles.h"
37 42
@@ -140,13 +145,13 @@ static void decode_params(game_params *ret, char const *string)
140 if (*string == 'c') { 145 if (*string == 'c') {
141 string++; 146 string++;
142 ret->colours = atoi(string); 147 ret->colours = atoi(string);
143 while (string[1] && isdigit((unsigned char)string[1])) string++; 148 while (*string && isdigit((unsigned char)*string)) string++;
144 } else if (*string == 'm') { 149 } else if (*string == 'm') {
145 string++; 150 string++;
146 ret->leniency = atoi(string); 151 ret->leniency = atoi(string);
147 while (string[1] && isdigit((unsigned char)string[1])) string++; 152 while (*string && isdigit((unsigned char)*string)) string++;
148 } 153 } else
149 string++; 154 string++;
150 } 155 }
151} 156}
152 157
@@ -210,7 +215,9 @@ static const char *validate_params(const game_params *params, bool full)
210 if (params->w * params->h < 2) 215 if (params->w * params->h < 2)
211 return "Grid must contain at least two squares"; 216 return "Grid must contain at least two squares";
212 if (params->w < 1 || params->h < 1) 217 if (params->w < 1 || params->h < 1)
213 return "Width and height must both be at least one"; 218 return "Width and height must be at least one";
219 if (params->w > INT_MAX / params->h)
220 return "Width times height must not be unreasonably large";
214 if (params->colours < 3 || params->colours > 10) 221 if (params->colours < 3 || params->colours > 10)
215 return "Must have between 3 and 10 colours"; 222 return "Must have between 3 and 10 colours";
216 if (params->leniency < 0) 223 if (params->leniency < 0)
@@ -554,8 +561,10 @@ static char *new_game_desc(const game_params *params, random_state *rs,
554 /* 561 /*
555 * Invent a random grid. 562 * Invent a random grid.
556 */ 563 */
557 for (i = 0; i < wh; i++) 564 do {
558 scratch->grid[i] = random_upto(rs, params->colours); 565 for (i = 0; i < wh; i++)
566 scratch->grid[i] = random_upto(rs, params->colours);
567 } while (completed(w, h, scratch->grid));
559 568
560 /* 569 /*
561 * Run the solver, and count how many moves it uses. 570 * Run the solver, and count how many moves it uses.
@@ -770,7 +779,7 @@ struct game_ui {
770static game_ui *new_ui(const game_state *state) 779static game_ui *new_ui(const game_state *state)
771{ 780{
772 struct game_ui *ui = snew(struct game_ui); 781 struct game_ui *ui = snew(struct game_ui);
773 ui->cursor_visible = false; 782 ui->cursor_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
774 ui->cx = FILLX; 783 ui->cx = FILLX;
775 ui->cy = FILLY; 784 ui->cy = FILLY;
776 return ui; 785 return ui;
@@ -781,18 +790,21 @@ static void free_ui(game_ui *ui)
781 sfree(ui); 790 sfree(ui);
782} 791}
783 792
784static char *encode_ui(const game_ui *ui) 793static void game_changed_state(game_ui *ui, const game_state *oldstate,
785{ 794 const game_state *newstate)
786 return NULL;
787}
788
789static void decode_ui(game_ui *ui, const char *encoding)
790{ 795{
791} 796}
792 797
793static void game_changed_state(game_ui *ui, const game_state *oldstate, 798static const char *current_key_label(const game_ui *ui,
794 const game_state *newstate) 799 const game_state *state, int button)
795{ 800{
801 if (button == CURSOR_SELECT &&
802 state->grid[0] != state->grid[ui->cy*state->w+ui->cx])
803 return "Fill";
804 if (button == CURSOR_SELECT2 &&
805 state->soln && state->solnpos < state->soln->nmoves)
806 return "Advance";
807 return "";
796} 808}
797 809
798struct game_drawstate { 810struct game_drawstate {
@@ -818,35 +830,26 @@ static char *interpret_move(const game_state *state, game_ui *ui,
818{ 830{
819 int w = state->w, h = state->h; 831 int w = state->w, h = state->h;
820 int tx = -1, ty = -1, move = -1; 832 int tx = -1, ty = -1, move = -1;
833 char *nullret = MOVE_NO_EFFECT;
821 834
822 if (button == LEFT_BUTTON) { 835 if (button == LEFT_BUTTON) {
823 tx = FROMCOORD(x); 836 tx = FROMCOORD(x);
824 ty = FROMCOORD(y); 837 ty = FROMCOORD(y);
825 ui->cursor_visible = false; 838 if (ui->cursor_visible) {
826 } else if (button == CURSOR_LEFT && ui->cx > 0) { 839 ui->cursor_visible = false;
827 ui->cx--; 840 nullret = MOVE_UI_UPDATE;
828 ui->cursor_visible = true; 841 }
829 return UI_UPDATE; 842 } else if (IS_CURSOR_MOVE(button)) {
830 } else if (button == CURSOR_RIGHT && ui->cx+1 < w) { 843 return move_cursor(button, &ui->cx, &ui->cy, w, h, false,
831 ui->cx++; 844 &ui->cursor_visible);
832 ui->cursor_visible = true;
833 return UI_UPDATE;
834 } else if (button == CURSOR_UP && ui->cy > 0) {
835 ui->cy--;
836 ui->cursor_visible = true;
837 return UI_UPDATE;
838 } else if (button == CURSOR_DOWN && ui->cy+1 < h) {
839 ui->cy++;
840 ui->cursor_visible = true;
841 return UI_UPDATE;
842 } else if (button == CURSOR_SELECT) { 845 } else if (button == CURSOR_SELECT) {
843 tx = ui->cx; 846 tx = ui->cx;
844 ty = ui->cy; 847 ty = ui->cy;
845 } else if (button == CURSOR_SELECT2 && 848 } else if (button == CURSOR_SELECT2) {
846 state->soln && state->solnpos < state->soln->nmoves) { 849 if (state->soln && state->solnpos < state->soln->nmoves)
847 move = state->soln->moves[state->solnpos]; 850 move = state->soln->moves[state->solnpos];
848 } else { 851 } else {
849 return NULL; 852 return MOVE_UNUSED;
850 } 853 }
851 854
852 if (tx >= 0 && tx < w && ty >= 0 && ty < h && 855 if (tx >= 0 && tx < w && ty >= 0 && ty < h &&
@@ -859,7 +862,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
859 return dupstr(buf); 862 return dupstr(buf);
860 } 863 }
861 864
862 return NULL; 865 return nullret;
863} 866}
864 867
865static game_state *execute_move(const game_state *state, const char *move) 868static game_state *execute_move(const game_state *state, const char *move)
@@ -869,7 +872,8 @@ static game_state *execute_move(const game_state *state, const char *move)
869 872
870 if (move[0] == 'M' && 873 if (move[0] == 'M' &&
871 sscanf(move+1, "%d", &c) == 1 && 874 sscanf(move+1, "%d", &c) == 1 &&
872 c >= 0 && 875 c >= 0 && c < state->colours &&
876 c != state->grid[FILLY * state->w + FILLX] &&
873 !state->complete) { 877 !state->complete) {
874 int *queue = snewn(state->w * state->h, int); 878 int *queue = snewn(state->w * state->h, int);
875 ret = dup_game(state); 879 ret = dup_game(state);
@@ -920,11 +924,23 @@ static game_state *execute_move(const game_state *state, const char *move)
920 924
921 sol->moves = snewn(sol->nmoves, char); 925 sol->moves = snewn(sol->nmoves, char);
922 for (i = 0, p = move; i < sol->nmoves; i++) { 926 for (i = 0, p = move; i < sol->nmoves; i++) {
923 assert(*p); 927 if (!*p) {
928 badsolve:
929 sfree(sol->moves);
930 sfree(sol);
931 return NULL;
932 };
924 sol->moves[i] = atoi(p); 933 sol->moves[i] = atoi(p);
934 if (sol->moves[i] < 0 || sol->moves[i] >= state->colours ||
935 (i == 0 ?
936 sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
937 sol->moves[i] == sol->moves[i-1]))
938 /* Solution contains a fill with an invalid colour or
939 * the current colour. */
940 goto badsolve;
925 p += strspn(p, "0123456789"); 941 p += strspn(p, "0123456789");
926 if (*p) { 942 if (*p) {
927 assert(*p == ','); 943 if (*p != ',') goto badsolve;
928 p++; 944 p++;
929 } 945 }
930 } 946 }
@@ -949,7 +965,7 @@ static game_state *execute_move(const game_state *state, const char *move)
949 */ 965 */
950 966
951static void game_compute_size(const game_params *params, int tilesize, 967static void game_compute_size(const game_params *params, int tilesize,
952 int *x, int *y) 968 const game_ui *ui, int *x, int *y)
953{ 969{
954 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 970 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
955 struct { int tilesize; } ads, *ds = &ads; 971 struct { int tilesize; } ads, *ds = &ads;
@@ -1076,31 +1092,33 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
1076 colour += COL_1; 1092 colour += COL_1;
1077 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour); 1093 draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour);
1078 1094
1079 if (tile & BORDER_L) 1095 if (SEP_WIDTH > 0) {
1080 draw_rect(dr, tx, ty, 1096 if (tile & BORDER_L)
1081 SEP_WIDTH, TILESIZE, COL_SEPARATOR); 1097 draw_rect(dr, tx, ty,
1082 if (tile & BORDER_R) 1098 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1083 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty, 1099 if (tile & BORDER_R)
1084 SEP_WIDTH, TILESIZE, COL_SEPARATOR); 1100 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1085 if (tile & BORDER_U) 1101 SEP_WIDTH, TILESIZE, COL_SEPARATOR);
1086 draw_rect(dr, tx, ty, 1102 if (tile & BORDER_U)
1087 TILESIZE, SEP_WIDTH, COL_SEPARATOR); 1103 draw_rect(dr, tx, ty,
1088 if (tile & BORDER_D) 1104 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1089 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH, 1105 if (tile & BORDER_D)
1090 TILESIZE, SEP_WIDTH, COL_SEPARATOR); 1106 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1091 1107 TILESIZE, SEP_WIDTH, COL_SEPARATOR);
1092 if (tile & CORNER_UL) 1108
1093 draw_rect(dr, tx, ty, 1109 if (tile & CORNER_UL)
1094 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1110 draw_rect(dr, tx, ty,
1095 if (tile & CORNER_UR) 1111 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1096 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty, 1112 if (tile & CORNER_UR)
1097 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1113 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
1098 if (tile & CORNER_DL) 1114 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1099 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH, 1115 if (tile & CORNER_DL)
1100 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1116 draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
1101 if (tile & CORNER_DR) 1117 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1102 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH, 1118 if (tile & CORNER_DR)
1103 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR); 1119 draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH,
1120 SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
1121 }
1104 1122
1105 if (tile & CURSOR) 1123 if (tile & CURSOR)
1106 draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET, 1124 draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET,
@@ -1130,13 +1148,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1130 if (!ds->started) { 1148 if (!ds->started) {
1131 int coords[10]; 1149 int coords[10];
1132 1150
1133 draw_rect(dr, 0, 0,
1134 TILESIZE * w + 2 * BORDER,
1135 TILESIZE * h + 2 * BORDER, COL_BACKGROUND);
1136 draw_update(dr, 0, 0,
1137 TILESIZE * w + 2 * BORDER,
1138 TILESIZE * h + 2 * BORDER);
1139
1140 /* 1151 /*
1141 * Recessed area containing the whole puzzle. 1152 * Recessed area containing the whole puzzle.
1142 */ 1153 */
@@ -1326,19 +1337,6 @@ static float game_flash_length(const game_state *oldstate,
1326 return 0.0F; 1337 return 0.0F;
1327} 1338}
1328 1339
1329static bool game_timing_state(const game_state *state, game_ui *ui)
1330{
1331 return true;
1332}
1333
1334static void game_print_size(const game_params *params, float *x, float *y)
1335{
1336}
1337
1338static void game_print(drawing *dr, const game_state *state, int tilesize)
1339{
1340}
1341
1342#ifdef COMBINED 1340#ifdef COMBINED
1343#define thegame flood 1341#define thegame flood
1344#endif 1342#endif
@@ -1360,12 +1358,14 @@ const struct game thegame = {
1360 free_game, 1358 free_game,
1361 true, solve_game, 1359 true, solve_game,
1362 true, game_can_format_as_text_now, game_text_format, 1360 true, game_can_format_as_text_now, game_text_format,
1361 NULL, NULL, /* get_prefs, set_prefs */
1363 new_ui, 1362 new_ui,
1364 free_ui, 1363 free_ui,
1365 encode_ui, 1364 NULL, /* encode_ui */
1366 decode_ui, 1365 NULL, /* decode_ui */
1367 NULL, /* game_request_keys */ 1366 NULL, /* game_request_keys */
1368 game_changed_state, 1367 game_changed_state,
1368 current_key_label,
1369 interpret_move, 1369 interpret_move,
1370 execute_move, 1370 execute_move,
1371 PREFERRED_TILESIZE, game_compute_size, game_set_size, 1371 PREFERRED_TILESIZE, game_compute_size, game_set_size,
@@ -1377,8 +1377,8 @@ const struct game thegame = {
1377 game_flash_length, 1377 game_flash_length,
1378 game_get_cursor_location, 1378 game_get_cursor_location,
1379 game_status, 1379 game_status,
1380 false, false, game_print_size, game_print, 1380 false, false, NULL, NULL, /* print_size, print */
1381 true, /* wants_statusbar */ 1381 true, /* wants_statusbar */
1382 false, game_timing_state, 1382 false, NULL, /* timing_state */
1383 0, /* flags */ 1383 0, /* flags */
1384}; 1384};