I came across this tile puzzle at a rental cottage and it frustrated most of those in attendance. Boasting “literally billions” of possible combinations I gave it a go and shortly decided swimming and canoeing was more rewarding.

The puzzle is comprised of 16 square tiles with one of 8 patterns on each of their 4 sides. To solve the puzzle one must place these tiles in a 4-by-4 grid with their edges aligned such that the patterns on each side match the patterns on the sides of the tiles adjacent, above and below.

I was curious, however, if the solution entailed some sort of offset layout, or if it was just a standard 4-by-4 edge-aligned grid as the box implied — it’s an optical illusion puzzle after all. So I took to searching online for the solution, with no luck, I again decided swimming and canoeing was more rewarding.

Upon returning home from the cottage the puzzle nagged me, so I set to writing a program to more-or-less brute force the solution, seen below, written in C#. I didn’t feel like programming routes for every possible combination so instead I wrote in some random elements to try while systematically eliminating other factors. I found an image of the tiles online and translated them to numeric representations of their patterns and rotations. After 142,594 attempts of the last iteration of my program it found a solution. I say “a solution” because I ran it again and it found a different solution, both valid. Multiple solutions runs contrary to the packaging, but oh well.

An image of the solved puzzle can be found by clicking here. Hopefully this helps others get on with their summer rentals.

You can find some of these puzzles on Amazon, though they are long out of production.

#### Successful Output

Attempt #142594
[0, 0] Tile Success
[1, 0] Tile Success
[2, 0] Tile Success
[3, 0] Tile Success
[0, 1] Tile Success
[1, 1] Tile Success
[2, 1] Tile Success
[3, 1] Tile Success
[0, 2] Tile Success
[1, 2] Tile Success
[2, 2] Tile Success
[3, 2] Tile Success
[0, 3] Tile Success
Partial Success
4 9 12 10
2 0 7 14
3 6 8 15
5 _ _ _
[1, 3] Tile Success
Partial Success
4 9 12 10
2 0 7 14
3 6 8 15
5 11 _ _
[2, 3] Tile Success
Partial Success
4 9 12 10
2 0 7 14
3 6 8 15
5 11 1 _
[3, 3] Tile Success
Puzzle Success
4 9 12 10
2 0 7 14
3 6 8 15
5 11 1 13

#### C# Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Puzzle_Solver
{
class Program
{
static int[,] puzzle = new int[16,4] {
{5, 1, 4, 2},
{1, 2, 7, 2},
{4, 3, 2, 1},
{8, 4, 4, 7},
{2, 1, 4, 6},
{5, 6, 6, 8},
{7, 5, 3, 3},
{2, 1, 2, 3},
{2, 3, 3, 1},
{1, 4, 6, 2},
{3, 2, 1, 2},
{5, 3, 1, 2},
{2, 1, 1, 4},
{7, 3, 2, 5},
{3, 1, 2, 3},
{2, 3, 1, 1},
};
static void Main(string[] args)
{
List tile_set = new List();
for (int x = 0; x < 16; x++)
{
rotation[] rotations = new rotation[4]
{
new rotation(puzzle[x, 0], puzzle[x, 1], puzzle[x, 2], puzzle[x, 3]),
new rotation(puzzle[x, 3], puzzle[x, 0], puzzle[x, 1], puzzle[x, 2]),
new rotation(puzzle[x, 2], puzzle[x, 3], puzzle[x, 0], puzzle[x, 1]),
new rotation(puzzle[x, 1], puzzle[x, 2], puzzle[x, 3], puzzle[x, 0])
};
tile_set.Add(new tile(x, rotations));
}
bool puzzle_success = false;
tile[,] solution;
int[,] starting_candidates = new int[4, 4];
int attempt = 0;
Random random = new Random();
while (!puzzle_success)
{
tile_set = tile_set.OrderBy(x => random.Next()).ToList();
int random_offsets = random.Next(16);
for(int ran = 0; ran < random_offsets; ran++)
{
starting_candidates = random.Next(16);
}
for (int tile_to_offset_start = 0; tile_to_offset_start < 16; tile_to_offset_start++)
{
for (int offset = 0; offset < 16; offset++)
{
attempt++;
List current_tile_set = tile_set.ToList();
// reset
foreach (tile t in current_tile_set)
{
t.match_rotation = null;
}
solution = new tile[4, 4];
Console.WriteLine("Attempt #" + attempt);
for (int y = 0; y < 4; y++)
{
bool tile_success = false;
for (int x = 0; x < 4; x++)
{
tile_success = false;
for (int t = 0; t < current_tile_set.Count(); t++)
{
int t_adjusted = starting_candidates[x, y] + t;
if (t_adjusted >= current_tile_set.Count())
{
t_adjusted -= current_tile_set.Count();
}
tile current_tile = current_tile_set[t_adjusted];
// used
if (current_tile.match_rotation != null)
{
continue;
}
int random_rotation = random.Next(4);
for (int r = 0; r < 4; r++)
{
int rotation = random_rotation + r;
if(rotation > 3)
{
rotation -= 4;
}
rotation current_rotation = current_tile.rotations[rotation];
// check left
if (x != 0
&& solution[x - 1, y].match_rotation.right != current_rotation.left)
{
continue;
}
// check top
if (y != 0
&& solution[x, y - 1].match_rotation.bottom != current_rotation.top)
{
continue;
}
// match
current_tile.match_rotation = current_rotation;
solution[x, y] = current_tile;
tile_success = true;
Console.WriteLine("[" + x + ", " + y + "] Tile Success");
break;
}
if (tile_success)
{
break;
}
}
if (tile_success && x == 3 && y == 3)
{
Console.WriteLine("Puzzle Success");
output_solution(solution);
puzzle_success = true;
break;
}
else if (tile_success && x >= 0 && y == 3)
{
Console.WriteLine("Partial Success");
output_solution(solution);
continue;
}
else if (!tile_success)
{
Console.WriteLine("[" + x + ", " + y + "] Puzzle Fail");
output_solution(solution);
break;
}
}
if (!tile_success)
{
break;
}
}
int offset_x = Convert.ToInt16(Math.Floor(Convert.ToDecimal(tile_to_offset_start) / 4));
int offset_y = tile_to_offset_start % 4;
starting_candidates[offset_x, offset_y] = offset;
}
}
}
}
public static void output_solution(tile[,] solution)
{
Console.WriteLine();
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
Console.Write(solution[x, y] == null ? "_" : Convert.ToString(solution[x, y].id));
Console.Write("\t");
}
Console.WriteLine();
Console.WriteLine();
}
}
}
class tile
{
public rotation[] rotations;
public rotation match_rotation;
public int id;
public tile(int id, rotation[] rotations)
{
this.id = id;
this.rotations = rotations;
}
}
class rotation
{
public int left;
public int top;
public int right;
public int bottom;
public rotation(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
}