X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/a3631c72fe1013ab8fa71b8ec91b82e433193ab4..1cdfcdbdd6bb3f14838be9ff1ef78699bffba9c1:/twiddle.c diff --git a/twiddle.c b/twiddle.c index 5af27d5..e296f48 100644 --- a/twiddle.c +++ b/twiddle.c @@ -5,15 +5,6 @@ * Gyro Chamber). */ -/* - * Possibly TODO: - * - * - it's horribly tempting to give the pieces significant - * _orientations_, perhaps by drawing some sort of oriented - * polygonal figure beneath the number. (An arrow pointing - * upwards springs readily to mind.) - */ - #include #include #include @@ -41,8 +32,6 @@ enum { COL_HIGHLIGHT_GENTLE, COL_LOWLIGHT, COL_LOWLIGHT_GENTLE, - COL_TOP, - COL_BOTTOM, NCOLOURS }; @@ -302,7 +291,8 @@ static int grid_complete(int *grid, int wh, int orientable) return ok; } -static 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) { int *grid; int w = params->w, h = params->h, n = params->n, wh = w*h; @@ -345,7 +335,9 @@ static char *new_game_seed(game_params *params, random_state *rs) /* * Now construct the game seed, by describing the grid as a - * simple sequence of comma-separated integers. + * simple sequence of integers. They're comma-separated, unless + * the puzzle is orientable in which case they're separated by + * orientation letters `u', `d', `l' and `r'. */ ret = NULL; retlen = 0; @@ -353,18 +345,25 @@ static char *new_game_seed(game_params *params, random_state *rs) char buf[80]; int k; - k = sprintf(buf, "%d,", grid[i]); + k = sprintf(buf, "%d%c", grid[i] / 4, + params->orientable ? "uldr"[grid[i] & 3] : ','); ret = sresize(ret, retlen + k + 1, char); strcpy(ret + retlen, buf); retlen += k; } - ret[retlen-1] = '\0'; /* delete last comma */ + if (!params->orientable) + ret[retlen-1] = '\0'; /* delete last comma */ sfree(grid); return ret; } +void game_free_aux_info(game_aux_info *aux) +{ + assert(!"Shouldn't happen"); +} + static char *validate_seed(game_params *params, char *seed) { char *p, *err; @@ -375,15 +374,17 @@ static char *validate_seed(game_params *params, char *seed) err = NULL; for (i = 0; i < wh; i++) { - if (*p < '0' || *p > '9') { + if (*p < '0' || *p > '9') return "Not enough numbers in string"; - } while (*p >= '0' && *p <= '9') p++; - if (i < wh-1 && *p != ',') { - return "Expected comma after number"; - } - else if (i == wh-1 && *p) { + if (!params->orientable && i < wh-1) { + if (*p != ',') + return "Expected comma after number"; + } else if (params->orientable && i < wh) { + if (*p != 'l' && *p != 'r' && *p != 'u' && *p != 'd') + return "Expected orientation letter after number"; + } else if (i == wh-1 && *p) { return "Excess junk at end of string"; } @@ -413,11 +414,19 @@ static game_state *new_game(game_params *params, char *seed) p = seed; for (i = 0; i < wh; i++) { - state->grid[i] = atoi(p); + state->grid[i] = 4 * atoi(p); while (*p >= '0' && *p <= '9') p++; - - if (*p) p++; /* eat comma */ + if (*p) { + if (params->orientable) { + switch (*p) { + case 'l': state->grid[i] |= 1; break; + case 'd': state->grid[i] |= 2; break; + case 'r': state->grid[i] |= 3; break; + } + } + p++; + } } return state; @@ -449,6 +458,53 @@ static void free_game(game_state *state) sfree(state); } +static char *game_text_format(game_state *state) +{ + char *ret, *p, buf[80]; + int i, x, y, col, o, maxlen; + + /* + * First work out how many characters we need to display each + * number. We're pretty flexible on grid contents here, so we + * have to scan the entire grid. + */ + col = 0; + for (i = 0; i < state->w * state->h; i++) { + x = sprintf(buf, "%d", state->grid[i] / 4); + if (col < x) col = x; + } + o = (state->orientable ? 1 : 0); + + /* + * Now we know the exact total size of the grid we're going to + * produce: it's got h rows, each containing w lots of col+o, + * w-1 spaces and a trailing newline. + */ + maxlen = state->h * state->w * (col+o+1); + + ret = snewn(maxlen, char); + p = ret; + + for (y = 0; y < state->h; y++) { + for (x = 0; x < state->w; x++) { + int v = state->grid[state->w*y+x]; + sprintf(buf, "%*d", col, v/4); + memcpy(p, buf, col); + p += col; + if (o) + *p++ = "^"[v & 3]; + if (x+1 == state->w) + *p++ = '\n'; + else + *p++ = ' '; + } + } + + assert(p - ret == maxlen); + *p = '\0'; + return ret; +} + static game_ui *new_ui(game_state *state) { return NULL; @@ -545,14 +601,6 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours) ret[COL_TEXT * 3 + i] = 0.0; } - ret[COL_TOP * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.3F; - ret[COL_TOP * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 1.3F; - ret[COL_TOP * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.6F; - - ret[COL_BOTTOM * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.6F; - ret[COL_BOTTOM * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 1.3F; - ret[COL_BOTTOM * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.6F; - *ncolours = NCOLOURS; return ret; } @@ -678,59 +726,43 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, * Next, the colour bars for orientation. */ if (state->orientable) { - int xw, yw, swap; + int xdx, xdy, ydx, ydy; + int cx, cy, displ, displ2; switch (tile & 3) { case 0: - xw = TILE_SIZE - 3 - 2*HIGHLIGHT_WIDTH; - yw = HIGHLIGHT_WIDTH; - swap = FALSE; + xdx = 1, xdy = 0; + ydx = 0, ydy = 1; break; case 1: - xw = HIGHLIGHT_WIDTH; - yw = TILE_SIZE - 3 - 2*HIGHLIGHT_WIDTH; - swap = FALSE; + xdx = 0, xdy = -1; + ydx = 1, ydy = 0; break; case 2: - xw = TILE_SIZE - 3 - 2*HIGHLIGHT_WIDTH; - yw = HIGHLIGHT_WIDTH; - swap = TRUE; + xdx = -1, xdy = 0; + ydx = 0, ydy = -1; break; default /* case 3 */: - xw = HIGHLIGHT_WIDTH; - yw = TILE_SIZE - 3 - 2*HIGHLIGHT_WIDTH; - swap = TRUE; + xdx = 0, xdy = 1; + ydx = -1, ydy = 0; break; } - coords[0] = x + HIGHLIGHT_WIDTH + 1; - coords[1] = y + HIGHLIGHT_WIDTH + 1; - rotate(coords+0, rot); - coords[2] = x + HIGHLIGHT_WIDTH + 1 + xw; - coords[3] = y + HIGHLIGHT_WIDTH + 1; - rotate(coords+2, rot); - coords[4] = x + HIGHLIGHT_WIDTH + 1 + xw; - coords[5] = y + HIGHLIGHT_WIDTH + 1 + yw; - rotate(coords+4, rot); - coords[6] = x + HIGHLIGHT_WIDTH + 1; - coords[7] = y + HIGHLIGHT_WIDTH + 1 + yw; - rotate(coords+6, rot); - draw_polygon(fe, coords, 4, TRUE, swap ? COL_BOTTOM : COL_TOP); - draw_polygon(fe, coords, 4, FALSE, swap ? COL_BOTTOM : COL_TOP); + cx = x + TILE_SIZE / 2; + cy = y + TILE_SIZE / 2; + displ = TILE_SIZE / 2 - HIGHLIGHT_WIDTH - 2; + displ2 = TILE_SIZE / 3 - HIGHLIGHT_WIDTH; - coords[0] = x + TILE_SIZE - 2 - HIGHLIGHT_WIDTH; - coords[1] = y + TILE_SIZE - 2 - HIGHLIGHT_WIDTH; + coords[0] = cx - displ * xdx + displ2 * ydx; + coords[1] = cy - displ * xdy + displ2 * ydy; rotate(coords+0, rot); - coords[2] = x + TILE_SIZE - 2 - HIGHLIGHT_WIDTH - xw; - coords[3] = y + TILE_SIZE - 2 - HIGHLIGHT_WIDTH; + coords[2] = cx + displ * xdx + displ2 * ydx; + coords[3] = cy + displ * xdy + displ2 * ydy; rotate(coords+2, rot); - coords[4] = x + TILE_SIZE - 2 - HIGHLIGHT_WIDTH - xw; - coords[5] = y + TILE_SIZE - 2 - HIGHLIGHT_WIDTH - yw; + coords[4] = cx - displ * ydx; + coords[5] = cy - displ * ydy; rotate(coords+4, rot); - coords[6] = x + TILE_SIZE - 2 - HIGHLIGHT_WIDTH; - coords[7] = y + TILE_SIZE - 2 - HIGHLIGHT_WIDTH - yw; - rotate(coords+6, rot); - draw_polygon(fe, coords, 4, TRUE, swap ? COL_TOP : COL_BOTTOM); - draw_polygon(fe, coords, 4, FALSE, swap ? COL_TOP : COL_BOTTOM); + draw_polygon(fe, coords, 3, TRUE, COL_LOWLIGHT_GENTLE); + draw_polygon(fe, coords, 3, FALSE, COL_LOWLIGHT_GENTLE); } coords[0] = x + TILE_SIZE/2; @@ -826,33 +858,6 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, TILE_SIZE * state->w + 2 * BORDER, TILE_SIZE * state->h + 2 * BORDER); - /* - * In an orientable puzzle, draw some colour bars at the - * sides as a gentle reminder of which colours need to be - * aligned where. - */ - if (state->orientable) { - int y; - for (y = 0; y < state->h; y++) { - draw_rect(fe, COORD(0) - BORDER / 2, - COORD(y) + HIGHLIGHT_WIDTH + 1, - BORDER / 2 - 2 * HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH + 1, COL_TOP); - draw_rect(fe, COORD(state->w) + 2 * HIGHLIGHT_WIDTH, - COORD(y) + HIGHLIGHT_WIDTH + 1, - BORDER / 2 - 2 * HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH + 1, COL_TOP); - draw_rect(fe, COORD(0) - BORDER / 2, - COORD(y) + TILE_SIZE - 2 - 2 * HIGHLIGHT_WIDTH, - BORDER / 2 - 2 * HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH + 1, COL_BOTTOM); - draw_rect(fe, COORD(state->w) + 2 * HIGHLIGHT_WIDTH, - COORD(y) + TILE_SIZE - 2 - 2 * HIGHLIGHT_WIDTH, - BORDER / 2 - 2 * HIGHLIGHT_WIDTH, - HIGHLIGHT_WIDTH + 1, COL_BOTTOM); - } - } - /* * Recessed area containing the whole puzzle. */ @@ -976,21 +981,22 @@ static int game_wants_statusbar(void) #endif const struct game thegame = { - "Twiddle", "games.twiddle", TRUE, + "Twiddle", "games.twiddle", default_params, game_fetch_preset, decode_params, encode_params, free_params, dup_params, - game_configure, - custom_params, + TRUE, game_configure, custom_params, validate_params, new_game_seed, + game_free_aux_info, validate_seed, new_game, dup_game, free_game, + TRUE, game_text_format, new_ui, free_ui, make_move,