diff options
Diffstat (limited to 'apps/plugins/puzzles/src/blackbox.c')
-rw-r--r-- | apps/plugins/puzzles/src/blackbox.c | 115 |
1 files changed, 66 insertions, 49 deletions
diff --git a/apps/plugins/puzzles/src/blackbox.c b/apps/plugins/puzzles/src/blackbox.c index a9c1f88261..ab8e382d0a 100644 --- a/apps/plugins/puzzles/src/blackbox.c +++ b/apps/plugins/puzzles/src/blackbox.c | |||
@@ -7,7 +7,11 @@ | |||
7 | #include <string.h> | 7 | #include <string.h> |
8 | #include <assert.h> | 8 | #include <assert.h> |
9 | #include <ctype.h> | 9 | #include <ctype.h> |
10 | #include <math.h> | 10 | #ifdef NO_TGMATH_H |
11 | # include <math.h> | ||
12 | #else | ||
13 | # include <tgmath.h> | ||
14 | #endif | ||
11 | 15 | ||
12 | #include "puzzles.h" | 16 | #include "puzzles.h" |
13 | 17 | ||
@@ -192,12 +196,14 @@ static const char *validate_params(const game_params *params, bool full) | |||
192 | * types, and could be worked around if required. */ | 196 | * types, and could be worked around if required. */ |
193 | if (params->w > 255 || params->h > 255) | 197 | if (params->w > 255 || params->h > 255) |
194 | return "Widths and heights greater than 255 are not supported"; | 198 | return "Widths and heights greater than 255 are not supported"; |
199 | if (params->minballs < 0) | ||
200 | return "Negative number of balls"; | ||
201 | if (params->minballs < 1) | ||
202 | return "Number of balls must be at least one"; | ||
195 | if (params->minballs > params->maxballs) | 203 | if (params->minballs > params->maxballs) |
196 | return "Minimum number of balls may not be greater than maximum"; | 204 | return "Minimum number of balls may not be greater than maximum"; |
197 | if (params->minballs >= params->w * params->h) | 205 | if (params->minballs >= params->w * params->h) |
198 | return "Too many balls to fit in grid"; | 206 | return "Too many balls to fit in grid"; |
199 | if (params->minballs < 1) | ||
200 | return "Number of balls must be at least one"; | ||
201 | return NULL; | 207 | return NULL; |
202 | } | 208 | } |
203 | 209 | ||
@@ -307,7 +313,7 @@ struct game_state { | |||
307 | 313 | ||
308 | #define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)]) | 314 | #define GRID(s,x,y) ((s)->grid[(y)*((s)->w+2) + (x)]) |
309 | 315 | ||
310 | #define RANGECHECK(s,x) ((x) >= 0 && (x) <= (s)->nlasers) | 316 | #define RANGECHECK(s,x) ((x) >= 0 && (x) < (s)->nlasers) |
311 | 317 | ||
312 | /* specify numbers because they must match array indexes. */ | 318 | /* specify numbers because they must match array indexes. */ |
313 | enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 }; | 319 | enum { DIR_UP = 0, DIR_RIGHT = 1, DIR_DOWN = 2, DIR_LEFT = 3 }; |
@@ -468,16 +474,6 @@ static char *solve_game(const game_state *state, const game_state *currstate, | |||
468 | return dupstr("S"); | 474 | return dupstr("S"); |
469 | } | 475 | } |
470 | 476 | ||
471 | static bool game_can_format_as_text_now(const game_params *params) | ||
472 | { | ||
473 | return true; | ||
474 | } | ||
475 | |||
476 | static char *game_text_format(const game_state *state) | ||
477 | { | ||
478 | return NULL; | ||
479 | } | ||
480 | |||
481 | struct game_ui { | 477 | struct game_ui { |
482 | int flash_laserno; | 478 | int flash_laserno; |
483 | int errors; | 479 | int errors; |
@@ -495,7 +491,7 @@ static game_ui *new_ui(const game_state *state) | |||
495 | ui->newmove = false; | 491 | ui->newmove = false; |
496 | 492 | ||
497 | ui->cur_x = ui->cur_y = 1; | 493 | ui->cur_x = ui->cur_y = 1; |
498 | ui->cur_visible = false; | 494 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
499 | 495 | ||
500 | ui->flash_laser = 0; | 496 | ui->flash_laser = 0; |
501 | 497 | ||
@@ -517,7 +513,8 @@ static char *encode_ui(const game_ui *ui) | |||
517 | return dupstr(buf); | 513 | return dupstr(buf); |
518 | } | 514 | } |
519 | 515 | ||
520 | static void decode_ui(game_ui *ui, const char *encoding) | 516 | static void decode_ui(game_ui *ui, const char *encoding, |
517 | const game_state *state) | ||
521 | { | 518 | { |
522 | sscanf(encoding, "E%d", &ui->errors); | 519 | sscanf(encoding, "E%d", &ui->errors); |
523 | } | 520 | } |
@@ -534,6 +531,41 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
534 | ui->newmove = false; | 531 | ui->newmove = false; |
535 | } | 532 | } |
536 | 533 | ||
534 | static const char *current_key_label(const game_ui *ui, | ||
535 | const game_state *state, int button) | ||
536 | { | ||
537 | if (IS_CURSOR_SELECT(button) && ui->cur_visible && !state->reveal) { | ||
538 | int gx = ui->cur_x, gy = ui->cur_y, rangeno = -1; | ||
539 | if (gx == 0 && gy == 0 && button == CURSOR_SELECT) return "Check"; | ||
540 | if (gx >= 1 && gx <= state->w && gy >= 1 && gy <= state->h) { | ||
541 | /* Cursor somewhere in the arena. */ | ||
542 | if (button == CURSOR_SELECT && !(GRID(state, gx,gy) & BALL_LOCK)) | ||
543 | return (GRID(state, gx, gy) & BALL_GUESS) ? "Clear" : "Ball"; | ||
544 | if (button == CURSOR_SELECT2) | ||
545 | return (GRID(state, gx, gy) & BALL_LOCK) ? "Unlock" : "Lock"; | ||
546 | } | ||
547 | if (grid2range(state, gx, gy, &rangeno)) { | ||
548 | if (button == CURSOR_SELECT && | ||
549 | state->exits[rangeno] == LASER_EMPTY) | ||
550 | return "Fire"; | ||
551 | if (button == CURSOR_SELECT2) { | ||
552 | int n = 0; | ||
553 | /* Row or column lock or unlock. */ | ||
554 | if (gy == 0 || gy > state->h) { /* Column lock */ | ||
555 | for (gy = 1; gy <= state->h; gy++) | ||
556 | n += !!(GRID(state, gx, gy) & BALL_LOCK); | ||
557 | return n > state->h/2 ? "Unlock" : "Lock"; | ||
558 | } else { /* Row lock */ | ||
559 | for (gx = 1; gx <= state->w; gx++) | ||
560 | n += !!(GRID(state, gx, gy) & BALL_LOCK); | ||
561 | return n > state->w/2 ? "Unlock" : "Lock"; | ||
562 | } | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | return ""; | ||
567 | } | ||
568 | |||
537 | #define OFFSET(gx,gy,o) do { \ | 569 | #define OFFSET(gx,gy,o) do { \ |
538 | int off = (4 + (o) % 4) % 4; \ | 570 | int off = (4 + (o) % 4) % 4; \ |
539 | (gx) += offsets[off].x; \ | 571 | (gx) += offsets[off].x; \ |
@@ -875,7 +907,7 @@ done: | |||
875 | #define TILE_SIZE (ds->tilesize) | 907 | #define TILE_SIZE (ds->tilesize) |
876 | 908 | ||
877 | #define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2)) | 909 | #define TODRAW(x) ((TILE_SIZE * (x)) + (TILE_SIZE / 2)) |
878 | #define FROMDRAW(x) (((x) - (TILE_SIZE / 2)) / TILE_SIZE) | 910 | #define FROMDRAW(x) (((x) + (TILE_SIZE / 2)) / TILE_SIZE - 1) |
879 | 911 | ||
880 | #define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \ | 912 | #define CAN_REVEAL(state) ((state)->nguesses >= (state)->minballs && \ |
881 | (state)->nguesses <= (state)->maxballs && \ | 913 | (state)->nguesses <= (state)->maxballs && \ |
@@ -900,7 +932,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
900 | if (IS_CURSOR_MOVE(button)) { | 932 | if (IS_CURSOR_MOVE(button)) { |
901 | int cx = ui->cur_x, cy = ui->cur_y; | 933 | int cx = ui->cur_x, cy = ui->cur_y; |
902 | 934 | ||
903 | move_cursor(button, &cx, &cy, state->w+2, state->h+2, false); | 935 | move_cursor(button, &cx, &cy, state->w+2, state->h+2, false, NULL); |
904 | if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) || | 936 | if ((cx == 0 && cy == 0 && !CAN_REVEAL(state)) || |
905 | (cx == 0 && cy == state->h+1) || | 937 | (cx == 0 && cy == state->h+1) || |
906 | (cx == state->w+1 && cy == 0) || | 938 | (cx == state->w+1 && cy == 0) || |
@@ -909,7 +941,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
909 | ui->cur_x = cx; | 941 | ui->cur_x = cx; |
910 | ui->cur_y = cy; | 942 | ui->cur_y = cy; |
911 | ui->cur_visible = true; | 943 | ui->cur_visible = true; |
912 | return UI_UPDATE; | 944 | return MOVE_UI_UPDATE; |
913 | } | 945 | } |
914 | 946 | ||
915 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { | 947 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { |
@@ -919,7 +951,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
919 | wouldflash = 1; | 951 | wouldflash = 1; |
920 | } else if (button == LEFT_RELEASE) { | 952 | } else if (button == LEFT_RELEASE) { |
921 | ui->flash_laser = 0; | 953 | ui->flash_laser = 0; |
922 | return UI_UPDATE; | 954 | return MOVE_UI_UPDATE; |
923 | } else if (IS_CURSOR_SELECT(button)) { | 955 | } else if (IS_CURSOR_SELECT(button)) { |
924 | if (ui->cur_visible) { | 956 | if (ui->cur_visible) { |
925 | gx = ui->cur_x; | 957 | gx = ui->cur_x; |
@@ -928,7 +960,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
928 | wouldflash = 2; | 960 | wouldflash = 2; |
929 | } else { | 961 | } else { |
930 | ui->cur_visible = true; | 962 | ui->cur_visible = true; |
931 | return UI_UPDATE; | 963 | return MOVE_UI_UPDATE; |
932 | } | 964 | } |
933 | /* Fix up 'button' for the below logic. */ | 965 | /* Fix up 'button' for the below logic. */ |
934 | if (button == CURSOR_SELECT2) button = RIGHT_BUTTON; | 966 | if (button == CURSOR_SELECT2) button = RIGHT_BUTTON; |
@@ -977,9 +1009,9 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
977 | return nullret; | 1009 | return nullret; |
978 | ui->flash_laserno = rangeno; | 1010 | ui->flash_laserno = rangeno; |
979 | ui->flash_laser = wouldflash; | 1011 | ui->flash_laser = wouldflash; |
980 | nullret = UI_UPDATE; | 1012 | nullret = MOVE_UI_UPDATE; |
981 | if (state->exits[rangeno] != LASER_EMPTY) | 1013 | if (state->exits[rangeno] != LASER_EMPTY) |
982 | return UI_UPDATE; | 1014 | return MOVE_UI_UPDATE; |
983 | sprintf(buf, "F%d", rangeno); | 1015 | sprintf(buf, "F%d", rangeno); |
984 | break; | 1016 | break; |
985 | 1017 | ||
@@ -1034,10 +1066,10 @@ static game_state *execute_move(const game_state *from, const char *move) | |||
1034 | 1066 | ||
1035 | case 'F': | 1067 | case 'F': |
1036 | sscanf(move+1, "%d", &rangeno); | 1068 | sscanf(move+1, "%d", &rangeno); |
1037 | if (ret->exits[rangeno] != LASER_EMPTY) | ||
1038 | goto badmove; | ||
1039 | if (!RANGECHECK(ret, rangeno)) | 1069 | if (!RANGECHECK(ret, rangeno)) |
1040 | goto badmove; | 1070 | goto badmove; |
1071 | if (ret->exits[rangeno] != LASER_EMPTY) | ||
1072 | goto badmove; | ||
1041 | fire_laser(ret, rangeno); | 1073 | fire_laser(ret, rangeno); |
1042 | break; | 1074 | break; |
1043 | 1075 | ||
@@ -1119,7 +1151,7 @@ static void game_get_cursor_location(const game_ui *ui, | |||
1119 | */ | 1151 | */ |
1120 | 1152 | ||
1121 | static void game_compute_size(const game_params *params, int tilesize, | 1153 | static void game_compute_size(const game_params *params, int tilesize, |
1122 | int *x, int *y) | 1154 | const game_ui *ui, int *x, int *y) |
1123 | { | 1155 | { |
1124 | /* Border is ts/2, to make things easier. | 1156 | /* Border is ts/2, to make things easier. |
1125 | * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles | 1157 | * Thus we have (width) + 2 (firing range*2) + 1 (border*2) tiles |
@@ -1379,10 +1411,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1379 | int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1; | 1411 | int x0 = TODRAW(0)-1, y0 = TODRAW(0)-1; |
1380 | int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2); | 1412 | int x1 = TODRAW(state->w+2), y1 = TODRAW(state->h+2); |
1381 | 1413 | ||
1382 | draw_rect(dr, 0, 0, | ||
1383 | TILE_SIZE * (state->w+3), TILE_SIZE * (state->h+3), | ||
1384 | COL_BACKGROUND); | ||
1385 | |||
1386 | /* clockwise around the outline starting at pt behind (1,1). */ | 1414 | /* clockwise around the outline starting at pt behind (1,1). */ |
1387 | draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT); | 1415 | draw_line(dr, x0+ts, y0+ts, x0+ts, y0, COL_HIGHLIGHT); |
1388 | draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT); | 1416 | draw_line(dr, x0+ts, y0, x1-ts, y0, COL_HIGHLIGHT); |
@@ -1429,14 +1457,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
1429 | int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0) | 1457 | int outline = (ui->cur_visible && ui->cur_x == 0 && ui->cur_y == 0) |
1430 | ? COL_CURSOR : COL_BALL; | 1458 | ? COL_CURSOR : COL_BALL; |
1431 | clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1); | 1459 | clip(dr, TODRAW(0)-1, TODRAW(0)-1, TILE_SIZE+1, TILE_SIZE+1); |
1432 | draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad, | 1460 | draw_circle(dr, TODRAW(0) + ds->crad-1, TODRAW(0) + ds->crad-1, ds->crad-1, |
1433 | outline, outline); | 1461 | outline, outline); |
1434 | draw_circle(dr, TODRAW(0) + ds->crad, TODRAW(0) + ds->crad, ds->crad-2, | 1462 | draw_circle(dr, TODRAW(0) + ds->crad-1, TODRAW(0) + ds->crad-1, ds->crad-3, |
1435 | COL_BUTTON, COL_BUTTON); | 1463 | COL_BUTTON, COL_BUTTON); |
1436 | unclip(dr); | 1464 | unclip(dr); |
1437 | } else { | 1465 | } else { |
1438 | draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1, | 1466 | draw_rect(dr, TODRAW(0)-1, TODRAW(0)-1, |
1439 | TILE_SIZE+1, TILE_SIZE+1, COL_BACKGROUND); | 1467 | TILE_SIZE, TILE_SIZE, COL_BACKGROUND); |
1440 | } | 1468 | } |
1441 | draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE); | 1469 | draw_update(dr, TODRAW(0), TODRAW(0), TILE_SIZE, TILE_SIZE); |
1442 | ds->reveal = state->reveal; | 1470 | ds->reveal = state->reveal; |
@@ -1509,19 +1537,6 @@ static int game_status(const game_state *state) | |||
1509 | return 0; | 1537 | return 0; |
1510 | } | 1538 | } |
1511 | 1539 | ||
1512 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
1513 | { | ||
1514 | return true; | ||
1515 | } | ||
1516 | |||
1517 | static void game_print_size(const game_params *params, float *x, float *y) | ||
1518 | { | ||
1519 | } | ||
1520 | |||
1521 | static void game_print(drawing *dr, const game_state *state, int tilesize) | ||
1522 | { | ||
1523 | } | ||
1524 | |||
1525 | #ifdef COMBINED | 1540 | #ifdef COMBINED |
1526 | #define thegame blackbox | 1541 | #define thegame blackbox |
1527 | #endif | 1542 | #endif |
@@ -1542,13 +1557,15 @@ const struct game thegame = { | |||
1542 | dup_game, | 1557 | dup_game, |
1543 | free_game, | 1558 | free_game, |
1544 | true, solve_game, | 1559 | true, solve_game, |
1545 | false, game_can_format_as_text_now, game_text_format, | 1560 | false, NULL, NULL, /* can_format_as_text_now, text_format */ |
1561 | NULL, NULL, /* get_prefs, set_prefs */ | ||
1546 | new_ui, | 1562 | new_ui, |
1547 | free_ui, | 1563 | free_ui, |
1548 | encode_ui, | 1564 | encode_ui, |
1549 | decode_ui, | 1565 | decode_ui, |
1550 | NULL, /* game_request_keys */ | 1566 | NULL, /* game_request_keys */ |
1551 | game_changed_state, | 1567 | game_changed_state, |
1568 | current_key_label, | ||
1552 | interpret_move, | 1569 | interpret_move, |
1553 | execute_move, | 1570 | execute_move, |
1554 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 1571 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -1560,9 +1577,9 @@ const struct game thegame = { | |||
1560 | game_flash_length, | 1577 | game_flash_length, |
1561 | game_get_cursor_location, | 1578 | game_get_cursor_location, |
1562 | game_status, | 1579 | game_status, |
1563 | false, false, game_print_size, game_print, | 1580 | false, false, NULL, NULL, /* print_size, print */ |
1564 | true, /* wants_statusbar */ | 1581 | true, /* wants_statusbar */ |
1565 | false, game_timing_state, | 1582 | false, NULL, /* timing_state */ |
1566 | REQUIRE_RBUTTON, /* flags */ | 1583 | REQUIRE_RBUTTON, /* flags */ |
1567 | }; | 1584 | }; |
1568 | 1585 | ||