Solving Minesweeper – Part 8: Ludicrous Speed

If light speed isn’t fast enough, you can still go into ludicrous speed but keep in mind, you risk going into plaid. If you don’t know what I’m talking about, don’t worry and watch this funny sequence from the cult classic, Spaceballs. In other words, i managed to improve the speed, yes i know, i’m obsessed with performance.

To be honest, i didn’t make the algorithm run faster. Last time we introduced a 200ms delay so we wait for the animations to end. Setting a timeout is always a tricky system, especially if it would be hardware dependent. Luckily in our case it’s not, however we all still running faster than the game to catch-up. In our case sometimes having the timeout makes sense, other times it doesn’t.

To get rid of that 200ms delay, we need to detect when there are animations running. If animations are running, in my case, there will be cells which cannot be analysed. So what i did was to go into a loop, until i was able to successfully analyze all the cells that were still not discovered.

int tries = 0;
while (tries < 10)
{
	tries++;
	Sleep(25);
	gm.GetImageInto(imageAnalyzer.img.data);
	if (imageAnalyzer.Analyze())
		break;
}
if (tries == 10) // timeout
	break;
set<int> b, s;
decisionMaker.GetDecisions(b, s);

The analyze method now returns the total amount of cells processed. We try 10 times, if i don’t run into a complete state by then, we declare it a timeout. This is handy to discover when the game is over.Let’s also peek into the analyze method.

bool GameImageAnalyzer::Analyze()
{
	Mat bmp = GetBmp();
	Mat result;
	for (int i = 0; i < SIZE; i++)
	{
		if (game[i] <= BOMB) // we already processed this cell
			continue;
		
		int j = (game[i] == NOTBOMB) ? 1 : 0; 
		for (; j < TEMPLATE_COUNT - 1; j++)
		{
			int t = templates[j];
			Mat temp = temps[j];
			Mat in = bmp(rects[i]);

			matchTemplate(in, temp, result, TM_CCOEFF_NORMED);
			threshold(result, result, 0.85, 1, THRESH_BINARY);
			if (countNonZero(result) > 0)
			{
				game[i] = t;
				break;
			}
		}
		if (j == TEMPLATE_COUNT - 1)
			return false;
	}
	return true;
}

We keep a count of the cells we processed. If we declared a cell NOTBOMB we also expect to discover a value or an empty cell there (templates[0] is the blue cell template, so we detect very fast if a previously undiscovered cell remained undiscovered). This is what the start value of j holds. If we run into a cell that didn’t match any template, we signal that we were not successful and we need to try again.

Ok but just how fast is ludicrous speed. Check this out!

As you can see in general when it solves a game, it is under 6 seconds. Which means it is three times faster than what we had before.