Add a jumble' key (J') to Net, which scrambles the positions of all unlocked

tiles randomly. (Rachel asked for this; it's been being tested for a good few
months now, and Simon didn't care either way, so in it goes :)

As part of this, the front end can now be asked to provide a random random
seed (IYSWIM).

[originally from svn r5019]
This commit is contained in:
Jacob Nevins
2004-12-22 19:27:26 +00:00
parent ec3d23c9d9
commit 46fa25240e
6 changed files with 109 additions and 42 deletions

12
gtk.c
View File

@ -73,6 +73,14 @@ struct frontend {
GtkWidget *cfgbox; GtkWidget *cfgbox;
}; };
void get_random_seed(void **randseed, int *randseedsize)
{
time_t *tp = snew(time_t);
time(tp);
*randseed = (void *)tp;
*randseedsize = sizeof(time_t);
}
void frontend_default_colour(frontend *fe, float *output) void frontend_default_colour(frontend *fe, float *output)
{ {
GdkColor col = fe->window->style->bg[GTK_STATE_NORMAL]; GdkColor col = fe->window->style->bg[GTK_STATE_NORMAL];
@ -772,12 +780,10 @@ static frontend *new_window(char *game_id, char **error)
GtkBox *vbox; GtkBox *vbox;
GtkWidget *menubar, *menu, *menuitem; GtkWidget *menubar, *menu, *menuitem;
int x, y, n; int x, y, n;
time_t t;
fe = snew(frontend); fe = snew(frontend);
time(&t); fe->me = midend_new(fe);
fe->me = midend_new(fe, &t, sizeof(t));
if (game_id) { if (game_id) {
*error = midend_game_id(fe->me, game_id, FALSE); *error = midend_game_id(fe->me, game_id, FALSE);
if (*error) { if (*error) {

View File

@ -40,9 +40,13 @@ struct midend_data {
} \ } \
} while (0) } while (0)
midend_data *midend_new(frontend *fe, void *randseed, int randseedsize) midend_data *midend_new(frontend *fe)
{ {
midend_data *me = snew(midend_data); midend_data *me = snew(midend_data);
void *randseed;
int randseedsize;
get_random_seed(&randseed, &randseedsize);
me->frontend = fe; me->frontend = fe;
me->random = random_init(randseed, randseedsize); me->random = random_init(randseed, randseedsize);
@ -61,6 +65,8 @@ midend_data *midend_new(frontend *fe, void *randseed, int randseedsize)
me->dir = 0; me->dir = 0;
me->ui = NULL; me->ui = NULL;
sfree(randseed);
return me; return me;
} }

111
net.c
View File

@ -344,7 +344,7 @@ game_state *new_game(game_params *params, char *seed)
state->cx = state->width / 2; state->cx = state->width / 2;
state->cy = state->height / 2; state->cy = state->height / 2;
state->wrapping = params->wrapping; state->wrapping = params->wrapping;
state->last_rotate_dir = +1; /* *shrug* */ state->last_rotate_dir = 0;
state->completed = FALSE; state->completed = FALSE;
state->tiles = snewn(state->width * state->height, unsigned char); state->tiles = snewn(state->width * state->height, unsigned char);
memset(state->tiles, 0, state->width * state->height); memset(state->tiles, 0, state->width * state->height);
@ -767,20 +767,27 @@ static unsigned char *compute_active(game_state *state)
struct game_ui { struct game_ui {
int cur_x, cur_y; int cur_x, cur_y;
int cur_visible; int cur_visible;
random_state *rs; /* used for jumbling */
}; };
game_ui *new_ui(game_state *state) game_ui *new_ui(game_state *state)
{ {
void *seed;
int seedsize;
game_ui *ui = snew(game_ui); game_ui *ui = snew(game_ui);
ui->cur_x = state->width / 2; ui->cur_x = state->width / 2;
ui->cur_y = state->height / 2; ui->cur_y = state->height / 2;
ui->cur_visible = FALSE; ui->cur_visible = FALSE;
get_random_seed(&seed, &seedsize);
ui->rs = random_init(seed, seedsize);
sfree(seed);
return ui; return ui;
} }
void free_ui(game_ui *ui) void free_ui(game_ui *ui)
{ {
random_free(ui->rs);
sfree(ui); sfree(ui);
} }
@ -842,6 +849,10 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
else if (button == 'd' || button == 'D') else if (button == 'd' || button == 'D')
button = RIGHT_BUTTON; button = RIGHT_BUTTON;
ui->cur_visible = TRUE; ui->cur_visible = TRUE;
} else if (button == 'j' || button == 'J') {
/* XXX should we have some mouse control for this? */
button = 'J'; /* canonify */
tx = ty = -1; /* shut gcc up :( */
} else } else
return nullret; return nullret;
@ -856,31 +867,54 @@ game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
* unlocks it.) * unlocks it.)
*/ */
if (button == MIDDLE_BUTTON) { if (button == MIDDLE_BUTTON) {
ret = dup_game(state); ret = dup_game(state);
tile(ret, tx, ty) ^= LOCKED; tile(ret, tx, ty) ^= LOCKED;
ret->last_rotate_dir = 0;
return ret; return ret;
}
/* } else if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
* The left and right buttons have no effect if clicked on a
* locked tile.
*/
if (tile(state, tx, ty) & LOCKED)
return nullret;
/* /*
* Otherwise, turn the tile one way or the other. Left button * The left and right buttons have no effect if clicked on a
* turns anticlockwise; right button turns clockwise. * locked tile.
*/ */
ret = dup_game(state); if (tile(state, tx, ty) & LOCKED)
orig = tile(ret, tx, ty); return nullret;
if (button == LEFT_BUTTON) {
tile(ret, tx, ty) = A(orig); /*
ret->last_rotate_dir = +1; * Otherwise, turn the tile one way or the other. Left button
} else { * turns anticlockwise; right button turns clockwise.
tile(ret, tx, ty) = C(orig); */
ret->last_rotate_dir = -1; ret = dup_game(state);
} orig = tile(ret, tx, ty);
if (button == LEFT_BUTTON) {
tile(ret, tx, ty) = A(orig);
ret->last_rotate_dir = +1;
} else {
tile(ret, tx, ty) = C(orig);
ret->last_rotate_dir = -1;
}
} else if (button == 'J') {
/*
* Jumble all unlocked tiles to random orientations.
*/
int jx, jy;
ret = dup_game(state);
for (jy = 0; jy < ret->height; jy++) {
for (jx = 0; jx < ret->width; jx++) {
if (!(tile(ret, jx, jy) & LOCKED)) {
int rot = random_upto(ui->rs, 4);
orig = tile(ret, jx, jy);
tile(ret, jx, jy) = ROT(orig, rot);
}
}
}
ret->last_rotate_dir = 0; /* suppress animation */
} else assert(0);
/* /*
* Check whether the game has been completed. * Check whether the game has been completed.
@ -1253,7 +1287,7 @@ static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile,
void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate, void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
game_state *state, int dir, game_ui *ui, float t, float ft) game_state *state, int dir, game_ui *ui, float t, float ft)
{ {
int x, y, tx, ty, frame; int x, y, tx, ty, frame, last_rotate_dir;
unsigned char *active; unsigned char *active;
float angle = 0.0; float angle = 0.0;
@ -1309,9 +1343,11 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
} }
tx = ty = -1; tx = ty = -1;
if (oldstate && (t < ROTATE_TIME)) { last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
state->last_rotate_dir;
if (oldstate && (t < ROTATE_TIME) && last_rotate_dir) {
/* /*
* We're animating a tile rotation. Find the turning tile, * We're animating a single tile rotation. Find the turning tile,
* if any. * if any.
*/ */
for (x = 0; x < oldstate->width; x++) for (x = 0; x < oldstate->width; x++)
@ -1323,8 +1359,6 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
break_label: break_label:
if (tx >= 0) { if (tx >= 0) {
int last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
state->last_rotate_dir;
angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME); angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
state = oldstate; state = oldstate;
} }
@ -1404,17 +1438,26 @@ void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
float game_anim_length(game_state *oldstate, game_state *newstate, int dir) float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
{ {
int x, y; int x, y, last_rotate_dir;
/* /*
* If there's a tile which has been rotated, allow time to * Don't animate if last_rotate_dir is zero.
* animate its rotation.
*/ */
for (x = 0; x < oldstate->width; x++) last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
for (y = 0; y < oldstate->height; y++) newstate->last_rotate_dir;
if ((tile(oldstate, x, y) ^ tile(newstate, x, y)) & 0xF) { if (last_rotate_dir) {
return ROTATE_TIME;
} /*
* If there's a tile which has been rotated, allow time to
* animate its rotation.
*/
for (x = 0; x < oldstate->width; x++)
for (y = 0; y < oldstate->height; y++)
if ((tile(oldstate, x, y) ^ tile(newstate, x, y)) & 0xF) {
return ROTATE_TIME;
}
}
return 0.0F; return 0.0F;
} }

