/*
* 'same game' -- try to remove all the coloured squares by
* selecting regions of contiguous colours.
*/
/*
* TODO on grid generation:
*
* - Generation speed could still be improved.
* * 15x10c3 is the only really difficult one of the existing
* presets. The others are all either small enough, or have
* the great flexibility given by four colours, that they
* don't take long at all.
* * I still suspect many problems arise from separate
* subareas. I wonder if we can also somehow prioritise left-
* or rightmost insertions so as to avoid area splitting at
* all where feasible? It's not easy, though, because the
* current shuffle-then-try-all-options approach to move
* choice doesn't leave room for `soft' probabilistic
* prioritisation: we either try all class A moves before any
* class B ones, or we don't.
*
* - The current generation algorithm inserts exactly two squares
* at a time, with a single exception at the beginning of
* generation for grids of odd overall size. An obvious
* extension would be to permit larger inverse moves during
* generation.
* * this might reduce the number of failed generations by
* making the insertion algorithm more flexible
* * on the other hand, it would be significantly more complex
* * if I do this I'll need to take out the odd-subarea
* avoidance
* * a nice feature of the current algorithm is that the
* computer's `intended' solution always receives the minimum
* possible score, so that pretty much the player's entire
* score represents how much better they did than the
* computer.
*
* - Is it possible we can _temporarily_ tolerate neighbouring
* squares of the same colour, until we've finished setting up
* our inverse move?
* * or perhaps even not choose the colour of our inserted
* region until we have finished placing it, and _then_ look
* at what colours border on it?
* * I don't think this is currently meaningful unless we're
* placing more than a domino at a time.
*
* - possibly write out a full solution so that Solve can somehow
* show it step by step?
* * aux_info would have to encode the click points
* * solve_game() would have to encode not only those click
* points but also give a move string which reconstructed the
* initial state
* * the game_state would include a pointer to a solution move
* list, plus an index into that list
* * game_changed_state would auto-select the next move if
* handed a new state which had a solution move list active
* * execute_move, if passed such a state as input, would check
* to see whether the move being made was the same as the one
* stated by the solution, and if so would advance the move
* index. Failing that it would return a game_state without a
* solution move list active at all.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include "puzzles.h"
#define TILE_INNER (ds->tileinner)
#define TILE_GAP (ds->tilegap)
#define TILE_SIZE (TILE_INNER + TILE_GAP)
#define PREFERRED_TILE_SIZE 32
#define BORDER (TILE_SIZE / 2)
#define HIGHLIGHT_WIDTH 2
#define FLASH_FRAME 0.13F
#define COORD(x) ( (x) * TILE_SIZE + BORDER )
#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
#define X(state, i) ( (i) % (state)->params.w )
#define Y(state, i) ( (i) / (state)->params.w )
#define C(state, x, y) ( (y) * (state)->w + (x) )
enum {
COL_BACKGROUND,
COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9,
COL_IMPOSSIBLE, COL_SEL, COL_HIGHLIGHT, COL_LOWLIGHT,
NCOLOURS
};
/* scoresub is 1 or 2 (for (n-1)^2 or (n-2)^2) */
struct game_params {
int w, h, ncols, scoresub;
bool soluble; /* choose generation algorithm */
};
/* These flags must be unique across all uses; in the game_state,
* the game_ui, and the drawstate (as they all get combined in the
* drawstate). */
#define TILE_COLMASK 0x00ff
#define TILE_SELECTED 0x0100 /* used in ui and drawstate */
#define TILE_JOINRIGHT 0x0200 /* used in drawstate */
#define TILE_JOINDOWN 0x0400 /* used in drawstate */
#define TILE_JOINDIAG 0x0800 /* used in drawstate */
#define TILE_HASSEL 0x1000 /* used in drawstate */
#define TILE_IMPOSSIBLE 0x2000 /* used in drawstate */
#define TILE(gs,x,y) ((gs)->tiles[(gs)->params.w*(y)+(x)])
#define COL(gs,x,y) (TILE(gs,x,y) & TILE_COLMASK)
#define ISSEL(gs,x,y) (TILE(gs,x,y) & TILE_SELECTED)
#define SWAPTILE(gs,x1,y1,x2,y2) do { \
int t = TILE(gs,x1,y1); \
|