X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=inertia.c;h=4cbdd5299b823359b48b6bad5d16c8eed1b0d92f;hb=3234912f921916a1b8da164fd61dc75579358577;hp=6531b9845e86f42e559a30669c83b5ab47a4567a;hpb=74f45138aeb6608137344c7d5245ff6e3ec12572;p=sgt-puzzles.git diff --git a/inertia.c b/inertia.c index 6531b98..4cbdd52 100644 --- a/inertia.c +++ b/inertia.c @@ -88,8 +88,11 @@ static game_params *default_params(void) game_params *ret = snew(game_params); ret->w = 10; +#ifdef PORTRAIT_SCREEN + ret->h = 10; +#else ret->h = 8; - +#endif return ret; } @@ -98,7 +101,7 @@ static void free_params(game_params *params) sfree(params); } -static game_params *dup_params(game_params *params) +static game_params *dup_params(const game_params *params) { game_params *ret = snew(game_params); *ret = *params; /* structure copy */ @@ -106,9 +109,15 @@ static game_params *dup_params(game_params *params) } static const struct game_params inertia_presets[] = { +#ifdef PORTRAIT_SCREEN + { 10, 10 }, + { 12, 12 }, + { 16, 16 }, +#else { 10, 8 }, { 15, 12 }, { 20, 16 }, +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) @@ -140,7 +149,7 @@ static void decode_params(game_params *params, char const *string) } } -static char *encode_params(game_params *params, int full) +static char *encode_params(const game_params *params, int full) { char data[256]; @@ -149,7 +158,7 @@ static char *encode_params(game_params *params, int full) return dupstr(data); } -static config_item *game_configure(game_params *params) +static config_item *game_configure(const game_params *params) { config_item *ret; char buf[80]; @@ -176,7 +185,7 @@ static config_item *game_configure(game_params *params) return ret; } -static game_params *custom_params(config_item *cfg) +static game_params *custom_params(const config_item *cfg) { game_params *ret = snew(game_params); @@ -186,7 +195,7 @@ static game_params *custom_params(config_item *cfg) return ret; } -static char *validate_params(game_params *params, int full) +static char *validate_params(const game_params *params, int full) { /* * Avoid completely degenerate cases which only have one @@ -574,13 +583,13 @@ static char *gengrid(int w, int h, random_state *rs) return grid; } -static char *new_game_desc(game_params *params, random_state *rs, +static char *new_game_desc(const game_params *params, random_state *rs, char **aux, int interactive) { return gengrid(params->w, params->h, rs); } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { int w = params->w, h = params->h, wh = w*h; int starts = 0, gems = 0, i; @@ -608,7 +617,8 @@ static char *validate_desc(game_params *params, char *desc) return NULL; } -static game_state *new_game(midend *me, game_params *params, char *desc) +static game_state *new_game(midend *me, const game_params *params, + const char *desc) { int w = params->w, h = params->h, wh = w*h; int i; @@ -645,7 +655,7 @@ static game_state *new_game(midend *me, game_params *params, char *desc) return state; } -static game_state *dup_game(game_state *state) +static game_state *dup_game(const game_state *state) { int w = state->p.w, h = state->p.h, wh = w*h; game_state *ret = snew(game_state); @@ -722,10 +732,10 @@ static int compare_integers(const void *av, const void *bv) return 0; } -static char *solve_game(game_state *state, game_state *currstate, - char *aux, char **error) +static char *solve_game(const game_state *state, const game_state *currstate, + const char *aux, char **error) { - int w = state->p.w, h = state->p.h, wh = w*h; + int w = currstate->p.w, h = currstate->p.h, wh = w*h; int *nodes, *nodeindex, *edges, *backedges, *edgei, *backedgei, *circuit; int nedges; int *dist, *dist2, *list; @@ -1436,9 +1446,48 @@ static char *solve_game(game_state *state, game_state *currstate, return soln; } -static char *game_text_format(game_state *state) +static int game_can_format_as_text_now(const game_params *params) { - return NULL; + return TRUE; +} + +static char *game_text_format(const game_state *state) +{ + int w = state->p.w, h = state->p.h, r, c; + int cw = 4, ch = 2, gw = cw*w + 2, gh = ch * h + 1, len = gw * gh; + char *board = snewn(len + 1, char); + + sprintf(board, "%*s+\n", len - 2, ""); + + for (r = 0; r < h; ++r) { + for (c = 0; c < w; ++c) { + int cell = r*ch*gw + cw*c, center = cell + gw*ch/2 + cw/2; + int i = r*w + c; + switch (state->grid[i]) { + case BLANK: break; + case GEM: board[center] = 'o'; break; + case MINE: board[center] = 'M'; break; + case STOP: board[center-1] = '('; board[center+1] = ')'; break; + case WALL: memset(board + center - 1, 'X', 3); + } + + if (r == state->py && c == state->px) { + if (!state->dead) board[center] = '@'; + else memcpy(board + center - 1, ":-(", 3); + } + board[cell] = '+'; + memset(board + cell + 1, '-', cw - 1); + for (i = 1; i < ch; ++i) board[cell + i*gw] = '|'; + } + for (c = 0; c < ch; ++c) { + board[(r*ch+c)*gw + gw - 2] = "|+"[!c]; + board[(r*ch+c)*gw + gw - 1] = '\n'; + } + } + memset(board + len - gw, '-', gw - 2); + for (c = 0; c < w; ++c) board[len - gw + cw*c] = '+'; + + return board; } struct game_ui { @@ -1449,7 +1498,7 @@ struct game_ui { int just_died; }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->anim_length = 0.0F; @@ -1465,7 +1514,7 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(game_ui *ui) +static char *encode_ui(const game_ui *ui) { char buf[80]; /* @@ -1475,14 +1524,14 @@ static char *encode_ui(game_ui *ui) return dupstr(buf); } -static void decode_ui(game_ui *ui, char *encoding) +static void decode_ui(game_ui *ui, const char *encoding) { int p = 0; sscanf(encoding, "D%d%n", &ui->deaths, &p); } -static void game_changed_state(game_ui *ui, game_state *oldstate, - game_state *newstate) +static void game_changed_state(game_ui *ui, const game_state *oldstate, + const game_state *newstate) { /* * Increment the deaths counter. We only do this if @@ -1511,13 +1560,18 @@ struct game_drawstate { #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) +#ifdef SMALL_SCREEN +#define BORDER (TILESIZE / 4) +#else #define BORDER (TILESIZE) +#endif #define HIGHLIGHT_WIDTH (TILESIZE / 10) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int x, int y, int button) +static char *interpret_move(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button) { int w = state->p.w, h = state->p.h /*, wh = w*h */; int dir; @@ -1562,7 +1616,8 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, dir = 1; else if (button == (MOD_NUM_KEYPAD | '3')) dir = 3; - else if (button == ' ' && state->soln && state->solnpos < state->soln->len) + else if (IS_CURSOR_SELECT(button) && + state->soln && state->solnpos < state->soln->len) dir = state->soln->list[state->solnpos]; if (dir < 0) @@ -1590,32 +1645,51 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return dupstr(buf); } -static game_state *execute_move(game_state *state, char *move) +static void install_new_solution(game_state *ret, const char *move) +{ + int i; + soln *sol; + assert (*move == 'S'); + ++move; + + sol = snew(soln); + sol->len = strlen(move); + sol->list = snewn(sol->len, unsigned char); + for (i = 0; i < sol->len; ++i) sol->list[i] = move[i] - '0'; + + if (ret->soln && --ret->soln->refcount == 0) { + sfree(ret->soln->list); + sfree(ret->soln); + } + + ret->soln = sol; + sol->refcount = 1; + + ret->cheated = TRUE; + ret->solnpos = 0; +} + +static void discard_solution(game_state *ret) +{ + --ret->soln->refcount; + assert(ret->soln->refcount > 0); /* ret has a soln-pointing dup */ + ret->soln = NULL; + ret->solnpos = 0; +} + +static game_state *execute_move(const game_state *state, const char *move) { int w = state->p.w, h = state->p.h /*, wh = w*h */; int dir; game_state *ret; if (*move == 'S') { - int len, i; - soln *sol; - /* * This is a solve move, so we don't actually _change_ the * grid but merely set up a stored solution path. */ - move++; - len = strlen(move); - sol = snew(soln); - sol->len = len; - sol->list = snewn(len, unsigned char); - for (i = 0; i < len; i++) - sol->list[i] = move[i] - '0'; ret = dup_game(state); - ret->cheated = TRUE; - ret->soln = sol; - ret->solnpos = 0; - sol->refcount = 1; + install_new_solution(ret, move); return ret; } @@ -1656,22 +1730,18 @@ static game_state *execute_move(game_state *state, char *move) } if (ret->soln) { - /* - * If this move is the correct next one in the stored - * solution path, advance solnpos. - */ - if (ret->soln->list[ret->solnpos] == dir && - ret->solnpos+1 < ret->soln->len) { - ret->solnpos++; + if (ret->dead || ret->gems == 0) + discard_solution(ret); + else if (ret->soln->list[ret->solnpos] == dir) { + ++ret->solnpos; + assert(ret->solnpos < ret->soln->len); /* or gems == 0 */ + assert(!ret->dead); /* or not a solution */ } else { - /* - * Otherwise, the user has strayed from the path, so - * the path is no longer valid. - */ - ret->soln->refcount--; - assert(ret->soln->refcount > 0);/* `state' at least still exists */ - ret->soln = NULL; - ret->solnpos = 0; + char *error = NULL, *soln = solve_game(NULL, ret, NULL, &error); + if (!error) { + install_new_solution(ret, soln); + sfree(soln); + } else discard_solution(ret); } } @@ -1682,8 +1752,8 @@ static game_state *execute_move(game_state *state, char *move) * Drawing routines. */ -static void game_compute_size(game_params *params, int tilesize, - int *x, int *y) +static void game_compute_size(const game_params *params, int tilesize, + int *x, int *y) { /* Ick: fake up `ds->tilesize' for macro expansion purposes */ struct { int tilesize; } ads, *ds = &ads; @@ -1694,7 +1764,7 @@ static void game_compute_size(game_params *params, int tilesize, } static void game_set_size(drawing *dr, game_drawstate *ds, - game_params *params, int tilesize) + const game_params *params, int tilesize) { ds->tilesize = tilesize; @@ -1744,7 +1814,7 @@ static float *game_colours(frontend *fe, int *ncolours) return ret; } -static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) +static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) { int w = state->p.w, h = state->p.h, wh = w*h; struct game_drawstate *ds = snew(struct game_drawstate); @@ -1873,32 +1943,10 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) int cx = tx + TILESIZE / 2; int cy = ty + TILESIZE / 2; int r = TILESIZE / 2 - 3; - int coords[4*5*2]; - int xdx = 1, xdy = 0, ydx = 0, ydy = 1; - int tdx, tdy, i; - - for (i = 0; i < 4*5*2; i += 5*2) { - coords[i+2*0+0] = cx - r/6*xdx + r*4/5*ydx; - coords[i+2*0+1] = cy - r/6*xdy + r*4/5*ydy; - coords[i+2*1+0] = cx - r/6*xdx + r*ydx; - coords[i+2*1+1] = cy - r/6*xdy + r*ydy; - coords[i+2*2+0] = cx + r/6*xdx + r*ydx; - coords[i+2*2+1] = cy + r/6*xdy + r*ydy; - coords[i+2*3+0] = cx + r/6*xdx + r*4/5*ydx; - coords[i+2*3+1] = cy + r/6*xdy + r*4/5*ydy; - coords[i+2*4+0] = cx + r*3/5*xdx + r*3/5*ydx; - coords[i+2*4+1] = cy + r*3/5*xdy + r*3/5*ydy; - - tdx = ydx; - tdy = ydy; - ydx = xdx; - ydy = xdy; - xdx = -tdx; - xdy = -tdy; - } - - draw_polygon(dr, coords, 5*4, COL_MINE, COL_MINE); + draw_circle(dr, cx, cy, 5*r/6, COL_MINE, COL_MINE); + draw_rect(dr, cx - r/6, cy - r, 2*(r/6)+1, 2*r+1, COL_MINE); + draw_rect(dr, cx - r, cy - r/6, 2*r+1, 2*(r/6)+1, COL_MINE); draw_rect(dr, cx-r/3, cy-r/3, r/3, r/4, COL_HIGHLIGHT); } else if (v == STOP) { draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, @@ -1911,12 +1959,12 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) int coords[8]; coords[0] = tx+TILESIZE/2; - coords[1] = ty+TILESIZE*1/7; - coords[2] = tx+TILESIZE*1/7; + coords[1] = ty+TILESIZE/2-TILESIZE*5/14; + coords[2] = tx+TILESIZE/2-TILESIZE*5/14; coords[3] = ty+TILESIZE/2; coords[4] = tx+TILESIZE/2; - coords[5] = ty+TILESIZE-TILESIZE*1/7; - coords[6] = tx+TILESIZE-TILESIZE*1/7; + coords[5] = ty+TILESIZE/2+TILESIZE*5/14; + coords[6] = tx+TILESIZE/2+TILESIZE*5/14; coords[7] = ty+TILESIZE/2; draw_polygon(dr, coords, 4, COL_GEM, COL_OUTLINE); @@ -1929,9 +1977,10 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, int v) #define BASE_ANIM_LENGTH 0.1F #define FLASH_LENGTH 0.3F -static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) +static void game_redraw(drawing *dr, game_drawstate *ds, + const game_state *oldstate, const game_state *state, + int dir, const game_ui *ui, + float animtime, float flashtime) { int w = state->p.w, h = state->p.h /*, wh = w*h */; int x, y; @@ -2109,8 +2158,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, ds->player_bg_saved = TRUE; } -static float game_anim_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) +static float game_anim_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) { int dist; if (dir > 0) @@ -2121,8 +2170,8 @@ static float game_anim_length(game_state *oldstate, game_state *newstate, return ui->anim_length; } -static float game_flash_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) +static float game_flash_length(const game_state *oldstate, + const game_state *newstate, int dir, game_ui *ui) { if (!oldstate->dead && newstate->dead) { ui->flashtype = FLASH_DEAD; @@ -2134,16 +2183,26 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, return 0.0F; } -static int game_timing_state(game_state *state, game_ui *ui) +static int game_status(const game_state *state) +{ + /* + * We never report the game as lost, on the grounds that if the + * player has died they're quite likely to want to undo and carry + * on. + */ + return state->gems == 0 ? +1 : 0; +} + +static int game_timing_state(const game_state *state, game_ui *ui) { return TRUE; } -static void game_print_size(game_params *params, float *x, float *y) +static void game_print_size(const game_params *params, float *x, float *y) { } -static void game_print(drawing *dr, game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, int tilesize) { } @@ -2152,7 +2211,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) #endif const struct game thegame = { - "Inertia", "games.inertia", + "Inertia", "games.inertia", "inertia", default_params, game_fetch_preset, decode_params, @@ -2167,7 +2226,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, @@ -2182,6 +2241,7 @@ const struct game thegame = { game_redraw, game_anim_length, game_flash_length, + game_status, FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state,