View File

@ -203,6 +203,11 @@ controls are:
also unlock it again, but while it's locked you can't accidentally also unlock it again, but while it's locked you can't accidentally
turn it. turn it.
\dt \e{Jumble tiles}: \q{J} key
\dd This key turns all tiles that are not locked to random
orientations.
(All the actions described in \k{common-actions} are also available.) (All the actions described in \k{common-actions} are also available.)
\H{net-params} \I{parameters, for Net}Net parameters \H{net-params} \I{parameters, for Net}Net parameters

View File

@ -106,11 +106,12 @@ void end_draw(frontend *fe);
void deactivate_timer(frontend *fe); void deactivate_timer(frontend *fe);
void activate_timer(frontend *fe); void activate_timer(frontend *fe);
void status_bar(frontend *fe, char *text); void status_bar(frontend *fe, char *text);
void get_random_seed(void **randseed, int *randseedsize);
/* /*
* midend.c * midend.c
*/ */
midend_data *midend_new(frontend *fe, void *randseed, int randseedsize); midend_data *midend_new(frontend *fe);
void midend_free(midend_data *me); void midend_free(midend_data *me);
void midend_set_params(midend_data *me, game_params *params); void midend_set_params(midend_data *me, game_params *params);
void midend_size(midend_data *me, int *x, int *y); void midend_size(midend_data *me, int *x, int *y);

View File

@ -121,6 +121,14 @@ void fatal(char *fmt, ...)
exit(1); exit(1);
} }
void get_random_seed(void **randseed, int *randseedsize)
{
time_t *tp = snew(time_t);
time(tp);
*randseed = (void *)tp;
*randseedsize = sizeof(time_t);
}
void status_bar(frontend *fe, char *text) void status_bar(frontend *fe, char *text)
{ {
SetWindowText(fe->statusbar, text); SetWindowText(fe->statusbar, text);
@ -352,12 +360,10 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
int x, y; int x, y;
RECT r, sr; RECT r, sr;
HDC hdc; HDC hdc;
time_t t;
fe = snew(frontend); fe = snew(frontend);
time(&t); fe->me = midend_new(fe);
fe->me = midend_new(fe, &t, sizeof(t));
if (game_id) { if (game_id) {
*error = midend_game_id(fe->me, game_id, FALSE); *error = midend_game_id(fe->me, game_id, FALSE);