X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/6f2d8d7c70f6bbf8bce982ced1fa879e967afbbf..daaccee83ed942715e01ce256cd94340605bf330:/twiddle.c diff --git a/twiddle.c b/twiddle.c index e296f48..589dbe6 100644 --- a/twiddle.c +++ b/twiddle.c @@ -46,6 +46,8 @@ struct game_state { int orientable; int *grid; int completed; + int just_used_solve; /* used to suppress undo animation */ + int used_solve; /* used to suppress completion flash */ int movecount; int lastx, lasty, lastr; /* coordinates of last rotation */ }; @@ -315,7 +317,7 @@ static char *new_game_seed(game_params *params, random_state *rs, * and simply shuffle the grid by making a long sequence of * randomly chosen moves. */ - total_moves = w*h*n*n*2; + total_moves = w*h*n*n*2 + random_upto(rs, 1); for (i = 0; i < total_moves; i++) { int x, y; @@ -359,7 +361,7 @@ static char *new_game_seed(game_params *params, random_state *rs, return ret; } -void game_free_aux_info(game_aux_info *aux) +static void game_free_aux_info(game_aux_info *aux) { assert(!"Shouldn't happen"); } @@ -406,6 +408,7 @@ static game_state *new_game(game_params *params, char *seed) state->n = n; state->orientable = params->orientable; state->completed = 0; + state->used_solve = state->just_used_solve = FALSE; state->movecount = 0; state->lastx = state->lasty = state->lastr = -1; @@ -445,6 +448,8 @@ static game_state *dup_game(game_state *state) ret->lastx = state->lastx; ret->lasty = state->lasty; ret->lastr = state->lastr; + ret->used_solve = state->used_solve; + ret->just_used_solve = state->just_used_solve; ret->grid = snewn(ret->w * ret->h, int); memcpy(ret->grid, state->grid, ret->w * ret->h * sizeof(int)); @@ -458,6 +463,40 @@ static void free_game(game_state *state) sfree(state); } +static int compare_int(const void *av, const void *bv) +{ + const int *a = (const int *)av; + const int *b = (const int *)bv; + if (*a < *b) + return -1; + else if (*a > *b) + return +1; + else + return 0; +} + +static game_state *solve_game(game_state *state, game_aux_info *aux, + char **error) +{ + game_state *ret = dup_game(state); + int i; + + /* + * Simply replace the grid with a solved one. For this game, + * this isn't a useful operation for actually telling the user + * what they should have done, but it is useful for + * conveniently being able to get hold of a clean state from + * which to practise manoeuvres. + */ + qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int); + for (i = 0; i < ret->w*ret->h; i++) + ret->grid[i] &= ~3; + ret->used_solve = ret->just_used_solve = TRUE; + ret->completed = ret->movecount = 1; + + return ret; +} + static char *game_text_format(game_state *state) { char *ret, *p, buf[80]; @@ -482,7 +521,7 @@ static char *game_text_format(game_state *state) */ maxlen = state->h * state->w * (col+o+1); - ret = snewn(maxlen, char); + ret = snewn(maxlen+1, char); p = ret; for (y = 0; y < state->h; y++) { @@ -538,6 +577,7 @@ static game_state *make_move(game_state *from, game_ui *ui, int x, int y, * This is a valid move. Make it. */ ret = dup_game(from); + ret->just_used_solve = FALSE; /* zero this in a hurry */ ret->movecount++; dir = (button == LEFT_BUTTON ? 1 : -1); do_rotate(ret->grid, w, h, n, ret->orientable, x, y, dir); @@ -653,6 +693,16 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int coords[8]; char str[40]; + /* + * If we've been passed a rotation region but we're drawing a + * tile which is outside it, we must draw it normally. This can + * occur if we're cleaning up after a completion flash while a + * new move is also being made. + */ + if (rot && (x < rot->cx || y < rot->cy || + x >= rot->cx+rot->cw || y >= rot->cy+rot->ch)) + rot = NULL; + if (rot) clip(fe, rot->cx, rot->cy, rot->cw, rot->ch); @@ -723,7 +773,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, } /* - * Next, the colour bars for orientation. + * Next, the triangles for orientation. */ if (state->orientable) { int xdx, xdy, ydx, ydy; @@ -822,13 +872,18 @@ static int highlight_colour(float angle) static float game_anim_length(game_state *oldstate, game_state *newstate, int dir) { - return ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1); + if ((dir > 0 && newstate->just_used_solve) || + (dir < 0 && oldstate->just_used_solve)) + return 0.0F; + else + return ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1); } static float game_flash_length(game_state *oldstate, game_state *newstate, int dir) { - if (!oldstate->completed && newstate->completed) + if (!oldstate->completed && newstate->completed && + !oldstate->used_solve && !newstate->used_solve) return 2 * FLASH_FRAME; else return 0.0F; @@ -963,9 +1018,13 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, if (oldstate) state = oldstate; - sprintf(statusbuf, "%sMoves: %d", - (state->completed ? "COMPLETED! " : ""), - (state->completed ? state->completed : state->movecount)); + if (state->used_solve) + sprintf(statusbuf, "Moves since auto-solve: %d", + state->movecount - state->completed); + else + sprintf(statusbuf, "%sMoves: %d", + (state->completed ? "COMPLETED! " : ""), + (state->completed ? state->completed : state->movecount)); status_bar(fe, statusbuf); } @@ -996,6 +1055,7 @@ const struct game thegame = { new_game, dup_game, free_game, + TRUE, solve_game, TRUE, game_text_format, new_ui, free_ui,