How to optimally solve the flood fill puzzle?

18,891

Solution 1

As a heuristic, you could construct a graph where each node represents a set of contiguous, same-colour squares, and each node is connected to those it touches. (Each edge weighted as 1). You could then use a path-finding algorithm to calculate the "distance" from the top left to all other nodes. Then, by looking the results of flood-filling using each of the other 5 colours, determine which one minimizes the distance to the "furthest" node, since that will likely be your bottleneck.

Add the result of that calculation to the number of fills done so far, and use that as your A* heuristic.

Solution 2

A naive 'greedy' algorithm is to pick the next step that maximizes the overall perimeter of the main region.

(A couple of smart friends of mine were thinking about this the other day and decided the optimium may be NP-hard (e.g. you must brute force it) - I do not know if they're correct (wasn't around to hear the reasoning and haven't thought through it myself).)

Note that for computing steps, I presume the union-find algorithm is your friend, it makes computing 'one step' very fast (see e.g. this blog post).

Solution 3

A* is just a prioritized graph search. Each node is a game state, you rank nodes based on some heuristic, and always expand the lowest-expected-final-cost node. As long as your heuristic doesn't underestimate costs, the first solution you find is guaranteed to be optimal.

After playing the games a few times, I found that trying to drill to the opposite corner then all corners tended to result in a win. So a good starting cost estimate would be (cost so far) + a sufficient number of fills to reach the opposite corner [note: not minimum, just sufficient. Just greedily fill towards the corner to compute the heuristic].

Solution 4

Here's an idea for implementing the graph to support Smashery's heuristic.

Represent each group of contiguous, same-colour squares in a disjoint set, and a list of adjacent groups of squares. A flood fill merges a set to all its adjacent sets, and merges the adjacency lists. This implicit graph structure will let you find the distance from the upper left corner to the farthest node.

Solution 5

I have been working on this, and after I got my solver working I took a look at the approaches others had taken.

Most of the solvers out there are heuristic and do not guarantee optimality. Heuristics look at the number of squares and distribution of colors left unchosen, or the distance to the "farthest away" square. Combining a good heuristic with bounded DFS (or BFS with lookahead) results in solutions that are quite fast for the standard 14x14 grid.

I took a slightly different approach because I was interested in finding the provably optimal path, not just a 'good' one. I observed that the search space actually grows much slower than the branching factor of the search tree, because there are quite a lot of duplicate positions. (With a depth-first strategy it is therefore important to maintain a history to avoid a redundant work.) The effective branching factor seems closer to 3 than to 5.

The search strategy I took is to perform BFS up to a "midpoint" depth where the number of states would become infeasible, somewhere between 11 and 13 moves works best. Then, I examine each state at the midpoint depth and perform a new BFS starting with that as the root. Both of these BFS searches can be pruned by eliminating states found in previous depths, and the latter search can be bounded by the depth of the best-known solution. (A heuristic applied to the order of the subtrees examined in the second step would probably help some, as well.)

The other pruning technique which proved to be key to a fast solver is simply checking whether there are more than N colors left, if you are N or fewer steps away from the current best solution.

Once we know which midpoint state is on the path to an optimal solution, the program can perform DFS using that midpoint state as a goal (and pruning any path that selects a square not in the midpoint.) Or, it might be feasible to just build up the paths in the BFS steps, at the cost of some additional memory.

My solver is not super-fast but it can find a guaranteed optimal solution in no more than a couple minutes. (See http://markgritter.livejournal.com/673948.html, or the code at http://pastebin.com/ZcrS286b.)

Share:
18,891
felix
Author by

felix

Updated on June 05, 2022

Comments

  • felix
    felix almost 2 years

    I like playing the puzzle game Flood-It, which can be played online at:

    https://www.lemoda.net/javascript/flood-it/game.html

    It's also available as an iGoogle gadget. The aim is to fill the whole board with the least number of successive flood-fills.

    I'm trying to write a program which can solve this puzzle optimally. What's the best way to approach this problem? Ideally I want to use the A* algorithm, but I have no idea what should be the function estimating the number of steps left. I did write a program which conducted a depth-4 brute force search to maximize the filled area. It worked reasonably well and beat me in solving the puzzle, but I'm not completely satisfied with that algorithm.

    Any suggestions? Thanks in advance.

  • J-16 SDiZ
    J-16 SDiZ over 13 years
    NP-Hard does not means you must brute force it.
  • Chuck Simmons
    Chuck Simmons over 10 years
    Despite the negative rating on my comment; I've implemented the algorithm with these tweaks, and typical 14x14 boards can be solved while looking at on the order of 10,000 nodes. I'm not clear on why a technique that significantly improves the lower bound estimate would be down-voted. Especially when a number of inferior heuristics (greedy, count colors left) are not down-voted.