Add the ability to reorder the rows and columns in Group. It becomes

much easier to keep track of things if, once you've identified a
cyclic subgroup, you can move it into a contiguous correctly ordered
block.

[originally from svn r9075]
This commit is contained in:
Simon Tatham
2011-01-08 15:53:25 +00:00
parent f9eca22196
commit 48f6bfa474

View File

@ -86,6 +86,7 @@ struct game_state {
unsigned char *immutable; unsigned char *immutable;
int *pencil; /* bitmaps using bits 1<<1..1<<n */ int *pencil; /* bitmaps using bits 1<<1..1<<n */
int completed, cheated; int completed, cheated;
digit *sequence; /* sequence of group elements shown */
}; };
static game_params *default_params(void) static game_params *default_params(void)
@ -841,6 +842,10 @@ static game_state *new_game(midend *me, game_params *params, char *desc)
state->immutable[i] = 0; state->immutable[i] = 0;
state->pencil[i] = 0; state->pencil[i] = 0;
} }
state->sequence = snewn(w, digit);
for (i = 0; i < w; i++) {
state->sequence[i] = i;
}
desc = spec_to_grid(desc, state->grid, a); desc = spec_to_grid(desc, state->grid, a);
for (i = 0; i < a; i++) for (i = 0; i < a; i++)
@ -862,9 +867,11 @@ static game_state *dup_game(game_state *state)
ret->grid = snewn(a, digit); ret->grid = snewn(a, digit);
ret->immutable = snewn(a, unsigned char); ret->immutable = snewn(a, unsigned char);
ret->pencil = snewn(a, int); ret->pencil = snewn(a, int);
ret->sequence = snewn(w, digit);
memcpy(ret->grid, state->grid, a*sizeof(digit)); memcpy(ret->grid, state->grid, a*sizeof(digit));
memcpy(ret->immutable, state->immutable, a*sizeof(unsigned char)); memcpy(ret->immutable, state->immutable, a*sizeof(unsigned char));
memcpy(ret->pencil, state->pencil, a*sizeof(int)); memcpy(ret->pencil, state->pencil, a*sizeof(int));
memcpy(ret->sequence, state->sequence, w*sizeof(digit));
ret->completed = state->completed; ret->completed = state->completed;
ret->cheated = state->cheated; ret->cheated = state->cheated;
@ -877,6 +884,7 @@ static void free_game(game_state *state)
sfree(state->grid); sfree(state->grid);
sfree(state->immutable); sfree(state->immutable);
sfree(state->pencil); sfree(state->pencil);
sfree(state->sequence);
sfree(state); sfree(state);
} }
@ -977,6 +985,13 @@ struct game_ui {
* allowed on immutable squares. * allowed on immutable squares.
*/ */
int hcursor; int hcursor;
/*
* This indicates whether we're dragging a table header to
* reposition an entire row or column.
*/
int drag; /* 0=none 1=row 2=col */
int dragnum; /* element being dragged */
int dragpos; /* its current position */
}; };
static game_ui *new_ui(game_state *state) static game_ui *new_ui(game_state *state)
@ -985,6 +1000,7 @@ static game_ui *new_ui(game_state *state)
ui->hx = ui->hy = 0; ui->hx = ui->hy = 0;
ui->hpencil = ui->hshow = ui->hcursor = 0; ui->hpencil = ui->hshow = ui->hcursor = 0;
ui->drag = 0;
return ui; return ui;
} }
@ -1032,6 +1048,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
#define DF_HIGHLIGHT 0x0400 #define DF_HIGHLIGHT 0x0400
#define DF_HIGHLIGHT_PENCIL 0x0200 #define DF_HIGHLIGHT_PENCIL 0x0200
#define DF_IMMUTABLE 0x0100 #define DF_IMMUTABLE 0x0100
#define DF_LEGEND 0x0080
#define DF_DIGIT_MASK 0x001F #define DF_DIGIT_MASK 0x001F
#define EF_DIGIT_SHIFT 5 #define EF_DIGIT_SHIFT 5
@ -1046,8 +1063,9 @@ struct game_drawstate {
game_params par; game_params par;
int w, tilesize; int w, tilesize;
int started; int started;
long *tiles, *pencil, *errors; long *tiles, *legend, *pencil, *errors;
long *errtmp; long *errtmp;
digit *sequence;
}; };
static int check_errors(game_state *state, long *errors) static int check_errors(game_state *state, long *errors)
@ -1170,41 +1188,70 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
tx = FROMCOORD(x); tx = FROMCOORD(x);
ty = FROMCOORD(y); ty = FROMCOORD(y);
if (tx >= 0 && tx < w && ty >= 0 && ty < w) { if (ui->drag) {
if (button == LEFT_BUTTON) { if (IS_MOUSE_DRAG(button)) {
if (tx == ui->hx && ty == ui->hy && int tcoord = (ui->drag == 1 ? ty : tx);
ui->hshow && ui->hpencil == 0) { if (tcoord >= 0 && tcoord < w) {
ui->hshow = 0; ui->dragpos = tcoord;
} else { return "";
ui->hx = tx;
ui->hy = ty;
ui->hshow = !state->immutable[ty*w+tx];
ui->hpencil = 0;
} }
ui->hcursor = 0; } else if (IS_MOUSE_RELEASE(button)) {
return ""; /* UI activity occurred */ ui->drag = 0; /* end drag */
if (state->sequence[ui->dragpos] == ui->dragnum)
return ""; /* drag was a no-op overall */
sprintf(buf, "D%d,%d", ui->dragnum, ui->dragpos);
return dupstr(buf);
} }
if (button == RIGHT_BUTTON) { } else if (IS_MOUSE_DOWN(button)) {
/* if (tx >= 0 && tx < w && ty >= 0 && ty < w) {
* Pencil-mode highlighting for non filled squares. tx = state->sequence[tx];
*/ ty = state->sequence[ty];
if (state->grid[ty*w+tx] == 0) { if (button == LEFT_BUTTON) {
if (tx == ui->hx && ty == ui->hy && if (tx == ui->hx && ty == ui->hy &&
ui->hshow && ui->hpencil) { ui->hshow && ui->hpencil == 0) {
ui->hshow = 0; ui->hshow = 0;
} else { } else {
ui->hpencil = 1;
ui->hx = tx; ui->hx = tx;
ui->hy = ty; ui->hy = ty;
ui->hshow = 1; ui->hshow = !state->immutable[ty*w+tx];
ui->hpencil = 0;
} }
} else { ui->hcursor = 0;
ui->hshow = 0; return ""; /* UI activity occurred */
} }
ui->hcursor = 0; if (button == RIGHT_BUTTON) {
return ""; /* UI activity occurred */ /*
* Pencil-mode highlighting for non filled squares.
*/
if (state->grid[ty*w+tx] == 0) {
if (tx == ui->hx && ty == ui->hy &&
ui->hshow && ui->hpencil) {
ui->hshow = 0;
} else {
ui->hpencil = 1;
ui->hx = tx;
ui->hy = ty;
ui->hshow = 1;
}
} else {
ui->hshow = 0;
}
ui->hcursor = 0;
return ""; /* UI activity occurred */
}
} else if (tx >= 0 && tx < w && ty == -1) {
ui->drag = 2;
ui->dragnum = state->sequence[tx];
ui->dragpos = tx;
return "";
} else if (ty >= 0 && ty < w && tx == -1) {
ui->drag = 1;
ui->dragnum = state->sequence[ty];
ui->dragpos = ty;
return "";
} }
} }
if (IS_CURSOR_MOVE(button)) { if (IS_CURSOR_MOVE(button)) {
move_cursor(button, &ui->hx, &ui->hy, w, w, 0); move_cursor(button, &ui->hx, &ui->hy, w, w, 0);
ui->hshow = ui->hcursor = 1; ui->hshow = ui->hcursor = 1;
@ -1255,7 +1302,7 @@ static game_state *execute_move(game_state *from, char *move)
{ {
int w = from->par.w, a = w*w; int w = from->par.w, a = w*w;
game_state *ret; game_state *ret;
int x, y, i, n; int x, y, i, j, n;
if (move[0] == 'S') { if (move[0] == 'S') {
ret = dup_game(from); ret = dup_game(from);
@ -1306,6 +1353,23 @@ static game_state *execute_move(game_state *from, char *move)
ret->pencil[i] = (1 << (w+1)) - (1 << 1); ret->pencil[i] = (1 << (w+1)) - (1 << 1);
} }
return ret; return ret;
} else if (move[0] == 'D' &&
sscanf(move+1, "%d,%d", &x, &y) == 2) {
/*
* Reorder the rows and columns so that digit x is in position
* y.
*/
ret = dup_game(from);
for (i = j = 0; i < w; i++) {
if (i == y) {
ret->sequence[i] = x;
} else {
if (from->sequence[j] == x)
j++;
ret->sequence[i] = from->sequence[j++];
}
}
return ret;
} else } else
return NULL; /* couldn't parse move string */ return NULL; /* couldn't parse move string */
} }
@ -1373,10 +1437,14 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
ds->tilesize = 0; ds->tilesize = 0;
ds->started = FALSE; ds->started = FALSE;
ds->tiles = snewn(a, long); ds->tiles = snewn(a, long);
ds->legend = snewn(w, long);
ds->pencil = snewn(a, long); ds->pencil = snewn(a, long);
ds->errors = snewn(a, long); ds->errors = snewn(a, long);
ds->sequence = snewn(a, digit);
for (i = 0; i < a; i++) for (i = 0; i < a; i++)
ds->tiles[i] = ds->pencil[i] = -1; ds->tiles[i] = ds->pencil[i] = -1;
for (i = 0; i < w; i++)
ds->legend[i] = -1;
ds->errtmp = snewn(a, long); ds->errtmp = snewn(a, long);
return ds; return ds;
@ -1388,6 +1456,7 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
sfree(ds->pencil); sfree(ds->pencil);
sfree(ds->errors); sfree(ds->errors);
sfree(ds->errtmp); sfree(ds->errtmp);
sfree(ds->sequence);
sfree(ds); sfree(ds);
} }
@ -1407,6 +1476,14 @@ static void draw_tile(drawing *dr, game_drawstate *ds, int x, int y, long tile,
cw = tw = TILESIZE-1; cw = tw = TILESIZE-1;
ch = th = TILESIZE-1; ch = th = TILESIZE-1;
if (tile & DF_LEGEND) {
cx += TILESIZE/10;
cy += TILESIZE/10;
cw -= TILESIZE/5;
ch -= TILESIZE/5;
tile |= DF_IMMUTABLE;
}
clip(dr, cx, cy, cw, ch); clip(dr, cx, cy, cw, ch);
/* background needs erasing */ /* background needs erasing */
@ -1550,7 +1627,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
float animtime, float flashtime) float animtime, float flashtime)
{ {
int w = state->par.w /*, a = w*w */; int w = state->par.w /*, a = w*w */;
int x, y; int x, y, i, j;
if (!ds->started) { if (!ds->started) {
/* /*
@ -1568,21 +1645,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2, w*TILESIZE+1+GRIDEXTRA*2,
COL_GRID); COL_GRID);
/*
* Table legend.
*/
for (x = 0; x < w; x++) {
char str[2];
str[1] = '\0';
str[0] = TOCHAR(x+1, ds->par.id);
draw_text(dr, COORD(x) + TILESIZE/2, BORDER + TILESIZE/2,
FONT_VARIABLE, TILESIZE/2,
ALIGN_VCENTRE | ALIGN_HCENTRE, COL_GRID, str);
draw_text(dr, BORDER + TILESIZE/2, COORD(x) + TILESIZE/2,
FONT_VARIABLE, TILESIZE/2,
ALIGN_VCENTRE | ALIGN_HCENTRE, COL_GRID, str);
}
draw_update(dr, 0, 0, SIZE(w), SIZE(w)); draw_update(dr, 0, 0, SIZE(w), SIZE(w));
ds->started = TRUE; ds->started = TRUE;
@ -1590,19 +1652,57 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
check_errors(state, ds->errtmp); check_errors(state, ds->errtmp);
/*
* Construct a modified version of state->sequence which takes
* into account an unfinished drag operation.
*/
if (ui->drag) {
x = ui->dragnum;
y = ui->dragpos;
} else {
x = y = -1;
}
for (i = j = 0; i < w; i++) {
if (i == y) {
ds->sequence[i] = x;
} else {
if (state->sequence[j] == x)
j++;
ds->sequence[i] = state->sequence[j++];
}
}
/*
* Draw the table legend.
*/
for (x = 0; x < w; x++) {
int sx = ds->sequence[x];
long tile = (sx+1) | DF_LEGEND;
if (ds->legend[x] != tile) {
ds->legend[x] = tile;
draw_tile(dr, ds, -1, x, tile, 0, 0);
draw_tile(dr, ds, x, -1, tile, 0, 0);
}
}
for (y = 0; y < w; y++) { for (y = 0; y < w; y++) {
int sy = ds->sequence[y];
for (x = 0; x < w; x++) { for (x = 0; x < w; x++) {
long tile = 0L, pencil = 0L, error; long tile = 0L, pencil = 0L, error;
int sx = ds->sequence[x];
if (state->grid[y*w+x]) if (state->grid[sy*w+sx])
tile = state->grid[y*w+x]; tile = state->grid[sy*w+sx];
else else
pencil = (long)state->pencil[y*w+x]; pencil = (long)state->pencil[sy*w+sx];
if (state->immutable[y*w+x]) if (state->immutable[sy*w+sx])
tile |= DF_IMMUTABLE; tile |= DF_IMMUTABLE;
if (ui->hshow && ui->hx == x && ui->hy == y) if ((ui->drag == 1 && ui->dragnum == sy) ||
(ui->drag == 2 && ui->dragnum == sx))
tile |= DF_HIGHLIGHT;
else if (ui->hshow && ui->hx == sx && ui->hy == sy)
tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT); tile |= (ui->hpencil ? DF_HIGHLIGHT_PENCIL : DF_HIGHLIGHT);
if (flashtime > 0 && if (flashtime > 0 &&