#include "puzzles.h"
+#define PI 3.14159265358979323846264338327950884197169399
+
#define MAXVERTICES 20
#define MAXFACES 20
#define MAXORDER 4
return ret;
}
-static game_params *decode_params(char const *string)
+static void decode_params(game_params *ret, char const *string)
{
- game_params *ret = default_params();
-
switch (*string) {
case 't': ret->solid = TETRAHEDRON; string++; break;
case 'c': ret->solid = CUBE; string++; break;
string++;
ret->d2 = atoi(string);
}
-
- return ret;
}
-static char *encode_params(game_params *params)
+static char *encode_params(game_params *params, int full)
{
char data[256];
data->squareindex++;
}
-static char *new_game_seed(game_params *params, random_state *rs,
- game_aux_info **aux)
+static char *new_game_desc(game_params *params, random_state *rs,
+ game_aux_info **aux, int interactive)
{
struct grid_data data;
int i, j, k, m, area, facesperclass;
int *flags;
- char *seed, *p;
+ char *desc, *p;
/*
* Enumerate the grid squares, dividing them into equivalence
* the non-blue squares into a list in the now-unused gridptrs
* array.
*/
- seed = snewn(area / 4 + 40, char);
- p = seed;
+ desc = snewn(area / 4 + 40, char);
+ p = desc;
j = 0;
k = 8;
m = 0;
sfree(data.gridptrs[0]);
sfree(flags);
- return seed;
+ return desc;
}
static void game_free_aux_info(game_aux_info *aux)
return ret;
}
-static char *validate_seed(game_params *params, char *seed)
+static char *validate_desc(game_params *params, char *desc)
{
int area = grid_area(params->d1, params->d2, solids[params->solid]->order);
int i, j;
i = (area + 3) / 4;
for (j = 0; j < i; j++) {
- int c = seed[j];
+ int c = desc[j];
if (c >= '0' && c <= '9') continue;
if (c >= 'A' && c <= 'F') continue;
if (c >= 'a' && c <= 'f') continue;
return "Not enough hex digits at start of string";
- /* NB if seed[j]=='\0' that will also be caught here, so we're safe */
+ /* NB if desc[j]=='\0' that will also be caught here, so we're safe */
}
- if (seed[i] != ',')
+ if (desc[i] != ',')
return "Expected ',' after hex digits";
i++;
do {
- if (seed[i] < '0' || seed[i] > '9')
+ if (desc[i] < '0' || desc[i] > '9')
return "Expected decimal integer after ','";
i++;
- } while (seed[i]);
+ } while (desc[i]);
return NULL;
}
-static game_state *new_game(game_params *params, char *seed)
+static game_state *new_game(midend_data *me, game_params *params, char *desc)
{
game_state *state = snew(game_state);
int area;
/*
* Set up the blue squares and polyhedron position according to
- * the game seed.
+ * the game description.
*/
{
- char *p = seed;
+ char *p = desc;
int i, j, v;
j = 8;
memcpy(ret->facecolours, state->facecolours,
ret->solid->nfaces * sizeof(int));
ret->nsquares = state->nsquares;
+ ret->current = state->current;
ret->squares = snewn(ret->nsquares, struct grid_square);
memcpy(ret->squares, state->squares,
ret->nsquares * sizeof(struct grid_square));
sfree(state);
}
+static game_state *solve_game(game_state *state, game_aux_info *aux,
+ char **error)
+{
+ return NULL;
+}
+
static char *game_text_format(game_state *state)
{
return NULL;
{
}
-static game_state *make_move(game_state *from, game_ui *ui,
+struct game_drawstate {
+ int ox, oy; /* pixel position of float origin */
+};
+
+static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
int x, int y, int button)
{
int direction;
int i, j, dest, mask;
struct solid *poly;
+ button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
+
/*
- * All moves are made with the cursor keys.
+ * Moves can be made with the cursor keys or numeric keypad, or
+ * alternatively you can left-click and the polyhedron will
+ * move in the general direction of the mouse pointer.
*/
- if (button == CURSOR_UP)
+ if (button == CURSOR_UP || button == (MOD_NUM_KEYPAD | '8'))
direction = UP;
- else if (button == CURSOR_DOWN)
+ else if (button == CURSOR_DOWN || button == (MOD_NUM_KEYPAD | '2'))
direction = DOWN;
- else if (button == CURSOR_LEFT)
+ else if (button == CURSOR_LEFT || button == (MOD_NUM_KEYPAD | '4'))
direction = LEFT;
- else if (button == CURSOR_RIGHT)
+ else if (button == CURSOR_RIGHT || button == (MOD_NUM_KEYPAD | '6'))
direction = RIGHT;
- else if (button == CURSOR_UP_LEFT)
+ else if (button == (MOD_NUM_KEYPAD | '7'))
direction = UP_LEFT;
- else if (button == CURSOR_DOWN_LEFT)
+ else if (button == (MOD_NUM_KEYPAD | '1'))
direction = DOWN_LEFT;
- else if (button == CURSOR_UP_RIGHT)
+ else if (button == (MOD_NUM_KEYPAD | '9'))
direction = UP_RIGHT;
- else if (button == CURSOR_DOWN_RIGHT)
+ else if (button == (MOD_NUM_KEYPAD | '3'))
direction = DOWN_RIGHT;
- else
+ else if (button == LEFT_BUTTON) {
+ /*
+ * Find the bearing of the click point from the current
+ * square's centre.
+ */
+ int cx, cy;
+ double angle;
+
+ cx = from->squares[from->current].x * GRID_SCALE + ds->ox;
+ cy = from->squares[from->current].y * GRID_SCALE + ds->oy;
+
+ if (x == cx && y == cy)
+ return NULL; /* clicked in exact centre! */
+ angle = atan2(y - cy, x - cx);
+
+ /*
+ * There are three possibilities.
+ *
+ * - This square is a square, so we choose between UP,
+ * DOWN, LEFT and RIGHT by dividing the available angle
+ * at the 45-degree points.
+ *
+ * - This square is an up-pointing triangle, so we choose
+ * between DOWN, LEFT and RIGHT by dividing into
+ * 120-degree arcs.
+ *
+ * - This square is a down-pointing triangle, so we choose
+ * between UP, LEFT and RIGHT in the inverse manner.
+ *
+ * Don't forget that since our y-coordinates increase
+ * downwards, `angle' is measured _clockwise_ from the
+ * x-axis, not anticlockwise as most mathematicians would
+ * instinctively assume.
+ */
+ if (from->squares[from->current].npoints == 4) {
+ /* Square. */
+ if (fabs(angle) > 3*PI/4)
+ direction = LEFT;
+ else if (fabs(angle) < PI/4)
+ direction = RIGHT;
+ else if (angle > 0)
+ direction = DOWN;
+ else
+ direction = UP;
+ } else if (from->squares[from->current].directions[UP] == 0) {
+ /* Up-pointing triangle. */
+ if (angle < -PI/2 || angle > 5*PI/6)
+ direction = LEFT;
+ else if (angle > PI/6)
+ direction = DOWN;
+ else
+ direction = RIGHT;
+ } else {
+ /* Down-pointing triangle. */
+ assert(from->squares[from->current].directions[DOWN] == 0);
+ if (angle > PI/2 || angle < -5*PI/6)
+ direction = LEFT;
+ else if (angle < -PI/6)
+ direction = UP;
+ else
+ direction = RIGHT;
+ }
+ } else
return NULL;
/*
float l, r, u, d;
};
-struct game_drawstate {
- int ox, oy; /* pixel position of float origin */
-};
-
static void find_bbox_callback(void *ctx, struct grid_square *sq)
{
struct bbox *bb = (struct bbox *)ctx;
}
static float game_anim_length(game_state *oldstate,
- game_state *newstate, int dir)
+ game_state *newstate, int dir, game_ui *ui)
{
return ROLLTIME;
}
static float game_flash_length(game_state *oldstate,
- game_state *newstate, int dir)
+ game_state *newstate, int dir, game_ui *ui)
{
return 0.0F;
}
return TRUE;
}
+static int game_timing_state(game_state *state)
+{
+ return TRUE;
+}
+
#ifdef COMBINED
#define thegame cube
#endif
dup_params,
TRUE, game_configure, custom_params,
validate_params,
- new_game_seed,
+ new_game_desc,
game_free_aux_info,
- validate_seed,
+ validate_desc,
new_game,
dup_game,
free_game,
- NULL, game_text_format,
+ FALSE, solve_game,
+ FALSE, game_text_format,
new_ui,
free_ui,
make_move,
game_anim_length,
game_flash_length,
game_wants_statusbar,
+ FALSE, game_timing_state,
};