+ return dupstr("S");
+}
+
+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 x, y, col, maxlen;
+
+ /*
+ * First work out how many characters we need to display each
+ * number.
+ */
+ col = sprintf(buf, "%d", state->n);
+
+ /*
+ * Now we know the exact total size of the grid we're going to
+ * produce: it's got h rows, each containing w lots of col, w-1
+ * spaces and a trailing newline.
+ */
+ maxlen = state->h * state->w * (col+1);
+
+ ret = snewn(maxlen+1, char);
+ p = ret;
+
+ for (y = 0; y < state->h; y++) {
+ for (x = 0; x < state->w; x++) {
+ int v = state->tiles[state->w*y+x];
+ sprintf(buf, "%*d", col, v);
+ memcpy(p, buf, col);
+ p += col;
+ if (x+1 == state->w)
+ *p++ = '\n';
+ else
+ *p++ = ' ';
+ }
+ }
+
+ assert(p - ret == maxlen);
+ *p = '\0';
+ return ret;
+}
+
+enum cursor_mode { unlocked, lock_tile, lock_position };
+
+struct game_ui {
+ int cur_x, cur_y;
+ int cur_visible;
+ enum cursor_mode cur_mode;
+};
+
+static game_ui *new_ui(const game_state *state)
+{
+ game_ui *ui = snew(game_ui);
+ ui->cur_x = 0;
+ ui->cur_y = 0;
+ ui->cur_visible = FALSE;
+ ui->cur_mode = unlocked;
+
+ return ui;
+}
+
+static void free_ui(game_ui *ui)
+{
+ sfree(ui);
+}
+
+static char *encode_ui(const game_ui *ui)
+{
+ return NULL;
+}
+
+static void decode_ui(game_ui *ui, const char *encoding)
+{
+}
+
+static void game_changed_state(game_ui *ui, const game_state *oldstate,
+ const game_state *newstate)
+{
+}
+
+struct game_drawstate {
+ int started;
+ int w, h, bgcolour;
+ int *tiles;
+ int tilesize;
+ int cur_x, cur_y;
+};
+
+static char *interpret_move(const game_state *state, game_ui *ui,
+ const game_drawstate *ds,
+ int x, int y, int button)
+{
+ int cx = -1, cy = -1, dx, dy;
+ char buf[80];
+ int shift = button & MOD_SHFT, control = button & MOD_CTRL,
+ pad = button & MOD_NUM_KEYPAD;
+
+ button &= ~MOD_MASK;
+
+ if (IS_CURSOR_MOVE(button) || pad) {
+ if (!ui->cur_visible) {
+ ui->cur_visible = 1;
+ return "";
+ }
+
+ if (control || shift || ui->cur_mode) {
+ int x = ui->cur_x, y = ui->cur_y, xwrap = x, ywrap = y;
+ if (x < 0 || x >= state->w || y < 0 || y >= state->h)
+ return NULL;
+ move_cursor(button | pad, &x, &y,
+ state->w, state->h, FALSE);
+ move_cursor(button | pad, &xwrap, &ywrap,
+ state->w, state->h, TRUE);
+
+ if (x != xwrap) {
+ sprintf(buf, "R%d,%c1", y, x ? '+' : '-');
+ } else if (y != ywrap) {
+ sprintf(buf, "C%d,%c1", x, y ? '+' : '-');
+ } else if (x == ui->cur_x)
+ sprintf(buf, "C%d,%d", x, y - ui->cur_y);
+ else
+ sprintf(buf, "R%d,%d", y, x - ui->cur_x);
+
+ if (control || (!shift && ui->cur_mode == lock_tile)) {
+ ui->cur_x = xwrap;
+ ui->cur_y = ywrap;
+ }
+
+ return dupstr(buf);
+ } else {
+ int x = ui->cur_x + 1, y = ui->cur_y + 1;
+
+ move_cursor(button | pad, &x, &y,
+ state->w + 2, state->h + 2, FALSE);
+
+ if (x == 0 && y == 0) {
+ int t = ui->cur_x;
+ ui->cur_x = ui->cur_y;
+ ui->cur_y = t;
+ } else if (x == 0 && y == state->h + 1) {
+ int t = ui->cur_x;
+ ui->cur_x = (state->h - 1) - ui->cur_y;
+ ui->cur_y = (state->h - 1) - t;
+ } else if (x == state->w + 1 && y == 0) {
+ int t = ui->cur_x;
+ ui->cur_x = (state->w - 1) - ui->cur_y;
+ ui->cur_y = (state->w - 1) - t;
+ } else if (x == state->w + 1 && y == state->h + 1) {
+ int t = ui->cur_x;
+ ui->cur_x = state->w - state->h + ui->cur_y;
+ ui->cur_y = state->h - state->w + t;
+ } else {
+ ui->cur_x = x - 1;
+ ui->cur_y = y - 1;
+ }