X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/cbb5549e12c8edfd8b38b642b7b25c1fb8912c9b..4b24f58222c549b383d23ed86a958c9656218c48:/net.c?ds=inline diff --git a/net.c b/net.c index 6849dbc..c8cafc1 100644 --- a/net.c +++ b/net.c @@ -12,10 +12,6 @@ #include "puzzles.h" #include "tree234.h" -const char *const game_name = "Net"; -const char *const game_winhelp_topic = "games.net"; -const int game_can_configure = TRUE; - #define PI 3.141592653589793238462643383279502884197169399 #define MATMUL(xr,yr,m,x,y) do { \ @@ -79,10 +75,18 @@ struct game_params { float barrier_probability; }; +struct solved_game_state { + int width, height; + int refcount; + unsigned char *tiles; +}; + struct game_state { int width, height, cx, cy, wrapping, completed, last_rotate_dir; + int used_solve, just_used_solve; unsigned char *tiles; unsigned char *barriers; + struct solved_game_state *solution; }; #define OFFSET(x2,y2,x1,y1,dir,state) \ @@ -127,7 +131,7 @@ static struct xyd *new_xyd(int x, int y, int direction) /* ---------------------------------------------------------------------- * Manage game parameters. */ -game_params *default_params(void) +static game_params *default_params(void) { game_params *ret = snew(game_params); @@ -139,7 +143,7 @@ game_params *default_params(void) return ret; } -int game_fetch_preset(int i, char **name, game_params **params) +static int game_fetch_preset(int i, char **name, game_params **params) { game_params *ret; char str[80]; @@ -173,19 +177,19 @@ int game_fetch_preset(int i, char **name, game_params **params) return TRUE; } -void free_params(game_params *params) +static void free_params(game_params *params) { sfree(params); } -game_params *dup_params(game_params *params) +static game_params *dup_params(game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ return ret; } -game_params *decode_params(char const *string) +static game_params *decode_params(char const *string) { game_params *ret = default_params(); char const *p = string; @@ -207,7 +211,7 @@ game_params *decode_params(char const *string) return ret; } -char *encode_params(game_params *params) +static char *encode_params(game_params *params) { char ret[400]; int len; @@ -223,7 +227,7 @@ char *encode_params(game_params *params) return dupstr(ret); } -config_item *game_configure(game_params *params) +static config_item *game_configure(game_params *params) { config_item *ret; char buf[80]; @@ -261,7 +265,7 @@ config_item *game_configure(game_params *params) return ret; } -game_params *custom_params(config_item *cfg) +static game_params *custom_params(config_item *cfg) { game_params *ret = snew(game_params); @@ -273,7 +277,7 @@ game_params *custom_params(config_item *cfg) return ret; } -char *validate_params(game_params *params) +static char *validate_params(game_params *params) { if (params->width <= 0 && params->height <= 0) return "Width and height must both be greater than zero"; @@ -294,7 +298,8 @@ char *validate_params(game_params *params) * Randomly select a new game seed. */ -char *new_game_seed(game_params *params, random_state *rs) +static char *new_game_seed(game_params *params, random_state *rs, + game_aux_info **aux) { /* * The full description of a Net game is far too large to @@ -312,7 +317,12 @@ char *new_game_seed(game_params *params, random_state *rs) return dupstr(buf); } -char *validate_seed(game_params *params, char *seed) +static void game_free_aux_info(game_aux_info *aux) +{ + assert(!"Shouldn't happen"); +} + +static char *validate_seed(game_params *params, char *seed) { /* * Since any string at all will suffice to seed the RNG, there @@ -325,7 +335,7 @@ char *validate_seed(game_params *params, char *seed) * Construct an initial game state, given a seed and parameters. */ -game_state *new_game(game_params *params, char *seed) +static game_state *new_game(game_params *params, char *seed) { random_state *rs; game_state *state; @@ -345,7 +355,7 @@ game_state *new_game(game_params *params, char *seed) state->cy = state->height / 2; state->wrapping = params->wrapping; state->last_rotate_dir = 0; - state->completed = FALSE; + state->completed = state->used_solve = state->just_used_solve = FALSE; state->tiles = snewn(state->width * state->height, unsigned char); memset(state->tiles, 0, state->width * state->height); state->barriers = snewn(state->width * state->height, unsigned char); @@ -557,6 +567,25 @@ game_state *new_game(game_params *params, char *seed) } /* + * Save the unshuffled grid. We do this using a separate + * reference-counted structure since it's a large chunk of + * memory which we don't want to have to replicate in every + * game state while playing. + */ + { + struct solved_game_state *solution; + + solution = snew(struct solved_game_state); + solution->width = state->width; + solution->height = state->height; + solution->refcount = 1; + solution->tiles = snewn(state->width * state->height, unsigned char); + memcpy(solution->tiles, state->tiles, state->width * state->height); + + state->solution = solution; + } + + /* * Now shuffle the grid. */ for (y = 0; y < state->height; y++) { @@ -676,7 +705,7 @@ game_state *new_game(game_params *params, char *seed) return state; } -game_state *dup_game(game_state *state) +static game_state *dup_game(game_state *state) { game_state *ret; @@ -687,22 +716,63 @@ game_state *dup_game(game_state *state) ret->cy = state->cy; ret->wrapping = state->wrapping; ret->completed = state->completed; + ret->used_solve = state->used_solve; + ret->just_used_solve = state->just_used_solve; ret->last_rotate_dir = state->last_rotate_dir; ret->tiles = snewn(state->width * state->height, unsigned char); memcpy(ret->tiles, state->tiles, state->width * state->height); ret->barriers = snewn(state->width * state->height, unsigned char); memcpy(ret->barriers, state->barriers, state->width * state->height); + ret->solution = state->solution; + if (ret->solution) + ret->solution->refcount++; return ret; } -void free_game(game_state *state) +static void free_game(game_state *state) { + if (state->solution && --state->solution->refcount <= 0) { + sfree(state->solution->tiles); + sfree(state->solution); + } sfree(state->tiles); sfree(state->barriers); sfree(state); } +static game_state *solve_game(game_state *state, game_aux_info *aux, + char **error) +{ + game_state *ret; + + if (!state->solution) { + /* + * 2005-05-02: This shouldn't happen, at the time of + * writing, because Net is incapable of receiving a puzzle + * description from outside. If in future it becomes so, + * then we will have puzzles for which we don't know the + * solution. + */ + *error = "Solution not known for this puzzle"; + return NULL; + } + + assert(state->solution->width == state->width); + assert(state->solution->height == state->height); + ret = dup_game(state); + memcpy(ret->tiles, state->solution->tiles, ret->width * ret->height); + ret->used_solve = ret->just_used_solve = TRUE; + ret->completed = TRUE; + + return ret; +} + +static char *game_text_format(game_state *state) +{ + return NULL; +} + /* ---------------------------------------------------------------------- * Utility routine. */ @@ -770,7 +840,7 @@ struct game_ui { random_state *rs; /* used for jumbling */ }; -game_ui *new_ui(game_state *state) +static game_ui *new_ui(game_state *state) { void *seed; int seedsize; @@ -785,7 +855,7 @@ game_ui *new_ui(game_state *state) return ui; } -void free_ui(game_ui *ui) +static void free_ui(game_ui *ui) { random_free(ui->rs); sfree(ui); @@ -794,7 +864,8 @@ void free_ui(game_ui *ui) /* ---------------------------------------------------------------------- * Process a move. */ -game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button) +static game_state *make_move(game_state *state, game_ui *ui, + int x, int y, int button) { game_state *ret, *nullret; int tx, ty, orig; @@ -869,6 +940,7 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button) if (button == MIDDLE_BUTTON) { ret = dup_game(state); + ret->just_used_solve = FALSE; tile(ret, tx, ty) ^= LOCKED; ret->last_rotate_dir = 0; return ret; @@ -887,6 +959,7 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button) * turns anticlockwise; right button turns clockwise. */ ret = dup_game(state); + ret->just_used_solve = FALSE; orig = tile(ret, tx, ty); if (button == LEFT_BUTTON) { tile(ret, tx, ty) = A(orig); @@ -903,6 +976,7 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button) */ int jx, jy; ret = dup_game(state); + ret->just_used_solve = FALSE; for (jy = 0; jy < ret->height; jy++) { for (jx = 0; jx < ret->width; jx++) { if (!(tile(ret, jx, jy) & LOCKED)) { @@ -951,7 +1025,7 @@ struct game_drawstate { unsigned char *visible; }; -game_drawstate *game_new_drawstate(game_state *state) +static game_drawstate *game_new_drawstate(game_state *state) { game_drawstate *ds = snew(game_drawstate); @@ -964,19 +1038,19 @@ game_drawstate *game_new_drawstate(game_state *state) return ds; } -void game_free_drawstate(game_drawstate *ds) +static void game_free_drawstate(game_drawstate *ds) { sfree(ds->visible); sfree(ds); } -void game_size(game_params *params, int *x, int *y) +static void game_size(game_params *params, int *x, int *y) { *x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER; *y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER; } -float *game_colours(frontend *fe, game_state *state, int *ncolours) +static float *game_colours(frontend *fe, game_state *state, int *ncolours) { float *ret; @@ -1284,7 +1358,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile, draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER); } -void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, +static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, game_state *state, int dir, game_ui *ui, float t, float ft) { int x, y, tx, ty, frame, last_rotate_dir; @@ -1428,7 +1502,8 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, a++; sprintf(statusbuf, "%sActive: %d/%d", - (state->completed ? "COMPLETED! " : ""), a, n); + (state->used_solve ? "Auto-solved. " : + state->completed ? "COMPLETED! " : ""), a, n); status_bar(fe, statusbuf); } @@ -1436,11 +1511,19 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, sfree(active); } -float game_anim_length(game_state *oldstate, game_state *newstate, int dir) +static float game_anim_length(game_state *oldstate, + game_state *newstate, int dir) { int x, y, last_rotate_dir; /* + * Don't animate an auto-solve move. + */ + if ((dir > 0 && newstate->just_used_solve) || + (dir < 0 && oldstate->just_used_solve)) + return 0.0F; + + /* * Don't animate if last_rotate_dir is zero. */ last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir : @@ -1462,13 +1545,15 @@ float game_anim_length(game_state *oldstate, game_state *newstate, int dir) return 0.0F; } -float game_flash_length(game_state *oldstate, game_state *newstate, int dir) +static float game_flash_length(game_state *oldstate, + game_state *newstate, int dir) { /* * If the game has just been completed, we display a completion * flash. */ - if (!oldstate->completed && newstate->completed) { + if (!oldstate->completed && newstate->completed && + !oldstate->used_solve && !newstate->used_solve) { int size; size = 0; if (size < newstate->cx+1) @@ -1485,7 +1570,42 @@ float game_flash_length(game_state *oldstate, game_state *newstate, int dir) return 0.0F; } -int game_wants_statusbar(void) +static int game_wants_statusbar(void) { return TRUE; } + +#ifdef COMBINED +#define thegame net +#endif + +const struct game thegame = { + "Net", "games.net", + default_params, + game_fetch_preset, + decode_params, + encode_params, + free_params, + dup_params, + TRUE, game_configure, custom_params, + validate_params, + new_game_seed, + game_free_aux_info, + validate_seed, + new_game, + dup_game, + free_game, + TRUE, solve_game, + FALSE, game_text_format, + new_ui, + free_ui, + make_move, + game_size, + game_colours, + game_new_drawstate, + game_free_drawstate, + game_redraw, + game_anim_length, + game_flash_length, + game_wants_statusbar, +};