#include "puzzles.h"
#include "tree234.h"
-const char *const game_name = "Net";
-const char *const game_winhelp_topic = "games.net";
-const int game_can_configure = TRUE;
-
#define PI 3.141592653589793238462643383279502884197169399
#define MATMUL(xr,yr,m,x,y) do { \
/* ----------------------------------------------------------------------
* Manage game parameters.
*/
-game_params *default_params(void)
+static game_params *default_params(void)
{
game_params *ret = snew(game_params);
return ret;
}
-int game_fetch_preset(int i, char **name, game_params **params)
+static int game_fetch_preset(int i, char **name, game_params **params)
{
game_params *ret;
char str[80];
return TRUE;
}
-void free_params(game_params *params)
+static void free_params(game_params *params)
{
sfree(params);
}
-game_params *dup_params(game_params *params)
+static game_params *dup_params(game_params *params)
{
game_params *ret = snew(game_params);
*ret = *params; /* structure copy */
return ret;
}
-game_params *decode_params(char const *string)
+static game_params *decode_params(char const *string)
{
game_params *ret = default_params();
char const *p = string;
return ret;
}
-char *encode_params(game_params *params)
+static char *encode_params(game_params *params)
{
char ret[400];
int len;
return dupstr(ret);
}
-config_item *game_configure(game_params *params)
+static config_item *game_configure(game_params *params)
{
config_item *ret;
char buf[80];
return ret;
}
-game_params *custom_params(config_item *cfg)
+static game_params *custom_params(config_item *cfg)
{
game_params *ret = snew(game_params);
return ret;
}
-char *validate_params(game_params *params)
+static char *validate_params(game_params *params)
{
if (params->width <= 0 && params->height <= 0)
return "Width and height must both be greater than zero";
* Randomly select a new game seed.
*/
-char *new_game_seed(game_params *params, random_state *rs)
+static char *new_game_seed(game_params *params, random_state *rs)
{
/*
* The full description of a Net game is far too large to
return dupstr(buf);
}
-char *validate_seed(game_params *params, char *seed)
+static char *validate_seed(game_params *params, char *seed)
{
/*
* Since any string at all will suffice to seed the RNG, there
* Construct an initial game state, given a seed and parameters.
*/
-game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(game_params *params, char *seed)
{
random_state *rs;
game_state *state;
state->cx = state->width / 2;
state->cy = state->height / 2;
state->wrapping = params->wrapping;
- state->last_rotate_dir = +1; /* *shrug* */
+ state->last_rotate_dir = 0;
state->completed = FALSE;
state->tiles = snewn(state->width * state->height, unsigned char);
memset(state->tiles, 0, state->width * state->height);
return state;
}
-game_state *dup_game(game_state *state)
+static game_state *dup_game(game_state *state)
{
game_state *ret;
return ret;
}
-void free_game(game_state *state)
+static void free_game(game_state *state)
{
sfree(state->tiles);
sfree(state->barriers);
struct game_ui {
int cur_x, cur_y;
int cur_visible;
+ random_state *rs; /* used for jumbling */
};
-game_ui *new_ui(game_state *state)
+static game_ui *new_ui(game_state *state)
{
+ void *seed;
+ int seedsize;
game_ui *ui = snew(game_ui);
ui->cur_x = state->width / 2;
ui->cur_y = state->height / 2;
ui->cur_visible = FALSE;
+ get_random_seed(&seed, &seedsize);
+ ui->rs = random_init(seed, seedsize);
+ sfree(seed);
return ui;
}
-void free_ui(game_ui *ui)
+static void free_ui(game_ui *ui)
{
+ random_free(ui->rs);
sfree(ui);
}
/* ----------------------------------------------------------------------
* Process a move.
*/
-game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
+static game_state *make_move(game_state *state, game_ui *ui,
+ int x, int y, int button)
{
game_state *ret, *nullret;
int tx, ty, orig;
else if (button == 'd' || button == 'D')
button = RIGHT_BUTTON;
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
return nullret;
* unlocks it.)
*/
if (button == MIDDLE_BUTTON) {
+
ret = dup_game(state);
tile(ret, tx, ty) ^= LOCKED;
+ ret->last_rotate_dir = 0;
return ret;
- }
- /*
- * The left and right buttons have no effect if clicked on a
- * locked tile.
- */
- if (tile(state, tx, ty) & LOCKED)
- return nullret;
+ } else if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
- /*
- * Otherwise, turn the tile one way or the other. Left button
- * turns anticlockwise; right button turns clockwise.
- */
- 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;
- }
+ /*
+ * 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
+ * turns anticlockwise; right button turns clockwise.
+ */
+ 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.
unsigned char *visible;
};
-game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(game_state *state)
{
game_drawstate *ds = snew(game_drawstate);
return ds;
}
-void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(game_drawstate *ds)
{
sfree(ds->visible);
sfree(ds);
}
-void game_size(game_params *params, int *x, int *y)
+static void game_size(game_params *params, int *x, int *y)
{
*x = WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
*y = WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
}
-float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, game_state *state, int *ncolours)
{
float *ret;
draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
}
-void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
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;
float angle = 0.0;
}
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.
*/
for (x = 0; x < oldstate->width; x++)
break_label:
if (tx >= 0) {
- if (tile(state, tx, ty) == ROT(tile(oldstate, tx, ty),
- state->last_rotate_dir))
- angle = state->last_rotate_dir * 90.0F * (t / ROTATE_TIME);
- else
- angle = state->last_rotate_dir * -90.0F * (t / ROTATE_TIME);
+ angle = last_rotate_dir * dir * 90.0F * (t / ROTATE_TIME);
state = oldstate;
}
}
sfree(active);
}
-float game_anim_length(game_state *oldstate, game_state *newstate, int dir)
+static 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
- * animate its rotation.
+ * Don't animate if last_rotate_dir is zero.
*/
- 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;
- }
+ last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
+ newstate->last_rotate_dir;
+ if (last_rotate_dir) {
+
+ /*
+ * 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;
}
-float game_flash_length(game_state *oldstate, game_state *newstate, int dir)
+static float game_flash_length(game_state *oldstate,
+ game_state *newstate, int dir)
{
/*
* If the game has just been completed, we display a completion
return 0.0F;
}
-int game_wants_statusbar(void)
+static int game_wants_statusbar(void)
{
return TRUE;
}
+
+#ifdef COMBINED
+#define thegame net
+#endif
+
+const struct game thegame = {
+ "Net", "games.net", TRUE,
+ default_params,
+ game_fetch_preset,
+ decode_params,
+ encode_params,
+ free_params,
+ dup_params,
+ game_configure,
+ custom_params,
+ validate_params,
+ new_game_seed,
+ validate_seed,
+ new_game,
+ dup_game,
+ free_game,
+ new_ui,
+ free_ui,
+ make_move,
+ game_size,
+ game_colours,
+ game_new_drawstate,
+ game_free_drawstate,
+ game_redraw,
+ game_anim_length,
+ game_flash_length,
+ game_wants_statusbar,
+};