diff options
Diffstat (limited to 'apps/plugins/puzzles/src/solo.c')
-rw-r--r-- | apps/plugins/puzzles/src/solo.c | 197 |
1 files changed, 126 insertions, 71 deletions
diff --git a/apps/plugins/puzzles/src/solo.c b/apps/plugins/puzzles/src/solo.c index 49753f41dc..2445501f57 100644 --- a/apps/plugins/puzzles/src/solo.c +++ b/apps/plugins/puzzles/src/solo.c | |||
@@ -20,7 +20,7 @@ | |||
20 | * + while I'm revamping this area, filling in the _last_ | 20 | * + while I'm revamping this area, filling in the _last_ |
21 | * number in a nearly-full row or column should certainly be | 21 | * number in a nearly-full row or column should certainly be |
22 | * permitted even at the lowest difficulty level. | 22 | * permitted even at the lowest difficulty level. |
23 | * + also Owen noticed that `Basic' grids requiring numeric | 23 | * + also Alex noticed that `Basic' grids requiring numeric |
24 | * elimination are actually very hard, so I wonder if a | 24 | * elimination are actually very hard, so I wonder if a |
25 | * difficulty gradation between that and positional- | 25 | * difficulty gradation between that and positional- |
26 | * elimination-only might be in order | 26 | * elimination-only might be in order |
@@ -87,11 +87,15 @@ | |||
87 | #include <string.h> | 87 | #include <string.h> |
88 | #include <assert.h> | 88 | #include <assert.h> |
89 | #include <ctype.h> | 89 | #include <ctype.h> |
90 | #include <math.h> | 90 | #ifdef NO_TGMATH_H |
91 | # include <math.h> | ||
92 | #else | ||
93 | # include <tgmath.h> | ||
94 | #endif | ||
91 | 95 | ||
92 | #ifdef STANDALONE_SOLVER | 96 | #ifdef STANDALONE_SOLVER |
93 | #include <stdarg.h> | 97 | #include <stdarg.h> |
94 | int solver_show_working, solver_recurse_depth; | 98 | static int solver_show_working, solver_recurse_depth; |
95 | #endif | 99 | #endif |
96 | 100 | ||
97 | #include "puzzles.h" | 101 | #include "puzzles.h" |
@@ -2638,6 +2642,7 @@ static void solver(int cr, struct block_structure *blocks, | |||
2638 | sfree(usage->row); | 2642 | sfree(usage->row); |
2639 | sfree(usage->col); | 2643 | sfree(usage->col); |
2640 | sfree(usage->blk); | 2644 | sfree(usage->blk); |
2645 | sfree(usage->diag); | ||
2641 | if (usage->kblocks) { | 2646 | if (usage->kblocks) { |
2642 | free_block_structure(usage->kblocks); | 2647 | free_block_structure(usage->kblocks); |
2643 | free_block_structure(usage->extra_cages); | 2648 | free_block_structure(usage->extra_cages); |
@@ -2969,6 +2974,7 @@ static bool gridgen(int cr, struct block_structure *blocks, | |||
2969 | sfree(usage->blk); | 2974 | sfree(usage->blk); |
2970 | sfree(usage->col); | 2975 | sfree(usage->col); |
2971 | sfree(usage->row); | 2976 | sfree(usage->row); |
2977 | sfree(usage->diag); | ||
2972 | sfree(usage); | 2978 | sfree(usage); |
2973 | 2979 | ||
2974 | return ret; | 2980 | return ret; |
@@ -3222,7 +3228,7 @@ static char *encode_solve_move(int cr, digit *grid) | |||
3222 | return ret; | 3228 | return ret; |
3223 | } | 3229 | } |
3224 | 3230 | ||
3225 | static void dsf_to_blocks(int *dsf, struct block_structure *blocks, | 3231 | static void dsf_to_blocks(DSF *dsf, struct block_structure *blocks, |
3226 | int min_expected, int max_expected) | 3232 | int min_expected, int max_expected) |
3227 | { | 3233 | { |
3228 | int cr = blocks->c * blocks->r, area = cr * cr; | 3234 | int cr = blocks->c * blocks->r, area = cr * cr; |
@@ -3654,10 +3660,11 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
3654 | * the puzzle size: all 2x2 puzzles appear to be Trivial | 3660 | * the puzzle size: all 2x2 puzzles appear to be Trivial |
3655 | * (DIFF_BLOCK) so we cannot hold out for even a Basic | 3661 | * (DIFF_BLOCK) so we cannot hold out for even a Basic |
3656 | * (DIFF_SIMPLE) one. | 3662 | * (DIFF_SIMPLE) one. |
3663 | * Jigsaw puzzles of size 2 and 3 are also all trivial. | ||
3657 | */ | 3664 | */ |
3658 | dlev.maxdiff = params->diff; | 3665 | dlev.maxdiff = params->diff; |
3659 | dlev.maxkdiff = params->kdiff; | 3666 | dlev.maxkdiff = params->kdiff; |
3660 | if (c == 2 && r == 2) | 3667 | if ((c == 2 && r == 2) || (r == 1 && c < 4)) |
3661 | dlev.maxdiff = DIFF_BLOCK; | 3668 | dlev.maxdiff = DIFF_BLOCK; |
3662 | 3669 | ||
3663 | grid = snewn(area, digit); | 3670 | grid = snewn(area, digit); |
@@ -3684,11 +3691,11 @@ static char *new_game_desc(const game_params *params, random_state *rs, | |||
3684 | * constructing the block structure. | 3691 | * constructing the block structure. |
3685 | */ | 3692 | */ |
3686 | if (r == 1) { /* jigsaw mode */ | 3693 | if (r == 1) { /* jigsaw mode */ |
3687 | int *dsf = divvy_rectangle(cr, cr, cr, rs); | 3694 | DSF *dsf = divvy_rectangle(cr, cr, cr, rs); |
3688 | 3695 | ||
3689 | dsf_to_blocks (dsf, blocks, cr, cr); | 3696 | dsf_to_blocks (dsf, blocks, cr, cr); |
3690 | 3697 | ||
3691 | sfree(dsf); | 3698 | dsf_free(dsf); |
3692 | } else { /* basic Sudoku mode */ | 3699 | } else { /* basic Sudoku mode */ |
3693 | for (y = 0; y < cr; y++) | 3700 | for (y = 0; y < cr; y++) |
3694 | for (x = 0; x < cr; x++) | 3701 | for (x = 0; x < cr; x++) |
@@ -3903,14 +3910,14 @@ static const char *spec_to_grid(const char *desc, digit *grid, int area) | |||
3903 | * end of the block spec, and return an error string or NULL if everything | 3910 | * end of the block spec, and return an error string or NULL if everything |
3904 | * is OK. The DSF is stored in *PDSF. | 3911 | * is OK. The DSF is stored in *PDSF. |
3905 | */ | 3912 | */ |
3906 | static const char *spec_to_dsf(const char **pdesc, int **pdsf, | 3913 | static const char *spec_to_dsf(const char **pdesc, DSF **pdsf, |
3907 | int cr, int area) | 3914 | int cr, int area) |
3908 | { | 3915 | { |
3909 | const char *desc = *pdesc; | 3916 | const char *desc = *pdesc; |
3910 | int pos = 0; | 3917 | int pos = 0; |
3911 | int *dsf; | 3918 | DSF *dsf; |
3912 | 3919 | ||
3913 | *pdsf = dsf = snew_dsf(area); | 3920 | *pdsf = dsf = dsf_new(area); |
3914 | 3921 | ||
3915 | while (*desc && *desc != ',') { | 3922 | while (*desc && *desc != ',') { |
3916 | int c; | 3923 | int c; |
@@ -3921,7 +3928,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf, | |||
3921 | else if (*desc >= 'a' && *desc <= 'z') | 3928 | else if (*desc >= 'a' && *desc <= 'z') |
3922 | c = *desc - 'a' + 1; | 3929 | c = *desc - 'a' + 1; |
3923 | else { | 3930 | else { |
3924 | sfree(dsf); | 3931 | dsf_free(dsf); |
3925 | return "Invalid character in game description"; | 3932 | return "Invalid character in game description"; |
3926 | } | 3933 | } |
3927 | desc++; | 3934 | desc++; |
@@ -3936,7 +3943,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf, | |||
3936 | * side of it. | 3943 | * side of it. |
3937 | */ | 3944 | */ |
3938 | if (pos >= 2*cr*(cr-1)) { | 3945 | if (pos >= 2*cr*(cr-1)) { |
3939 | sfree(dsf); | 3946 | dsf_free(dsf); |
3940 | return "Too much data in block structure specification"; | 3947 | return "Too much data in block structure specification"; |
3941 | } | 3948 | } |
3942 | 3949 | ||
@@ -3966,7 +3973,7 @@ static const char *spec_to_dsf(const char **pdesc, int **pdsf, | |||
3966 | * edge at the end. | 3973 | * edge at the end. |
3967 | */ | 3974 | */ |
3968 | if (pos != 2*cr*(cr-1)+1) { | 3975 | if (pos != 2*cr*(cr-1)+1) { |
3969 | sfree(dsf); | 3976 | dsf_free(dsf); |
3970 | return "Not enough data in block structure specification"; | 3977 | return "Not enough data in block structure specification"; |
3971 | } | 3978 | } |
3972 | 3979 | ||
@@ -4008,7 +4015,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area, | |||
4008 | int min_nr_squares, int max_nr_squares) | 4015 | int min_nr_squares, int max_nr_squares) |
4009 | { | 4016 | { |
4010 | const char *err; | 4017 | const char *err; |
4011 | int *dsf; | 4018 | DSF *dsf; |
4012 | 4019 | ||
4013 | err = spec_to_dsf(pdesc, &dsf, cr, area); | 4020 | err = spec_to_dsf(pdesc, &dsf, cr, area); |
4014 | if (err) { | 4021 | if (err) { |
@@ -4037,7 +4044,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area, | |||
4037 | if (canons[c] == j) { | 4044 | if (canons[c] == j) { |
4038 | counts[c]++; | 4045 | counts[c]++; |
4039 | if (counts[c] > max_nr_squares) { | 4046 | if (counts[c] > max_nr_squares) { |
4040 | sfree(dsf); | 4047 | dsf_free(dsf); |
4041 | sfree(canons); | 4048 | sfree(canons); |
4042 | sfree(counts); | 4049 | sfree(counts); |
4043 | return "A jigsaw block is too big"; | 4050 | return "A jigsaw block is too big"; |
@@ -4047,7 +4054,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area, | |||
4047 | 4054 | ||
4048 | if (c == ncanons) { | 4055 | if (c == ncanons) { |
4049 | if (ncanons >= max_nr_blocks) { | 4056 | if (ncanons >= max_nr_blocks) { |
4050 | sfree(dsf); | 4057 | dsf_free(dsf); |
4051 | sfree(canons); | 4058 | sfree(canons); |
4052 | sfree(counts); | 4059 | sfree(counts); |
4053 | return "Too many distinct jigsaw blocks"; | 4060 | return "Too many distinct jigsaw blocks"; |
@@ -4059,14 +4066,14 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area, | |||
4059 | } | 4066 | } |
4060 | 4067 | ||
4061 | if (ncanons < min_nr_blocks) { | 4068 | if (ncanons < min_nr_blocks) { |
4062 | sfree(dsf); | 4069 | dsf_free(dsf); |
4063 | sfree(canons); | 4070 | sfree(canons); |
4064 | sfree(counts); | 4071 | sfree(counts); |
4065 | return "Not enough distinct jigsaw blocks"; | 4072 | return "Not enough distinct jigsaw blocks"; |
4066 | } | 4073 | } |
4067 | for (c = 0; c < ncanons; c++) { | 4074 | for (c = 0; c < ncanons; c++) { |
4068 | if (counts[c] < min_nr_squares) { | 4075 | if (counts[c] < min_nr_squares) { |
4069 | sfree(dsf); | 4076 | dsf_free(dsf); |
4070 | sfree(canons); | 4077 | sfree(canons); |
4071 | sfree(counts); | 4078 | sfree(counts); |
4072 | return "A jigsaw block is too small"; | 4079 | return "A jigsaw block is too small"; |
@@ -4076,7 +4083,7 @@ static const char *validate_block_desc(const char **pdesc, int cr, int area, | |||
4076 | sfree(counts); | 4083 | sfree(counts); |
4077 | } | 4084 | } |
4078 | 4085 | ||
4079 | sfree(dsf); | 4086 | dsf_free(dsf); |
4080 | return NULL; | 4087 | return NULL; |
4081 | } | 4088 | } |
4082 | 4089 | ||
@@ -4161,13 +4168,13 @@ static game_state *new_game(midend *me, const game_params *params, | |||
4161 | 4168 | ||
4162 | if (r == 1) { | 4169 | if (r == 1) { |
4163 | const char *err; | 4170 | const char *err; |
4164 | int *dsf; | 4171 | DSF *dsf; |
4165 | assert(*desc == ','); | 4172 | assert(*desc == ','); |
4166 | desc++; | 4173 | desc++; |
4167 | err = spec_to_dsf(&desc, &dsf, cr, area); | 4174 | err = spec_to_dsf(&desc, &dsf, cr, area); |
4168 | assert(err == NULL); | 4175 | assert(err == NULL); |
4169 | dsf_to_blocks(dsf, state->blocks, cr, cr); | 4176 | dsf_to_blocks(dsf, state->blocks, cr, cr); |
4170 | sfree(dsf); | 4177 | dsf_free(dsf); |
4171 | } else { | 4178 | } else { |
4172 | int x, y; | 4179 | int x, y; |
4173 | 4180 | ||
@@ -4179,13 +4186,13 @@ static game_state *new_game(midend *me, const game_params *params, | |||
4179 | 4186 | ||
4180 | if (params->killer) { | 4187 | if (params->killer) { |
4181 | const char *err; | 4188 | const char *err; |
4182 | int *dsf; | 4189 | DSF *dsf; |
4183 | assert(*desc == ','); | 4190 | assert(*desc == ','); |
4184 | desc++; | 4191 | desc++; |
4185 | err = spec_to_dsf(&desc, &dsf, cr, area); | 4192 | err = spec_to_dsf(&desc, &dsf, cr, area); |
4186 | assert(err == NULL); | 4193 | assert(err == NULL); |
4187 | dsf_to_blocks(dsf, state->kblocks, cr, area); | 4194 | dsf_to_blocks(dsf, state->kblocks, cr, area); |
4188 | sfree(dsf); | 4195 | dsf_free(dsf); |
4189 | make_blocks_from_whichblock(state->kblocks); | 4196 | make_blocks_from_whichblock(state->kblocks); |
4190 | 4197 | ||
4191 | assert(*desc == ','); | 4198 | assert(*desc == ','); |
@@ -4550,6 +4557,17 @@ struct game_ui { | |||
4550 | * allowed on immutable squares. | 4557 | * allowed on immutable squares. |
4551 | */ | 4558 | */ |
4552 | bool hcursor; | 4559 | bool hcursor; |
4560 | |||
4561 | /* | ||
4562 | * User preference option: if the user right-clicks in a square | ||
4563 | * and presses a number or letter key to add/remove a pencil mark, | ||
4564 | * do we hide the mouse highlight again afterwards? | ||
4565 | * | ||
4566 | * Historically our answer was yes. The Android port prefers no. | ||
4567 | * There are advantages both ways, depending how much you dislike | ||
4568 | * the highlight cluttering your view. So it's a preference. | ||
4569 | */ | ||
4570 | bool pencil_keep_highlight; | ||
4553 | }; | 4571 | }; |
4554 | 4572 | ||
4555 | static game_ui *new_ui(const game_state *state) | 4573 | static game_ui *new_ui(const game_state *state) |
@@ -4558,8 +4576,9 @@ static game_ui *new_ui(const game_state *state) | |||
4558 | 4576 | ||
4559 | ui->hx = ui->hy = 0; | 4577 | ui->hx = ui->hy = 0; |
4560 | ui->hpencil = false; | 4578 | ui->hpencil = false; |
4561 | ui->hshow = false; | 4579 | ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
4562 | ui->hcursor = false; | 4580 | |
4581 | ui->pencil_keep_highlight = false; | ||
4563 | 4582 | ||
4564 | return ui; | 4583 | return ui; |
4565 | } | 4584 | } |
@@ -4569,13 +4588,26 @@ static void free_ui(game_ui *ui) | |||
4569 | sfree(ui); | 4588 | sfree(ui); |
4570 | } | 4589 | } |
4571 | 4590 | ||
4572 | static char *encode_ui(const game_ui *ui) | 4591 | static config_item *get_prefs(game_ui *ui) |
4573 | { | 4592 | { |
4574 | return NULL; | 4593 | config_item *ret; |
4594 | |||
4595 | ret = snewn(2, config_item); | ||
4596 | |||
4597 | ret[0].name = "Keep mouse highlight after changing a pencil mark"; | ||
4598 | ret[0].kw = "pencil-keep-highlight"; | ||
4599 | ret[0].type = C_BOOLEAN; | ||
4600 | ret[0].u.boolean.bval = ui->pencil_keep_highlight; | ||
4601 | |||
4602 | ret[1].name = NULL; | ||
4603 | ret[1].type = C_END; | ||
4604 | |||
4605 | return ret; | ||
4575 | } | 4606 | } |
4576 | 4607 | ||
4577 | static void decode_ui(game_ui *ui, const char *encoding) | 4608 | static void set_prefs(game_ui *ui, const config_item *cfg) |
4578 | { | 4609 | { |
4610 | ui->pencil_keep_highlight = cfg[0].u.boolean.bval; | ||
4579 | } | 4611 | } |
4580 | 4612 | ||
4581 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 4613 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
@@ -4594,6 +4626,14 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
4594 | } | 4626 | } |
4595 | } | 4627 | } |
4596 | 4628 | ||
4629 | static const char *current_key_label(const game_ui *ui, | ||
4630 | const game_state *state, int button) | ||
4631 | { | ||
4632 | if (ui->hshow && (button == CURSOR_SELECT)) | ||
4633 | return ui->hpencil ? "Ink" : "Pencil"; | ||
4634 | return ""; | ||
4635 | } | ||
4636 | |||
4597 | struct game_drawstate { | 4637 | struct game_drawstate { |
4598 | bool started, xtype; | 4638 | bool started, xtype; |
4599 | int cr; | 4639 | int cr; |
@@ -4613,7 +4653,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
4613 | int tx, ty; | 4653 | int tx, ty; |
4614 | char buf[80]; | 4654 | char buf[80]; |
4615 | 4655 | ||
4616 | button &= ~MOD_MASK; | 4656 | button = STRIP_BUTTON_MODIFIERS(button); |
4617 | 4657 | ||
4618 | tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; | 4658 | tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1; |
4619 | ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; | 4659 | ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1; |
@@ -4632,7 +4672,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
4632 | ui->hpencil = false; | 4672 | ui->hpencil = false; |
4633 | } | 4673 | } |
4634 | ui->hcursor = false; | 4674 | ui->hcursor = false; |
4635 | return UI_UPDATE; | 4675 | return MOVE_UI_UPDATE; |
4636 | } | 4676 | } |
4637 | if (button == RIGHT_BUTTON) { | 4677 | if (button == RIGHT_BUTTON) { |
4638 | /* | 4678 | /* |
@@ -4652,20 +4692,19 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
4652 | ui->hshow = false; | 4692 | ui->hshow = false; |
4653 | } | 4693 | } |
4654 | ui->hcursor = false; | 4694 | ui->hcursor = false; |
4655 | return UI_UPDATE; | 4695 | return MOVE_UI_UPDATE; |
4656 | } | 4696 | } |
4657 | } | 4697 | } |
4658 | if (IS_CURSOR_MOVE(button)) { | 4698 | if (IS_CURSOR_MOVE(button)) { |
4659 | move_cursor(button, &ui->hx, &ui->hy, cr, cr, false); | ||
4660 | ui->hshow = true; | ||
4661 | ui->hcursor = true; | 4699 | ui->hcursor = true; |
4662 | return UI_UPDATE; | 4700 | return move_cursor(button, &ui->hx, &ui->hy, cr, cr, false, |
4701 | &ui->hshow); | ||
4663 | } | 4702 | } |
4664 | if (ui->hshow && | 4703 | if (ui->hshow && |
4665 | (button == CURSOR_SELECT)) { | 4704 | (button == CURSOR_SELECT)) { |
4666 | ui->hpencil = !ui->hpencil; | 4705 | ui->hpencil = !ui->hpencil; |
4667 | ui->hcursor = true; | 4706 | ui->hcursor = true; |
4668 | return UI_UPDATE; | 4707 | return MOVE_UI_UPDATE; |
4669 | } | 4708 | } |
4670 | 4709 | ||
4671 | if (ui->hshow && | 4710 | if (ui->hshow && |
@@ -4695,10 +4734,37 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
4695 | if (ui->hpencil && state->grid[ui->hy*cr+ui->hx]) | 4734 | if (ui->hpencil && state->grid[ui->hy*cr+ui->hx]) |
4696 | return NULL; | 4735 | return NULL; |
4697 | 4736 | ||
4737 | /* | ||
4738 | * If you ask to fill a square with what it already contains, | ||
4739 | * or blank it when it's already empty, that has no effect... | ||
4740 | */ | ||
4741 | if ((!ui->hpencil || n == 0) && state->grid[ui->hy*cr+ui->hx] == n) { | ||
4742 | bool anypencil = false; | ||
4743 | int i; | ||
4744 | for (i = 0; i < cr; i++) | ||
4745 | anypencil = anypencil || | ||
4746 | state->pencil[(ui->hy*cr+ui->hx) * cr + i]; | ||
4747 | if (!anypencil) { | ||
4748 | /* ... expect to remove the cursor in mouse mode. */ | ||
4749 | if (!ui->hcursor) { | ||
4750 | ui->hshow = false; | ||
4751 | return MOVE_UI_UPDATE; | ||
4752 | } | ||
4753 | return NULL; | ||
4754 | } | ||
4755 | } | ||
4756 | |||
4698 | sprintf(buf, "%c%d,%d,%d", | 4757 | sprintf(buf, "%c%d,%d,%d", |
4699 | (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); | 4758 | (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n); |
4700 | 4759 | ||
4701 | if (!ui->hcursor) ui->hshow = false; | 4760 | /* |
4761 | * Hide the highlight after a keypress, if it was mouse- | ||
4762 | * generated. Also, don't hide it if this move has changed | ||
4763 | * pencil marks and the user preference says not to hide the | ||
4764 | * highlight in that situation. | ||
4765 | */ | ||
4766 | if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight)) | ||
4767 | ui->hshow = false; | ||
4702 | 4768 | ||
4703 | return dupstr(buf); | 4769 | return dupstr(buf); |
4704 | } | 4770 | } |
@@ -4787,7 +4853,7 @@ static game_state *execute_move(const game_state *from, const char *move) | |||
4787 | #define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) ) | 4853 | #define GETTILESIZE(cr, w) ( (double)(w-1) / (double)(cr+1) ) |
4788 | 4854 | ||
4789 | static void game_compute_size(const game_params *params, int tilesize, | 4855 | static void game_compute_size(const game_params *params, int tilesize, |
4790 | int *x, int *y) | 4856 | const game_ui *ui, int *x, int *y) |
4791 | { | 4857 | { |
4792 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | 4858 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ |
4793 | struct { int tilesize; } ads, *ds = &ads; | 4859 | struct { int tilesize; } ads, *ds = &ads; |
@@ -4920,6 +4986,18 @@ static void draw_number(drawing *dr, game_drawstate *ds, | |||
4920 | (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS : | 4986 | (ds->xtype && (ondiag0(y*cr+x) || ondiag1(y*cr+x))) ? COL_XDIAGONALS : |
4921 | COL_BACKGROUND)); | 4987 | COL_BACKGROUND)); |
4922 | 4988 | ||
4989 | /* pencil-mode highlight */ | ||
4990 | if ((hl & 15) == 2) { | ||
4991 | int coords[6]; | ||
4992 | coords[0] = cx; | ||
4993 | coords[1] = cy; | ||
4994 | coords[2] = cx+cw/2; | ||
4995 | coords[3] = cy; | ||
4996 | coords[4] = cx; | ||
4997 | coords[5] = cy+ch/2; | ||
4998 | draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); | ||
4999 | } | ||
5000 | |||
4923 | /* | 5001 | /* |
4924 | * Draw the corners of thick lines in corner-adjacent squares, | 5002 | * Draw the corners of thick lines in corner-adjacent squares, |
4925 | * which jut into this square by one pixel. | 5003 | * which jut into this square by one pixel. |
@@ -4933,18 +5011,6 @@ static void draw_number(drawing *dr, game_drawstate *ds, | |||
4933 | if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1]) | 5011 | if (x+1 < cr && y+1 < cr && state->blocks->whichblock[y*cr+x] != state->blocks->whichblock[(y+1)*cr+x+1]) |
4934 | draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); | 5012 | draw_rect(dr, tx+TILE_SIZE-1-2*GRIDEXTRA, ty+TILE_SIZE-1-2*GRIDEXTRA, GRIDEXTRA, GRIDEXTRA, COL_GRID); |
4935 | 5013 | ||
4936 | /* pencil-mode highlight */ | ||
4937 | if ((hl & 15) == 2) { | ||
4938 | int coords[6]; | ||
4939 | coords[0] = cx; | ||
4940 | coords[1] = cy; | ||
4941 | coords[2] = cx+cw/2; | ||
4942 | coords[3] = cy; | ||
4943 | coords[4] = cx; | ||
4944 | coords[5] = cy+ch/2; | ||
4945 | draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT); | ||
4946 | } | ||
4947 | |||
4948 | if (state->kblocks) { | 5014 | if (state->kblocks) { |
4949 | int t = GRIDEXTRA * 3; | 5015 | int t = GRIDEXTRA * 3; |
4950 | int kcx, kcy, kcw, kch; | 5016 | int kcx, kcy, kcw, kch; |
@@ -5104,7 +5170,7 @@ static void draw_number(drawing *dr, game_drawstate *ds, | |||
5104 | fw = (pr - pl) / (float)pw; | 5170 | fw = (pr - pl) / (float)pw; |
5105 | fh = (pb - pt) / (float)ph; | 5171 | fh = (pb - pt) / (float)ph; |
5106 | fs = min(fw, fh); | 5172 | fs = min(fw, fh); |
5107 | if (fs > bestsize) { | 5173 | if (fs >= bestsize) { |
5108 | bestsize = fs; | 5174 | bestsize = fs; |
5109 | pbest = pw; | 5175 | pbest = pw; |
5110 | } | 5176 | } |
@@ -5175,14 +5241,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
5175 | 5241 | ||
5176 | if (!ds->started) { | 5242 | if (!ds->started) { |
5177 | /* | 5243 | /* |
5178 | * The initial contents of the window are not guaranteed | ||
5179 | * and can vary with front ends. To be on the safe side, | ||
5180 | * all games should start by drawing a big | ||
5181 | * background-colour rectangle covering the whole window. | ||
5182 | */ | ||
5183 | draw_rect(dr, 0, 0, SIZE(cr), SIZE(cr), COL_BACKGROUND); | ||
5184 | |||
5185 | /* | ||
5186 | * Draw the grid. We draw it as a big thick rectangle of | 5244 | * Draw the grid. We draw it as a big thick rectangle of |
5187 | * COL_GRID initially; individual calls to draw_number() | 5245 | * COL_GRID initially; individual calls to draw_number() |
5188 | * will poke the right-shaped holes in it. | 5246 | * will poke the right-shaped holes in it. |
@@ -5315,14 +5373,8 @@ static int game_status(const game_state *state) | |||
5315 | return state->completed ? +1 : 0; | 5373 | return state->completed ? +1 : 0; |
5316 | } | 5374 | } |
5317 | 5375 | ||
5318 | static bool game_timing_state(const game_state *state, game_ui *ui) | 5376 | static void game_print_size(const game_params *params, const game_ui *ui, |
5319 | { | 5377 | float *x, float *y) |
5320 | if (state->completed) | ||
5321 | return false; | ||
5322 | return true; | ||
5323 | } | ||
5324 | |||
5325 | static void game_print_size(const game_params *params, float *x, float *y) | ||
5326 | { | 5378 | { |
5327 | int pw, ph; | 5379 | int pw, ph; |
5328 | 5380 | ||
@@ -5331,7 +5383,7 @@ static void game_print_size(const game_params *params, float *x, float *y) | |||
5331 | * for this game, because players will want to jot down no end | 5383 | * for this game, because players will want to jot down no end |
5332 | * of pencil marks in the squares. | 5384 | * of pencil marks in the squares. |
5333 | */ | 5385 | */ |
5334 | game_compute_size(params, 900, &pw, &ph); | 5386 | game_compute_size(params, 900, ui, &pw, &ph); |
5335 | *x = pw / 100.0F; | 5387 | *x = pw / 100.0F; |
5336 | *y = ph / 100.0F; | 5388 | *y = ph / 100.0F; |
5337 | } | 5389 | } |
@@ -5505,7 +5557,8 @@ static void outline_block_structure(drawing *dr, game_drawstate *ds, | |||
5505 | sfree(coords); | 5557 | sfree(coords); |
5506 | } | 5558 | } |
5507 | 5559 | ||
5508 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 5560 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
5561 | int tilesize) | ||
5509 | { | 5562 | { |
5510 | int cr = state->cr; | 5563 | int cr = state->cr; |
5511 | int ink = print_mono_colour(dr, 0); | 5564 | int ink = print_mono_colour(dr, 0); |
@@ -5620,12 +5673,14 @@ const struct game thegame = { | |||
5620 | free_game, | 5673 | free_game, |
5621 | true, solve_game, | 5674 | true, solve_game, |
5622 | true, game_can_format_as_text_now, game_text_format, | 5675 | true, game_can_format_as_text_now, game_text_format, |
5676 | get_prefs, set_prefs, | ||
5623 | new_ui, | 5677 | new_ui, |
5624 | free_ui, | 5678 | free_ui, |
5625 | encode_ui, | 5679 | NULL, /* encode_ui */ |
5626 | decode_ui, | 5680 | NULL, /* decode_ui */ |
5627 | game_request_keys, | 5681 | game_request_keys, |
5628 | game_changed_state, | 5682 | game_changed_state, |
5683 | current_key_label, | ||
5629 | interpret_move, | 5684 | interpret_move, |
5630 | execute_move, | 5685 | execute_move, |
5631 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 5686 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -5639,7 +5694,7 @@ const struct game thegame = { | |||
5639 | game_status, | 5694 | game_status, |
5640 | true, false, game_print_size, game_print, | 5695 | true, false, game_print_size, game_print, |
5641 | false, /* wants_statusbar */ | 5696 | false, /* wants_statusbar */ |
5642 | false, game_timing_state, | 5697 | false, NULL, /* timing_state */ |
5643 | REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ | 5698 | REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ |
5644 | }; | 5699 | }; |
5645 | 5700 | ||