summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/samegame.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/samegame.c')
-rw-r--r--apps/plugins/puzzles/src/samegame.c182
1 files changed, 86 insertions, 96 deletions
diff --git a/apps/plugins/puzzles/src/samegame.c b/apps/plugins/puzzles/src/samegame.c
index 615c60e0a5..30550e1f1c 100644
--- a/apps/plugins/puzzles/src/samegame.c
+++ b/apps/plugins/puzzles/src/samegame.c
@@ -67,7 +67,12 @@
67#include <string.h> 67#include <string.h>
68#include <assert.h> 68#include <assert.h>
69#include <ctype.h> 69#include <ctype.h>
70#include <math.h> 70#include <limits.h>
71#ifdef NO_TGMATH_H
72# include <math.h>
73#else
74# include <tgmath.h>
75#endif
71 76
72#include "puzzles.h" 77#include "puzzles.h"
73 78
@@ -285,6 +290,8 @@ static const char *validate_params(const game_params *params, bool full)
285{ 290{
286 if (params->w < 1 || params->h < 1) 291 if (params->w < 1 || params->h < 1)
287 return "Width and height must both be positive"; 292 return "Width and height must both be positive";
293 if (params->w > INT_MAX / params->h)
294 return "Width times height must not be unreasonably large";
288 295
289 if (params->ncols > 9) 296 if (params->ncols > 9)
290 return "Maximum of 9 colours"; 297 return "Maximum of 9 colours";
@@ -858,6 +865,8 @@ static void gen_grid(int w, int h, int nc, int *grid, random_state *rs)
858 865
859#if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES 866#if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES
860 printf("%d failures\n", failures); 867 printf("%d failures\n", failures);
868#else
869 (void)failures;
861#endif 870#endif
862#ifdef GENERATION_DIAGNOSTICS 871#ifdef GENERATION_DIAGNOSTICS
863 { 872 {
@@ -1013,12 +1022,6 @@ static void free_game(game_state *state)
1013 sfree(state); 1022 sfree(state);
1014} 1023}
1015 1024
1016static char *solve_game(const game_state *state, const game_state *currstate,
1017 const char *aux, const char **error)
1018{
1019 return NULL;
1020}
1021
1022static bool game_can_format_as_text_now(const game_params *params) 1025static bool game_can_format_as_text_now(const game_params *params)
1023{ 1026{
1024 return true; 1027 return true;
@@ -1065,7 +1068,7 @@ static game_ui *new_ui(const game_state *state)
1065 ui->nselected = 0; 1068 ui->nselected = 0;
1066 1069
1067 ui->xsel = ui->ysel = 0; 1070 ui->xsel = ui->ysel = 0;
1068 ui->displaysel = false; 1071 ui->displaysel = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1069 1072
1070 return ui; 1073 return ui;
1071} 1074}
@@ -1076,15 +1079,6 @@ static void free_ui(game_ui *ui)
1076 sfree(ui); 1079 sfree(ui);
1077} 1080}
1078 1081
1079static char *encode_ui(const game_ui *ui)
1080{
1081 return NULL;
1082}
1083
1084static void decode_ui(game_ui *ui, const char *encoding)
1085{
1086}
1087
1088static void sel_clear(game_ui *ui, const game_state *state) 1082static void sel_clear(game_ui *ui, const game_state *state)
1089{ 1083{
1090 int i; 1084 int i;
@@ -1099,14 +1093,25 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
1099 const game_state *newstate) 1093 const game_state *newstate)
1100{ 1094{
1101 sel_clear(ui, newstate); 1095 sel_clear(ui, newstate);
1096}
1102 1097
1103 /* 1098static const char *current_key_label(const game_ui *ui,
1104 * If the game state has just changed into an unplayable one 1099 const game_state *state, int button)
1105 * (either completed or impossible), we vanish the keyboard- 1100{
1106 * control cursor. 1101 if (IS_CURSOR_SELECT(button)) {
1107 */ 1102 int x = ui->xsel, y = ui->ysel, c = COL(state,x,y);
1108 if (newstate->complete || newstate->impossible) 1103 if (c == 0) return "";
1109 ui->displaysel = false; 1104 if (ISSEL(ui, x, y))
1105 return button == CURSOR_SELECT2 ? "Unselect" : "Remove";
1106 if ((x > 0 && COL(state,x-1,y) == c) ||
1107 (x+1 < state->params.w && COL(state,x+1,y) == c) ||
1108 (y > 0 && COL(state,x,y-1) == c) ||
1109 (y+1 < state->params.h && COL(state,x,y+1) == c))
1110 return "Select";
1111 /* Cursor is over a lone square, so we can't select it. */
1112 if (ui->nselected) return "Unselect";
1113 }
1114 return "";
1110} 1115}
1111 1116
1112static char *sel_movedesc(game_ui *ui, const game_state *state) 1117static char *sel_movedesc(game_ui *ui, const game_state *state)
@@ -1274,30 +1279,25 @@ static char *interpret_move(const game_state *state, game_ui *ui,
1274 int x, int y, int button) 1279 int x, int y, int button)
1275{ 1280{
1276 int tx, ty; 1281 int tx, ty;
1277 char *ret = UI_UPDATE; 1282 char *ret = MOVE_UI_UPDATE;
1278
1279 ui->displaysel = false;
1280 1283
1281 if (button == RIGHT_BUTTON || button == LEFT_BUTTON) { 1284 if (button == RIGHT_BUTTON || button == LEFT_BUTTON) {
1285 ui->displaysel = false;
1282 tx = FROMCOORD(x); ty= FROMCOORD(y); 1286 tx = FROMCOORD(x); ty= FROMCOORD(y);
1283 } else if (IS_CURSOR_MOVE(button)) { 1287 } else if (IS_CURSOR_MOVE(button)) {
1284 int dx = 0, dy = 0; 1288 return move_cursor(button, &ui->xsel, &ui->ysel,
1285 ui->displaysel = true; 1289 state->params.w, state->params.h,
1286 dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0); 1290 true, &ui->displaysel);
1287 dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
1288 ui->xsel = (ui->xsel + state->params.w + dx) % state->params.w;
1289 ui->ysel = (ui->ysel + state->params.h + dy) % state->params.h;
1290 return ret;
1291 } else if (IS_CURSOR_SELECT(button)) { 1291 } else if (IS_CURSOR_SELECT(button)) {
1292 ui->displaysel = true; 1292 ui->displaysel = true;
1293 tx = ui->xsel; 1293 tx = ui->xsel;
1294 ty = ui->ysel; 1294 ty = ui->ysel;
1295 } else 1295 } else
1296 return NULL; 1296 return MOVE_UNUSED;
1297 1297
1298 if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h) 1298 if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h)
1299 return NULL; 1299 return MOVE_UNUSED;
1300 if (COL(state, tx, ty) == 0) return NULL; 1300 if (COL(state, tx, ty) == 0) return MOVE_NO_EFFECT;
1301 1301
1302 if (ISSEL(ui,tx,ty)) { 1302 if (ISSEL(ui,tx,ty)) {
1303 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) 1303 if (button == RIGHT_BUTTON || button == CURSOR_SELECT2)
@@ -1324,6 +1324,10 @@ static game_state *execute_move(const game_state *from, const char *move)
1324 move++; 1324 move++;
1325 1325
1326 while (*move) { 1326 while (*move) {
1327 if (!isdigit((unsigned char)*move)) {
1328 free_game(ret);
1329 return NULL;
1330 }
1327 i = atoi(move); 1331 i = atoi(move);
1328 if (i < 0 || i >= ret->n) { 1332 if (i < 0 || i >= ret->n) {
1329 free_game(ret); 1333 free_game(ret);
@@ -1353,12 +1357,12 @@ static game_state *execute_move(const game_state *from, const char *move)
1353static void game_set_size(drawing *dr, game_drawstate *ds, 1357static void game_set_size(drawing *dr, game_drawstate *ds,
1354 const game_params *params, int tilesize) 1358 const game_params *params, int tilesize)
1355{ 1359{
1356 ds->tilegap = 2; 1360 ds->tilegap = (tilesize + 8) / 16;
1357 ds->tileinner = tilesize - ds->tilegap; 1361 ds->tileinner = tilesize - ds->tilegap;
1358} 1362}
1359 1363
1360static void game_compute_size(const game_params *params, int tilesize, 1364static void game_compute_size(const game_params *params, int tilesize,
1361 int *x, int *y) 1365 const game_ui *ui, int *x, int *y)
1362{ 1366{
1363 /* Ick: fake up tile size variables for macro expansion purposes */ 1367 /* Ick: fake up tile size variables for macro expansion purposes */
1364 game_drawstate ads, *ds = &ads; 1368 game_drawstate ads, *ds = &ads;
@@ -1372,7 +1376,7 @@ static float *game_colours(frontend *fe, int *ncolours)
1372{ 1376{
1373 float *ret = snewn(3 * NCOLOURS, float); 1377 float *ret = snewn(3 * NCOLOURS, float);
1374 1378
1375 frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); 1379 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1376 1380
1377 ret[COL_1 * 3 + 0] = 0.0F; 1381 ret[COL_1 * 3 + 0] = 0.0F;
1378 ret[COL_1 * 3 + 1] = 0.0F; 1382 ret[COL_1 * 3 + 1] = 0.0F;
@@ -1386,8 +1390,8 @@ static float *game_colours(frontend *fe, int *ncolours)
1386 ret[COL_3 * 3 + 1] = 0.0F; 1390 ret[COL_3 * 3 + 1] = 0.0F;
1387 ret[COL_3 * 3 + 2] = 0.0F; 1391 ret[COL_3 * 3 + 2] = 0.0F;
1388 1392
1389 ret[COL_4 * 3 + 0] = 1.0F; 1393 ret[COL_4 * 3 + 0] = 0.7F;
1390 ret[COL_4 * 3 + 1] = 1.0F; 1394 ret[COL_4 * 3 + 1] = 0.7F;
1391 ret[COL_4 * 3 + 2] = 0.0F; 1395 ret[COL_4 * 3 + 2] = 0.0F;
1392 1396
1393 ret[COL_5 * 3 + 0] = 1.0F; 1397 ret[COL_5 * 3 + 0] = 1.0F;
@@ -1395,16 +1399,16 @@ static float *game_colours(frontend *fe, int *ncolours)
1395 ret[COL_5 * 3 + 2] = 1.0F; 1399 ret[COL_5 * 3 + 2] = 1.0F;
1396 1400
1397 ret[COL_6 * 3 + 0] = 0.0F; 1401 ret[COL_6 * 3 + 0] = 0.0F;
1398 ret[COL_6 * 3 + 1] = 1.0F; 1402 ret[COL_6 * 3 + 1] = 0.8F;
1399 ret[COL_6 * 3 + 2] = 1.0F; 1403 ret[COL_6 * 3 + 2] = 0.8F;
1400 1404
1401 ret[COL_7 * 3 + 0] = 0.5F; 1405 ret[COL_7 * 3 + 0] = 0.5F;
1402 ret[COL_7 * 3 + 1] = 0.5F; 1406 ret[COL_7 * 3 + 1] = 0.5F;
1403 ret[COL_7 * 3 + 2] = 1.0F; 1407 ret[COL_7 * 3 + 2] = 1.0F;
1404 1408
1405 ret[COL_8 * 3 + 0] = 0.5F; 1409 ret[COL_8 * 3 + 0] = 0.2F;
1406 ret[COL_8 * 3 + 1] = 1.0F; 1410 ret[COL_8 * 3 + 1] = 0.8F;
1407 ret[COL_8 * 3 + 2] = 0.5F; 1411 ret[COL_8 * 3 + 2] = 0.2F;
1408 1412
1409 ret[COL_9 * 3 + 0] = 1.0F; 1413 ret[COL_9 * 3 + 0] = 1.0F;
1410 ret[COL_9 * 3 + 1] = 0.5F; 1414 ret[COL_9 * 3 + 1] = 0.5F;
@@ -1418,14 +1422,6 @@ static float *game_colours(frontend *fe, int *ncolours)
1418 ret[COL_SEL * 3 + 1] = 1.0F; 1422 ret[COL_SEL * 3 + 1] = 1.0F;
1419 ret[COL_SEL * 3 + 2] = 1.0F; 1423 ret[COL_SEL * 3 + 2] = 1.0F;
1420 1424
1421 ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
1422 ret[COL_HIGHLIGHT * 3 + 1] = 1.0F;
1423 ret[COL_HIGHLIGHT * 3 + 2] = 1.0F;
1424
1425 ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 2.0F / 3.0F;
1426 ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 2.0F / 3.0F;
1427 ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 2.0F / 3.0F;
1428
1429 *ncolours = NCOLOURS; 1425 *ncolours = NCOLOURS;
1430 return ret; 1426 return ret;
1431} 1427}
@@ -1462,6 +1458,7 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
1462 int tile, int bgcolour) 1458 int tile, int bgcolour)
1463{ 1459{
1464 int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK; 1460 int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK;
1461 int tile_w, tile_h, outer_w, outer_h;
1465 1462
1466 if (col) { 1463 if (col) {
1467 if (tile & TILE_IMPOSSIBLE) { 1464 if (tile & TILE_IMPOSSIBLE) {
@@ -1474,19 +1471,25 @@ static void tile_redraw(drawing *dr, game_drawstate *ds,
1474 outer = inner = col; 1471 outer = inner = col;
1475 } 1472 }
1476 } 1473 }
1477 draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer); 1474 tile_w = dright ? TILE_SIZE : TILE_INNER;
1478 draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4, 1475 tile_h = dbelow ? TILE_SIZE : TILE_INNER;
1479 TILE_INNER/2, TILE_INNER/2, inner); 1476 outer_w = (tile & TILE_JOINRIGHT) ? tile_w : TILE_INNER;
1480 1477 outer_h = (tile & TILE_JOINDOWN) ? tile_h : TILE_INNER;
1481 if (dright) 1478 /* Draw the background if any of it will be visible. */
1482 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER, 1479 if (outer_w != tile_w || outer_h != tile_h || outer == bgcolour)
1483 (tile & TILE_JOINRIGHT) ? outer : bgcolour); 1480 draw_rect(dr, COORD(x), COORD(y), tile_w, tile_h, bgcolour);
1484 if (dbelow) 1481 /* Draw the piece. */
1485 draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP, 1482 if (outer != bgcolour)
1486 (tile & TILE_JOINDOWN) ? outer : bgcolour); 1483 draw_rect(dr, COORD(x), COORD(y), outer_w, outer_h, outer);
1487 if (dright && dbelow) 1484 if (inner != outer)
1488 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP, 1485 draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
1489 (tile & TILE_JOINDIAG) ? outer : bgcolour); 1486 TILE_INNER/2, TILE_INNER/2, inner);
1487 /* Reset bottom-right corner if necessary. */
1488 if ((tile & (TILE_JOINRIGHT | TILE_JOINDOWN | TILE_JOINDIAG)) ==
1489 (TILE_JOINRIGHT | TILE_JOINDOWN) && outer != bgcolour &&
1490 TILE_GAP != 0)
1491 draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER,
1492 TILE_GAP, TILE_GAP, bgcolour);
1490 1493
1491 if (tile & TILE_HASSEL) { 1494 if (tile & TILE_HASSEL) {
1492 int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5; 1495 int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5;
@@ -1512,13 +1515,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1512 if (!ds->started) { 1515 if (!ds->started) {
1513 int coords[10]; 1516 int coords[10];
1514 1517
1515 draw_rect(dr, 0, 0,
1516 TILE_SIZE * state->params.w + 2 * BORDER,
1517 TILE_SIZE * state->params.h + 2 * BORDER, COL_BACKGROUND);
1518 draw_update(dr, 0, 0,
1519 TILE_SIZE * state->params.w + 2 * BORDER,
1520 TILE_SIZE * state->params.h + 2 * BORDER);
1521
1522 /* 1518 /*
1523 * Recessed area containing the whole puzzle. 1519 * Recessed area containing the whole puzzle.
1524 */ 1520 */
@@ -1541,7 +1537,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1541 ds->started = true; 1537 ds->started = true;
1542 } 1538 }
1543 1539
1544 if (flashtime > 0.0) { 1540 if (flashtime > 0.0F) {
1545 int frame = (int)(flashtime / FLASH_FRAME); 1541 int frame = (int)(flashtime / FLASH_FRAME);
1546 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT); 1542 bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
1547 } else 1543 } else
@@ -1564,8 +1560,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
1564 if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) && 1560 if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) &&
1565 COL(state,x+1,y+1) == col) 1561 COL(state,x+1,y+1) == col)
1566 tile |= TILE_JOINDIAG; 1562 tile |= TILE_JOINDIAG;
1567 1563 /*
1568 if (ui->displaysel && ui->xsel == x && ui->ysel == y) 1564 * If the game state is an unplayable one (either
1565 * completed or impossible), we hide the keyboard-control
1566 * cursor.
1567 */
1568 if (ui->displaysel && ui->xsel == x && ui->ysel == y &&
1569 !(state->complete || state->impossible))
1569 tile |= TILE_HASSEL; 1570 tile |= TILE_HASSEL;
1570 1571
1571 /* For now we're never expecting oldstate at all (because we have 1572 /* For now we're never expecting oldstate at all (because we have
@@ -1637,19 +1638,6 @@ static int game_status(const game_state *state)
1637 return state->complete ? +1 : 0; 1638 return state->complete ? +1 : 0;
1638} 1639}
1639 1640
1640static bool game_timing_state(const game_state *state, game_ui *ui)
1641{
1642 return true;
1643}
1644
1645static void game_print_size(const game_params *params, float *x, float *y)
1646{
1647}
1648
1649static void game_print(drawing *dr, const game_state *state, int tilesize)
1650{
1651}
1652
1653#ifdef COMBINED 1641#ifdef COMBINED
1654#define thegame samegame 1642#define thegame samegame
1655#endif 1643#endif
@@ -1669,14 +1657,16 @@ const struct game thegame = {
1669 new_game, 1657 new_game,
1670 dup_game, 1658 dup_game,
1671 free_game, 1659 free_game,
1672 false, solve_game, 1660 false, NULL, /* solve */
1673 true, game_can_format_as_text_now, game_text_format, 1661 true, game_can_format_as_text_now, game_text_format,
1662 NULL, NULL, /* get_prefs, set_prefs */
1674 new_ui, 1663 new_ui,
1675 free_ui, 1664 free_ui,
1676 encode_ui, 1665 NULL, /* encode_ui */
1677 decode_ui, 1666 NULL, /* decode_ui */
1678 NULL, /* game_request_keys */ 1667 NULL, /* game_request_keys */
1679 game_changed_state, 1668 game_changed_state,
1669 current_key_label,
1680 interpret_move, 1670 interpret_move,
1681 execute_move, 1671 execute_move,
1682 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 1672 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -1688,8 +1678,8 @@ const struct game thegame = {
1688 game_flash_length, 1678 game_flash_length,
1689 game_get_cursor_location, 1679 game_get_cursor_location,
1690 game_status, 1680 game_status,
1691 false, false, game_print_size, game_print, 1681 false, false, NULL, NULL, /* print_size, print */
1692 true, /* wants_statusbar */ 1682 true, /* wants_statusbar */
1693 false, game_timing_state, 1683 false, NULL, /* timing_state */
1694 0, /* flags */ 1684 0, /* flags */
1695}; 1685};