-/* We're going to store lists of current candidate faces for colouring black
- * or white.
- * Each face gets a 'score', which tells us how adding that face right
- * now would affect the curliness of the solution loop. We're trying to
- * maximise that quantity so will bias our random selection of faces to
- * colour those with high scores */
-struct face_score {
- int white_score;
- int black_score;
- unsigned long random;
- /* No need to store a grid_face* here. The 'face_scores' array will
- * be a list of 'face_score' objects, one for each face of the grid, so
- * the position (index) within the 'face_scores' array will determine
- * which face corresponds to a particular face_score.
- * Having a single 'face_scores' array for all faces simplifies memory
- * management, and probably improves performance, because we don't have to
- * malloc/free each individual face_score, and we don't have to maintain
- * a mapping from grid_face* pointers to face_score* pointers.
- */
-};
-
-static int generic_sort_cmpfn(void *v1, void *v2, size_t offset)
-{
- struct face_score *f1 = v1;
- struct face_score *f2 = v2;
- int r;
-
- r = *(int *)((char *)f2 + offset) - *(int *)((char *)f1 + offset);
- if (r) {
- return r;
- }
-
- if (f1->random < f2->random)
- return -1;
- else if (f1->random > f2->random)
- return 1;
-
- /*
- * It's _just_ possible that two faces might have been given
- * the same random value. In that situation, fall back to
- * comparing based on the positions within the face_scores list.
- * This introduces a tiny directional bias, but not a significant one.
- */
- return f1 - f2;
-}
-
-static int white_sort_cmpfn(void *v1, void *v2)
-{
- return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,white_score));
-}
-
-static int black_sort_cmpfn(void *v1, void *v2)
-{
- return generic_sort_cmpfn(v1, v2, offsetof(struct face_score,black_score));
-}
-
-enum face_colour { FACE_WHITE, FACE_GREY, FACE_BLACK };
-
-/* face should be of type grid_face* here. */
-#define FACE_COLOUR(face) \
- ( (face) == NULL ? FACE_BLACK : \
- board[(face) - g->faces] )
-
-/* 'board' is an array of these enums, indicating which faces are
- * currently black/white/grey. 'colour' is FACE_WHITE or FACE_BLACK.
- * Returns whether it's legal to colour the given face with this colour. */
-static int can_colour_face(grid *g, char* board, int face_index,
- enum face_colour colour)
-{
- int i, j;
- grid_face *test_face = g->faces + face_index;
- grid_face *starting_face, *current_face;
- grid_dot *starting_dot;
- int transitions;
- int current_state, s; /* booleans: equal or not-equal to 'colour' */
- int found_same_coloured_neighbour = FALSE;
- assert(board[face_index] != colour);
-
- /* Can only consider a face for colouring if it's adjacent to a face
- * with the same colour. */
- for (i = 0; i < test_face->order; i++) {
- grid_edge *e = test_face->edges[i];
- grid_face *f = (e->face1 == test_face) ? e->face2 : e->face1;
- if (FACE_COLOUR(f) == colour) {
- found_same_coloured_neighbour = TRUE;
- break;
- }
- }
- if (!found_same_coloured_neighbour)
- return FALSE;
-
- /* Need to avoid creating a loop of faces of this colour around some
- * differently-coloured faces.
- * Also need to avoid meeting a same-coloured face at a corner, with
- * other-coloured faces in between. Here's a simple test that (I believe)
- * takes care of both these conditions:
- *
- * Take the circular path formed by this face's edges, and inflate it
- * slightly outwards. Imagine walking around this path and consider
- * the faces that you visit in sequence. This will include all faces
- * touching the given face, either along an edge or just at a corner.
- * Count the number of 'colour'/not-'colour' transitions you encounter, as
- * you walk along the complete loop. This will obviously turn out to be
- * an even number.
- * If 0, we're either in the middle of an "island" of this colour (should
- * be impossible as we're not supposed to create black or white loops),
- * or we're about to start a new island - also not allowed.
- * If 4 or greater, there are too many separate coloured regions touching
- * this face, and colouring it would create a loop or a corner-violation.
- * The only allowed case is when the count is exactly 2. */
-
- /* i points to a dot around the test face.
- * j points to a face around the i^th dot.
- * The current face will always be:
- * test_face->dots[i]->faces[j]
- * We assume dots go clockwise around the test face,
- * and faces go clockwise around dots. */
-
- /*
- * The end condition is slightly fiddly. In sufficiently strange
- * degenerate grids, our test face may be adjacent to the same
- * other face multiple times (typically if it's the exterior
- * face). Consider this, in particular:
- *
- * +--+
- * | |
- * +--+--+
- * | | |
- * +--+--+
- *
- * The bottom left face there is adjacent to the exterior face
- * twice, so we can't just terminate our iteration when we reach
- * the same _face_ we started at. Furthermore, we can't
- * condition on having the same (i,j) pair either, because
- * several (i,j) pairs identify the bottom left contiguity with
- * the exterior face! We canonicalise the (i,j) pair by taking
- * one step around before we set the termination tracking.
- */
-
- i = j = 0;
- current_face = test_face->dots[0]->faces[0];
- if (current_face == test_face) {
- j = 1;
- current_face = test_face->dots[0]->faces[1];
- }
- transitions = 0;
- current_state = (FACE_COLOUR(current_face) == colour);
- starting_dot = NULL;
- starting_face = NULL;
- while (TRUE) {
- /* Advance to next face.
- * Need to loop here because it might take several goes to
- * find it. */
- while (TRUE) {
- j++;
- if (j == test_face->dots[i]->order)
- j = 0;
-
- if (test_face->dots[i]->faces[j] == test_face) {
- /* Advance to next dot round test_face, then
- * find current_face around new dot
- * and advance to the next face clockwise */
- i++;
- if (i == test_face->order)
- i = 0;
- for (j = 0; j < test_face->dots[i]->order; j++) {
- if (test_face->dots[i]->faces[j] == current_face)
- break;
- }
- /* Must actually find current_face around new dot,
- * or else something's wrong with the grid. */
- assert(j != test_face->dots[i]->order);
- /* Found, so advance to next face and try again */
- } else {
- break;
- }
- }
- /* (i,j) are now advanced to next face */
- current_face = test_face->dots[i]->faces[j];
- s = (FACE_COLOUR(current_face) == colour);
- if (!starting_dot) {
- starting_dot = test_face->dots[i];
- starting_face = current_face;
- current_state = s;
- } else {
- if (s != current_state) {
- ++transitions;
- current_state = s;
- if (transitions > 2)
- break;
- }
- if (test_face->dots[i] == starting_dot &&
- current_face == starting_face)
- break;
- }
- }
-
- return (transitions == 2) ? TRUE : FALSE;
-}
-
-/* Count the number of neighbours of 'face', having colour 'colour' */
-static int face_num_neighbours(grid *g, char *board, grid_face *face,
- enum face_colour colour)
-{
- int colour_count = 0;
- int i;
- grid_face *f;
- grid_edge *e;
- for (i = 0; i < face->order; i++) {
- e = face->edges[i];
- f = (e->face1 == face) ? e->face2 : e->face1;
- if (FACE_COLOUR(f) == colour)
- ++colour_count;
- }
- return colour_count;
-}
-
-/* The 'score' of a face reflects its current desirability for selection
- * as the next face to colour white or black. We want to encourage moving
- * into grey areas and increasing loopiness, so we give scores according to
- * how many of the face's neighbours are currently coloured the same as the
- * proposed colour. */
-static int face_score(grid *g, char *board, grid_face *face,
- enum face_colour colour)
-{
- /* Simple formula: score = 0 - num. same-coloured neighbours,
- * so a higher score means fewer same-coloured neighbours. */
- return -face_num_neighbours(g, board, face, colour);
-}
-
-/* Generate a new complete set of clues for the given game_state.
- * The method is to generate a WHITE/BLACK colouring of all the faces,
- * such that the WHITE faces will define the inside of the path, and the
- * BLACK faces define the outside.
- * To do this, we initially colour all faces GREY. The infinite space outside
- * the grid is coloured BLACK, and we choose a random face to colour WHITE.
- * Then we gradually grow the BLACK and the WHITE regions, eliminating GREY
- * faces, until the grid is filled with BLACK/WHITE. As we grow the regions,
- * we avoid creating loops of a single colour, to preserve the topological
- * shape of the WHITE and BLACK regions.
- * We also try to make the boundary as loopy and twisty as possible, to avoid
- * generating paths that are uninteresting.
- * The algorithm works by choosing a BLACK/WHITE colour, then choosing a GREY
- * face that can be coloured with that colour (without violating the
- * topological shape of that region). It's not obvious, but I think this
- * algorithm is guaranteed to terminate without leaving any GREY faces behind.
- * Indeed, if there are any GREY faces at all, both the WHITE and BLACK
- * regions can be grown.
- * This is checked using assert()ions, and I haven't seen any failures yet.
- *
- * Hand-wavy proof: imagine what can go wrong...
- *
- * Could the white faces get completely cut off by the black faces, and still
- * leave some grey faces remaining?
- * No, because then the black faces would form a loop around both the white
- * faces and the grey faces, which is disallowed because we continually
- * maintain the correct topological shape of the black region.
- * Similarly, the black faces can never get cut off by the white faces. That
- * means both the WHITE and BLACK regions always have some room to grow into
- * the GREY regions.
- * Could it be that we can't colour some GREY face, because there are too many
- * WHITE/BLACK transitions as we walk round the face? (see the
- * can_colour_face() function for details)
- * No. Imagine otherwise, and we see WHITE/BLACK/WHITE/BLACK as we walk
- * around the face. The two WHITE faces would be connected by a WHITE path,
- * and the BLACK faces would be connected by a BLACK path. These paths would
- * have to cross, which is impossible.
- * Another thing that could go wrong: perhaps we can't find any GREY face to
- * colour WHITE, because it would create a loop-violation or a corner-violation
- * with the other WHITE faces?
- * This is a little bit tricky to prove impossible. Imagine you have such a
- * GREY face (that is, if you coloured it WHITE, you would create a WHITE loop
- * or corner violation).
- * That would cut all the non-white area into two blobs. One of those blobs
- * must be free of BLACK faces (because the BLACK stuff is a connected blob).
- * So we have a connected GREY area, completely surrounded by WHITE
- * (including the GREY face we've tentatively coloured WHITE).
- * A well-known result in graph theory says that you can always find a GREY
- * face whose removal leaves the remaining GREY area connected. And it says
- * there are at least two such faces, so we can always choose the one that
- * isn't the "tentative" GREY face. Colouring that face WHITE leaves
- * everything nice and connected, including that "tentative" GREY face which
- * acts as a gateway to the rest of the non-WHITE grid.
- */