X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=twiddle.c;h=6e05f4ddeced8a95cd46377289da9dec7c91d62d;hb=a0a581c8b5422bf0c5ed3fde6aa25811e4eb89fc;hp=daf691c325312ac39273b6bb508991b53008f92b;hpb=a3b837c69845e5ccfd3bc29ee72c9b3e6ea9adec;p=sgt-puzzles.git diff --git a/twiddle.c b/twiddle.c index daf691c..6e05f4d 100644 --- a/twiddle.c +++ b/twiddle.c @@ -21,7 +21,7 @@ #define COORD(x) ( (x) * TILE_SIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) -#define ANIM_PER_RADIUS_UNIT 0.13F +#define ANIM_PER_BLKSIZE_UNIT 0.13F #define FLASH_FRAME 0.13F enum { @@ -31,6 +31,7 @@ enum { COL_HIGHLIGHT_GENTLE, COL_LOWLIGHT, COL_LOWLIGHT_GENTLE, + COL_HIGHCURSOR, COL_LOWCURSOR, NCOLOURS }; @@ -69,7 +70,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 */ @@ -87,9 +88,9 @@ static int game_fetch_preset(int i, char **name, game_params **params) { "3x3 orientable", { 3, 3, 2, FALSE, TRUE } }, { "4x4 normal", { 4, 4, 2, FALSE } }, { "4x4 orientable", { 4, 4, 2, FALSE, TRUE } }, - { "4x4 radius 3", { 4, 4, 3, FALSE } }, - { "5x5 radius 3", { 5, 5, 3, FALSE } }, - { "6x6 radius 4", { 6, 6, 4, FALSE } }, + { "4x4, rotating 3x3 blocks", { 4, 4, 3, FALSE } }, + { "5x5, rotating 3x3 blocks", { 5, 5, 3, FALSE } }, + { "6x6, rotating 4x4 blocks", { 6, 6, 4, FALSE } }, }; if (i < 0 || i >= lenof(presets)) @@ -132,7 +133,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%dn%d%s%s", params->w, params->h, params->n, @@ -145,7 +146,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[80]; @@ -164,7 +165,7 @@ static config_item *game_configure(game_params *params) ret[1].sval = dupstr(buf); ret[1].ival = 0; - ret[2].name = "Rotation radius"; + ret[2].name = "Rotating block size"; ret[2].type = C_STRING; sprintf(buf, "%d", params->n); ret[2].sval = dupstr(buf); @@ -194,7 +195,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); @@ -208,14 +209,14 @@ 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->n < 2) - return "Rotation radius must be at least two"; + return "Rotating block size must be at least two"; if (params->w < params->n) - return "Width must be at least the rotation radius"; + return "Width must be at least the rotating block size"; if (params->h < params->n) - return "Height must be at least the rotation radius"; + return "Height must be at least the rotating block size"; return NULL; } @@ -305,7 +306,7 @@ static int grid_complete(int *grid, int wh, int orientable) return ok; } -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) { int *grid; @@ -429,14 +430,13 @@ static char *new_game_desc(game_params *params, random_state *rs, return ret; } -static char *validate_desc(game_params *params, char *desc) +static char *validate_desc(const game_params *params, const char *desc) { - char *p, *err; + const char *p; int w = params->w, h = params->h, wh = w*h; int i; p = desc; - err = NULL; for (i = 0; i < wh; i++) { if (*p < '0' || *p > '9') @@ -459,12 +459,13 @@ 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) { game_state *state = snew(game_state); int w = params->w, h = params->h, n = params->n, wh = w*h; int i; - char *p; + const char *p; state->w = w; state->h = h; @@ -499,7 +500,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) { game_state *ret = snew(game_state); @@ -539,13 +540,18 @@ static int compare_int(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) { return dupstr("S"); } -static char *game_text_format(game_state *state) +static int game_can_format_as_text_now(const game_params *params) +{ + return TRUE; +} + +static char *game_text_format(const game_state *state) { char *ret, *p, buf[80]; int i, x, y, col, o, maxlen; @@ -592,26 +598,38 @@ static char *game_text_format(game_state *state) return ret; } -static game_ui *new_ui(game_state *state) +struct game_ui { + int cur_x, cur_y; + int cur_visible; +}; + +static game_ui *new_ui(const game_state *state) { - return NULL; + game_ui *ui = snew(game_ui); + + ui->cur_x = 0; + ui->cur_y = 0; + ui->cur_visible = FALSE; + + return ui; } 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) { } @@ -620,10 +638,12 @@ struct game_drawstate { int w, h, bgcolour; int *grid; int tilesize; + int cur_x, cur_y; }; -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->w, h = state->h, n = state->n /* , wh = w*h */; char buf[80]; @@ -631,6 +651,19 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, button = button & (~MOD_MASK | MOD_NUM_KEYPAD); + if (IS_CURSOR_MOVE(button)) { + if (button == CURSOR_LEFT && ui->cur_x > 0) + ui->cur_x--; + if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w)) + ui->cur_x++; + if (button == CURSOR_UP && ui->cur_y > 0) + ui->cur_y--; + if (button == CURSOR_DOWN && (ui->cur_y+n) < (h)) + ui->cur_y++; + ui->cur_visible = 1; + return ""; + } + if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { /* * Determine the coordinates of the click. We offset by n-1 @@ -644,6 +677,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, dir = (button == LEFT_BUTTON ? 1 : -1); if (x < 0 || x > w-n || y < 0 || y > h-n) return NULL; + ui->cur_visible = 0; + } else if (IS_CURSOR_SELECT(button)) { + if (ui->cur_visible) { + x = ui->cur_x; + y = ui->cur_y; + dir = (button == CURSOR_SELECT2) ? -1 : +1; + } else { + ui->cur_visible = 1; + return ""; + } } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') { x = y = 0; dir = (button == 'A' ? -1 : +1); @@ -690,7 +733,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, return dupstr(buf); } -static game_state *execute_move(game_state *from, char *move) +static game_state *execute_move(const game_state *from, const char *move) { game_state *ret; int w = from->w, h = from->h, n = from->n, wh = w*h; @@ -741,8 +784,8 @@ static game_state *execute_move(game_state *from, 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; @@ -753,7 +796,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; } @@ -765,17 +808,23 @@ static float *game_colours(frontend *fe, int *ncolours) game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); + /* cursor is light-background with a red tinge. */ + ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F; + ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F; + ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F; + for (i = 0; i < 3; i++) { ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F; ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F; ret[COL_TEXT * 3 + i] = 0.0; + ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F; } *ncolours = 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); int i; @@ -788,6 +837,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) ds->tilesize = 0; /* haven't decided yet */ for (i = 0; i < ds->w*ds->h; i++) ds->grid[i] = -1; + ds->cur_x = ds->cur_y = -state->n; return ds; } @@ -808,20 +858,25 @@ struct rotation { static void rotate(int *xy, struct rotation *rot) { if (rot) { - float xf = xy[0] - rot->ox, yf = xy[1] - rot->oy; + float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy; float xf2, yf2; xf2 = rot->c * xf + rot->s * yf; yf2 = - rot->s * xf + rot->c * yf; - xy[0] = xf2 + rot->ox + 0.5; /* round to nearest */ - xy[1] = yf2 + rot->oy + 0.5; /* round to nearest */ + xy[0] = (int)(xf2 + rot->ox + 0.5); /* round to nearest */ + xy[1] = (int)(yf2 + rot->oy + 0.5); /* round to nearest */ } } -static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state, +#define CUR_TOP 1 +#define CUR_RIGHT 2 +#define CUR_BOTTOM 4 +#define CUR_LEFT 8 + +static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state, int x, int y, int tile, int flash_colour, - struct rotation *rot) + struct rotation *rot, unsigned cedges) { int coords[8]; char str[40]; @@ -858,28 +913,28 @@ static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state, coords[3] = y; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT, - rot ? rot->rc : COL_LOWLIGHT); + rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT); /* Bottom side. */ coords[2] = x; coords[3] = y + TILE_SIZE - 1; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT, - rot ? rot->bc : COL_LOWLIGHT); + rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT); /* Left side. */ coords[0] = x; coords[1] = y; rotate(coords+0, rot); draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT, - rot ? rot->lc : COL_HIGHLIGHT); + rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT); /* Top side. */ coords[2] = x + TILE_SIZE - 1; coords[3] = y; rotate(coords+2, rot); draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT, - rot ? rot->tc : COL_HIGHLIGHT); + rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT); /* * Now the main blank area in the centre of the tile. @@ -1000,14 +1055,29 @@ static int highlight_colour(float angle) return colours[(int)((angle + 2*PI) / (PI/16)) & 31]; } -static float game_anim_length(game_state *oldstate, game_state *newstate, - int dir, game_ui *ui) +static float game_anim_length_real(const game_state *oldstate, + const game_state *newstate, int dir, + const game_ui *ui) { - return ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1); + /* + * Our game_anim_length doesn't need to modify its game_ui, so + * this is the real function which declares ui as const. We must + * wrap this for the backend structure with a version that has ui + * non-const, but we still need this version to call from within + * game_redraw which only has a const ui available. + */ + return (float)(ANIM_PER_BLKSIZE_UNIT * sqrt(newstate->n-1)); } -static float game_flash_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 game_anim_length_real(oldstate, newstate, dir, 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->used_solve && !newstate->used_solve) @@ -1016,13 +1086,25 @@ static float game_flash_length(game_state *oldstate, game_state *newstate, return 0.0F; } -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 int game_status(const game_state *state) +{ + return state->completed ? +1 : 0; +} + +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 i, bgcolour; struct rotation srot, *rot; int lastx = -1, lasty = -1, lastr = -1; + int cx, cy, cmoved = 0, n = state->n; + + cx = ui->cur_visible ? ui->cur_x : -state->n; + cy = ui->cur_visible ? ui->cur_y : -state->n; + if (cx != ds->cur_x || cy != ds->cur_y) + cmoved = 1; if (flashtime > 0) { int frame = (int)(flashtime / FLASH_FRAME); @@ -1069,7 +1151,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, */ if (oldstate) { float angle; - float anim_max = game_anim_length(oldstate, state, dir, ui); + float anim_max = game_anim_length_real(oldstate, state, dir, ui); if (dir > 0) { lastx = state->lastx; @@ -1087,17 +1169,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, rot->cw = rot->ch = TILE_SIZE * state->n; rot->ox = rot->cx + rot->cw/2; rot->oy = rot->cy + rot->ch/2; - angle = (-PI/2 * lastr) * (1.0 - animtime / anim_max); - rot->c = cos(angle); - rot->s = sin(angle); + angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max)); + rot->c = (float)cos(angle); + rot->s = (float)sin(angle); /* * Sort out the colours of the various sides of the tile. */ - rot->lc = highlight_colour(PI + angle); + rot->lc = highlight_colour((float)PI + angle); rot->rc = highlight_colour(angle); - rot->tc = highlight_colour(PI/2 + angle); - rot->bc = highlight_colour(-PI/2 + angle); + rot->tc = highlight_colour((float)(PI/2.0) + angle); + rot->bc = highlight_colour((float)(-PI/2.0) + angle); draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour); } else @@ -1107,7 +1189,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, * Now draw each tile. */ for (i = 0; i < state->w * state->h; i++) { - int t; + int t, cc = 0; int tx = i % state->w, ty = i / state->w; /* @@ -1124,15 +1206,31 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, else t = state->grid[i]; + if (cmoved) { + /* cursor has moved (or changed visibility)... */ + if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1) + cc = 1; /* ...we're on new cursor, redraw */ + if (tx == ds->cur_x || tx == ds->cur_x+n-1 || + ty == ds->cur_y || ty == ds->cur_y+n-1) + cc = 1; /* ...we were on old cursor, redraw */ + } + if (ds->bgcolour != bgcolour || /* always redraw when flashing */ - ds->grid[i] != t || ds->grid[i] == -1 || t == -1) { + ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) { int x = COORD(tx), y = COORD(ty); + unsigned cedges = 0; + + if (tx == cx && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT; + if (ty == cy && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP; + if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT; + if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM; - draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot); + draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges); ds->grid[i] = t; } } ds->bgcolour = bgcolour; + ds->cur_x = cx; ds->cur_y = cy; /* * Update the status bar. @@ -1163,16 +1261,16 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, } } -static int game_timing_state(game_state *state, game_ui *ui) +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) { } @@ -1181,9 +1279,9 @@ static void game_print(drawing *dr, game_state *state, int tilesize) #endif const struct game thegame = { - "Twiddle", "games.twiddle", + "Twiddle", "games.twiddle", "twiddle", default_params, - game_fetch_preset, + game_fetch_preset, NULL, decode_params, encode_params, free_params, @@ -1196,7 +1294,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, @@ -1211,8 +1309,11 @@ 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, 0, /* flags */ }; + +/* vim: set shiftwidth=4 tabstop=8: */