X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=magnets.c;h=553ca0d0da13995048a3d91dde6a8c305e5046bc;hb=db313b3948d27244dd7c34c2609c66d6204d8931;hp=7af3d86e35bbd49588008c14a09463289714c018;hpb=b96a99042bd16535b0c219f69a67545bcab0bf70;p=sgt-puzzles.git diff --git a/magnets.c b/magnets.c index 7af3d86..553ca0d 100644 --- a/magnets.c +++ b/magnets.c @@ -46,7 +46,7 @@ int verbose = 0; enum { COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT, - COL_TEXT, COL_ERROR, COL_CURSOR, + COL_TEXT, COL_ERROR, COL_CURSOR, COL_DONE, COL_NEUTRAL, COL_NEGATIVE, COL_POSITIVE, COL_NOT, NCOLOURS }; @@ -138,7 +138,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 */ @@ -172,7 +172,7 @@ static void decode_params(game_params *ret, char const *string) } } -static char *encode_params(game_params *params, int full) +static char *encode_params(const game_params *params, int full) { char buf[256]; sprintf(buf, "%dx%d", params->w, params->h); @@ -183,7 +183,7 @@ static char *encode_params(game_params *params, int full) return dupstr(buf); } -static config_item *game_configure(game_params *params) +static config_item *game_configure(const game_params *params) { config_item *ret; char buf[64]; @@ -220,7 +220,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); @@ -232,7 +232,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) { if (params->w < 2) return "Width must be at least one"; if (params->h < 2) return "Height must be at least one"; @@ -273,6 +273,7 @@ struct game_state { int *grid; /* size w*h, for cell state (pos/neg) */ unsigned int *flags; /* size w*h */ int solved, completed, numbered; + unsigned char *counts_done; struct game_common *common; /* domino layout never changes. */ }; @@ -285,6 +286,7 @@ static void clear_state(game_state *ret) memset(ret->common->rowcount, 0, ret->h*3*sizeof(int)); memset(ret->common->colcount, 0, ret->w*3*sizeof(int)); + memset(ret->counts_done, 0, (ret->h + ret->w) * 2 * sizeof(unsigned char)); for (i = 0; i < ret->wh; i++) { ret->grid[i] = EMPTY; @@ -304,6 +306,7 @@ static game_state *new_state(int w, int h) ret->grid = snewn(ret->wh, int); ret->flags = snewn(ret->wh, unsigned int); + ret->counts_done = snewn((ret->h + ret->w) * 2, unsigned char); ret->common = snew(struct game_common); ret->common->refcount = 1; @@ -317,7 +320,7 @@ static game_state *new_state(int w, int h) return ret; } -static game_state *dup_game(game_state *src) +static game_state *dup_game(const game_state *src) { game_state *dest = snew(game_state); @@ -335,6 +338,10 @@ static game_state *dup_game(game_state *src) dest->grid = snewn(dest->wh, int); memcpy(dest->grid, src->grid, dest->wh*sizeof(int)); + dest->counts_done = snewn((dest->h + dest->w) * 2, unsigned char); + memcpy(dest->counts_done, src->counts_done, + (dest->h + dest->w) * 2 * sizeof(unsigned char)); + dest->flags = snewn(dest->wh, unsigned int); memcpy(dest->flags, src->flags, dest->wh*sizeof(unsigned int)); @@ -350,6 +357,7 @@ static void free_game(game_state *state) sfree(state->common->colcount); sfree(state->common); } + sfree(state->counts_done); sfree(state->flags); sfree(state->grid); sfree(state); @@ -395,7 +403,8 @@ static int c2n(char c) { /* XXX cloned from singles.c */ return -1; } -static char *readrow(char *desc, int n, int *array, int off, const char **prob) +static const char *readrow(const char *desc, int n, int *array, int off, + const char **prob) { int i, num; char c; @@ -422,7 +431,8 @@ badchar: return NULL; } -static game_state *new_game_int(game_params *params, char *desc, const char **prob) +static game_state *new_game_int(const game_params *params, const char *desc, + const char **prob) { game_state *state = new_state(params->w, params->h); int x, y, idx, *count; @@ -529,7 +539,7 @@ done: return state; } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { const char *prob; game_state *st = new_game_int(params, desc, &prob); @@ -538,7 +548,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) { const char *prob; game_state *st = new_game_int(params, desc, &prob); @@ -579,7 +590,7 @@ static char *generate_desc(game_state *new) return desc; } -static void game_text_hborder(game_state *state, char **p_r) +static void game_text_hborder(const game_state *state, char **p_r) { char *p = *p_r; int x; @@ -593,12 +604,12 @@ static void game_text_hborder(game_state *state, char **p_r) *p_r = p; } -static int game_can_format_as_text_now(game_params *params) +static int game_can_format_as_text_now(const game_params *params) { return TRUE; } -static char *game_text_format(game_state *state) +static char *game_text_format(const game_state *state) { int len, x, y, i; char *ret, *p; @@ -679,7 +690,7 @@ typedef struct rowcol { const char *name; } rowcol; -static rowcol mkrowcol(game_state *state, int num, int roworcol) +static rowcol mkrowcol(const game_state *state, int num, int roworcol) { rowcol rc; @@ -704,7 +715,8 @@ static rowcol mkrowcol(game_state *state, int num, int roworcol) return rc; } -static int count_rowcol(game_state *state, int num, int roworcol, int which) +static int count_rowcol(const game_state *state, int num, int roworcol, + int which) { int i, count = 0; rowcol rc = mkrowcol(state, num, roworcol); @@ -1046,7 +1058,7 @@ static int solve_rowcols(game_state *state, rowcolfn fn) static int solve_force(game_state *state) { - int x, y, i, which, didsth = 0; + int i, which, didsth = 0; unsigned long f; for (i = 0; i < state->wh; i++) { @@ -1062,7 +1074,6 @@ static int solve_force(game_state *state) if (f == (GS_NOTNEGATIVE|GS_NOTNEUTRAL)) which = POSITIVE; if (which != -1) { - x = i%state->w; y = i/state->w; if (solve_set(state, i, which, "forced by flags", NULL) < 0) return -1; didsth = 1; @@ -1073,7 +1084,7 @@ static int solve_force(game_state *state) static int solve_neither(game_state *state) { - int x, y, i, j, didsth = 0; + int i, j, didsth = 0; for (i = 0; i < state->wh; i++) { if (state->flags[i] & GS_SET) continue; @@ -1084,7 +1095,6 @@ static int solve_neither(game_state *state) (state->flags[j] & GS_NOTPOSITIVE)) || ((state->flags[i] & GS_NOTNEGATIVE) && (state->flags[j] & GS_NOTNEGATIVE))) { - x = i%state->w; y = i/state->w; if (solve_set(state, i, NEUTRAL, "neither tile magnet", NULL) < 0) return -1; didsth = 1; @@ -1394,7 +1404,8 @@ static int solve_state(game_state *state, int diff) } -static char *game_state_diff(game_state *src, game_state *dst, int issolve) +static char *game_state_diff(const game_state *src, const game_state *dst, + int issolve) { char *ret = NULL, buf[80], c; int retlen = 0, x, y, i, k; @@ -1433,7 +1444,7 @@ static char *game_state_diff(game_state *src, game_state *dst, int issolve) return ret; } -static void solve_from_aux(game_state *state, char *aux) +static void solve_from_aux(const game_state *state, const char *aux) { int i; assert(strlen(aux) == state->wh); @@ -1443,8 +1454,8 @@ static void solve_from_aux(game_state *state, char *aux) } } -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) { game_state *solved = dup_game(currstate); char *move = NULL; @@ -1494,7 +1505,7 @@ static int solve_unnumbered(game_state *state) static int lay_dominoes(game_state *state, random_state *rs, int *scratch) { - int n, i, ret = 0, x, y, nlaid = 0, n_initial_neutral; + int n, i, ret = 0, nlaid = 0, n_initial_neutral; for (i = 0; i < state->wh; i++) { scratch[i] = i; @@ -1513,8 +1524,7 @@ static int lay_dominoes(game_state *state, random_state *rs, int *scratch) /* ...and lay a domino if we can. */ - x = i%state->w; y = i/state->w; - debug(("Laying domino at i:%d, (%d,%d)\n", i, x, y)); + debug(("Laying domino at i:%d, (%d,%d)\n", i, i%state->w, i/state->w)); /* The choice of which type of domino to lay here leads to subtle differences * in the sorts of boards that get produced. Too much bias towards magnets @@ -1547,7 +1557,8 @@ static int lay_dominoes(game_state *state, random_state *rs, int *scratch) ret = solve_set(state, i, NEUTRAL, "layout", NULL); } if (!ret) { - debug(("Unable to lay anything at (%d,%d), giving up.", x, y)); + debug(("Unable to lay anything at (%d,%d), giving up.", + i%state->w, i/state->w)); ret = -1; break; } @@ -1605,7 +1616,7 @@ static void generate_aux(game_state *new, char *aux) aux[new->wh] = '\0'; } -static int check_difficulty(game_params *params, game_state *new, +static int check_difficulty(const game_params *params, game_state *new, random_state *rs) { int *scratch, *grid_correct, slen, i; @@ -1677,7 +1688,7 @@ static int check_difficulty(game_params *params, game_state *new, return 0; } -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_r, int interactive) { game_state *new = new_state(params->w, params->h); @@ -1702,7 +1713,7 @@ struct game_ui { int cur_x, cur_y, cur_visible; }; -static game_ui *new_ui(game_state *state) +static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->cur_x = ui->cur_y = 0; @@ -1715,17 +1726,17 @@ static void free_ui(game_ui *ui) sfree(ui); } -static char *encode_ui(game_ui *ui) +static char *encode_ui(const game_ui *ui) { return NULL; } -static void decode_ui(game_ui *ui, char *encoding) +static void decode_ui(game_ui *ui, const char *encoding) { } -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) { if (!oldstate->completed && newstate->completed) ui->cur_visible = 0; @@ -1743,11 +1754,10 @@ struct game_drawstate { #define DS_ERROR 0x10 #define DS_CURSOR 0x20 #define DS_SET 0x40 -#define DS_FULL 0x80 -#define DS_NOTPOS 0x100 -#define DS_NOTNEG 0x200 -#define DS_NOTNEU 0x400 -#define DS_FLASH 0x800 +#define DS_NOTPOS 0x80 +#define DS_NOTNEG 0x100 +#define DS_NOTNEU 0x200 +#define DS_FLASH 0x400 #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) @@ -1756,8 +1766,36 @@ struct game_drawstate { #define COORD(x) ( (x+1) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( (x - BORDER) / TILE_SIZE - 1 ) -static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, - int x, int y, int button) +static int is_clue(const game_state *state, int x, int y) +{ + int h = state->h, w = state->w; + + if (((x == -1 || x == w) && y >= 0 && y < h) || + ((y == -1 || y == h) && x >= 0 && x < w)) + return TRUE; + + return FALSE; +} + +static int clue_index(const game_state *state, int x, int y) +{ + int h = state->h, w = state->w; + + if (y == -1) + return x; + else if (x == w) + return w + y; + else if (y == h) + return 2 * w + h - x - 1; + else if (x == -1) + return 2 * (w + h) - y - 1; + + return -1; +} + +static char *interpret_move(const game_state *state, game_ui *ui, + const game_drawstate *ds, + int x, int y, int button) { int gx = FROMCOORD(x), gy = FROMCOORD(y), idx, curr; char *nullret = NULL, buf[80], movech; @@ -1782,6 +1820,9 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, nullret = ""; } action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL; + } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) { + sprintf(buf, "D%d,%d", gx, gy); + return dupstr(buf); } else return NULL; @@ -1819,7 +1860,7 @@ 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 game_state *execute_move(const game_state *state, const char *move) { game_state *ret = dup_game(state); int x, y, n, idx, idx2; @@ -1858,6 +1899,9 @@ static game_state *execute_move(game_state *state, char *move) ret->flags[idx] |= GS_SET; ret->flags[idx2] |= GS_SET; } + } else if (c == 'D' && sscanf(move, "%d,%d%n", &x, &y, &n) == 2 && + is_clue(ret, x, y)) { + ret->counts_done[clue_index(ret, x, y)] ^= 1; } else goto badmove; @@ -1879,8 +1923,8 @@ badmove: * 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; @@ -1891,7 +1935,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; } @@ -1907,6 +1951,7 @@ static float *game_colours(frontend *fe, int *ncolours) ret[COL_TEXT * 3 + i] = 0.0F; ret[COL_NEGATIVE * 3 + i] = 0.0F; ret[COL_CURSOR * 3 + i] = 0.9F; + ret[COL_DONE * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.5F; } ret[COL_POSITIVE * 3 + 0] = 0.8F; @@ -1929,7 +1974,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) { struct game_drawstate *ds = snew(struct game_drawstate); @@ -1956,7 +2001,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds) sfree(ds); } -static void draw_num_col(drawing *dr, game_drawstate *ds, int rowcol, int which, +static void draw_num(drawing *dr, game_drawstate *ds, int rowcol, int which, int idx, int colbg, int col, int num) { char buf[32]; @@ -1984,13 +2029,6 @@ static void draw_num_col(drawing *dr, game_drawstate *ds, int rowcol, int which, draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE); } -static void draw_num(drawing *dr, game_drawstate *ds, int rowcol, int which, - int idx, unsigned long c, int num) -{ - draw_num_col(dr, ds, rowcol, which, idx, COL_BACKGROUND, - (c & DS_ERROR) ? COL_ERROR : COL_TEXT, num); -} - static void draw_sym(drawing *dr, game_drawstate *ds, int x, int y, int which, int col) { int cx = COORD(x), cy = COORD(y); @@ -2128,13 +2166,34 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int *dominoes, draw_update(dr, cx, cy, TILE_SIZE, TILE_SIZE); } +static int get_count_color(const game_state *state, int rowcol, int which, + int index, int target) +{ + int idx; + int count = count_rowcol(state, index, rowcol, which); -static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, - game_state *state, int dir, game_ui *ui, - float animtime, float flashtime) + if ((count > target) || + (count < target && !count_rowcol(state, index, rowcol, -1))) { + return COL_ERROR; + } else if (rowcol == COLUMN) { + idx = clue_index(state, index, which == POSITIVE ? -1 : state->h); + } else { + idx = clue_index(state, which == POSITIVE ? -1 : state->w, index); + } + + if (state->counts_done[idx]) { + return COL_DONE; + } + + return COL_TEXT; +} + +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 x, y, w = state->w, h = state->h, which, i, j, flash; - unsigned long c = 0; flash = (int)(flashtime * 5 / FLASH_TIME) % 2; @@ -2157,8 +2216,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int idx = y*w+x; - - c = state->grid[idx]; + unsigned long c = state->grid[idx]; if (state->flags[idx] & GS_ERROR) c |= DS_ERROR; @@ -2186,33 +2244,24 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, } /* Draw counts around side */ for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) { - int target, count; for (i = 0; i < w; i++) { - target = state->common->colcount[i*3+which]; - count = count_rowcol(state, i, COLUMN, which); - c = 0; - if ((count > target) || - (count < target && !count_rowcol(state, i, COLUMN, -1))) - c |= DS_ERROR; - if (count == target) c |= DS_FULL; - if (c != ds->colwhat[i*3+which] || !ds->started) { - draw_num(dr, ds, COLUMN, which, i, c, - state->common->colcount[i*3+which]); - ds->colwhat[i*3+which] = c; + int index = i * 3 + which; + int target = state->common->colcount[index]; + int color = get_count_color(state, COLUMN, which, i, target); + + if (color != ds->colwhat[index] || !ds->started) { + draw_num(dr, ds, COLUMN, which, i, COL_BACKGROUND, color, target); + ds->colwhat[index] = color; } } for (i = 0; i < h; i++) { - target = state->common->rowcount[i*3+which]; - count = count_rowcol(state, i, ROW, which); - c = 0; - if ((count > target) || - (count < target && !count_rowcol(state, i, ROW, -1))) - c |= DS_ERROR; - if (count == target) c |= DS_FULL; - if (c != ds->rowwhat[i*3+which] || !ds->started) { - draw_num(dr, ds, ROW, which, i, c, - state->common->rowcount[i*3+which]); - ds->rowwhat[i*3+which] = c; + int index = i * 3 + which; + int target = state->common->rowcount[index]; + int color = get_count_color(state, ROW, which, i, target); + + if (color != ds->rowwhat[index] || !ds->started) { + draw_num(dr, ds, ROW, which, i, COL_BACKGROUND, color, target); + ds->rowwhat[index] = color; } } } @@ -2220,14 +2269,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, ds->started = 1; } -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) { return 0.0F; } -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->completed && newstate->completed && !oldstate->solved && !newstate->solved) @@ -2235,12 +2284,17 @@ 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) +{ + return state->completed ? +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) { int pw, ph; @@ -2252,12 +2306,12 @@ static void game_print_size(game_params *params, float *x, float *y) *y = ph / 100.0F; } -static void game_print(drawing *dr, game_state *state, int tilesize) +static void game_print(drawing *dr, const game_state *state, int tilesize) { int w = state->w, h = state->h; int ink = print_mono_colour(dr, 0); int paper = print_mono_colour(dr, 1); - int x, y, target, count, which, i, j; + int x, y, which, i, j; /* Ick: fake up `ds->tilesize' for macro expansion purposes */ game_drawstate ads, *ds = &ads; @@ -2272,16 +2326,12 @@ static void game_print(drawing *dr, game_state *state, int tilesize) draw_sym(dr, ds, state->w, state->h, NEGATIVE, ink); for (which = POSITIVE, j = 0; j < 2; which = OPPOSITE(which), j++) { for (i = 0; i < w; i++) { - target = state->common->colcount[i*3+which]; - count = count_rowcol(state, i, COLUMN, which); - draw_num_col(dr, ds, COLUMN, which, i, paper, ink, - state->common->colcount[i*3+which]); + draw_num(dr, ds, COLUMN, which, i, paper, ink, + state->common->colcount[i*3+which]); } for (i = 0; i < h; i++) { - target = state->common->rowcount[i*3+which]; - count = count_rowcol(state, i, ROW, which); - draw_num_col(dr, ds, ROW, which, i, paper, ink, - state->common->rowcount[i*3+which]); + draw_num(dr, ds, ROW, which, i, paper, ink, + state->common->rowcount[i*3+which]); } } @@ -2346,7 +2396,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize) const struct game thegame = { "Magnets", "games.magnets", "magnets", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params, @@ -2374,6 +2424,7 @@ const struct game thegame = { game_redraw, game_anim_length, game_flash_length, + game_status, TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, @@ -2555,7 +2606,7 @@ int main(int argc, const char *argv[]) goto done; } s = new_game(NULL, p, desc); - printf("%s:%s (seed %ld)\n", id, desc, seed); + printf("%s:%s (seed %ld)\n", id, desc, (long)seed); if (aux) { /* We just generated this ourself. */ if (verbose || print) {