Overview
Pearls (or Masyu) is a logic game played on a grid. There are black and white pearls placed on the grid. The object is to form a single, closed loop that travels through each pearl using only straight line segments and right angles.
There are some rules that govern how the loop interacts with pearls:
- White pearls must be traveled straight through, but the loop must turn in the previous and/or next cell in its path.
- Black pearls must be turned upon, but the loop must travel straight through the next and previous cells in its path.
- The loop must not cross or otherwise intersect itself. All cells have exactly zero or two loop entry/exits.
An example puzzle from Wikipedia (and its solution):
Your objective is to solve a given puzzle. If there are multiple possible solutions, it does not matter which one you give.
Input
Input will be an unsolved square grid. The example shown above would look like this:
..w.w.....
....w...b.
..b.b.w...
...w..w...
b....w...w
..w....w..
..b...w...
w...b....w
......ww..
..b......b
w
is a white pearl, b
is a black pearl, and .
is an empty cell.
Assume input is valid. This means it is well formed, and at least one solution is possible. All valid puzzles are at least 3x3, and contain at least one pearl.
Output
Output is a string of coordinates representing the path. The upper left corner of the grid is 0 0
, upper right is n-1 0
, where n is the width of the grid.
A path is simply a series of ordered coordinates:
x1 y1 x2 y2 x3 y3 ...
The path is assumed to be closed, so you do not need to repeat the first coordinate at the end, but there is no penalty for doing so.
The output should consist of at least all corners in the path. You do not have to output every cell on the path if there is no turn. For instance, the output for the example could start with:
1 0 5 0 5 1 ...
or
1 0 2 0 3 0 4 0 5 0 5 1 ...
Output should not contain any cell not in the path. You may start at any cell in the path.
Snippet
Here's a snippet you can use to visualize your solution. Just paste in the grid you're working on and the path you output. I'm aware that it's painful to look at my code, so I just suggest you don't ;)
function draw(){document.getElementById("output").innerHTML=gridSVG+pathSVG}function drawCoords(){coords=document.getElementById("coords").value;var e=prefix+'<path fill="none" d="';if(nums=coords.match(/[^\s]+/g),!(nums.length<2)){for(e+="M "+(nums[0]*scale+offset)+" "+(nums[1]*scale+offset),i=2;i<nums.length;i+=2)e+=" L "+(nums[i]*scale+offset)+" "+(nums[i+1]*scale+offset);e+=" L "+(nums[0]*scale+offset)+" "+(nums[1]*scale+offset),e+='" stroke="black" stroke-width="2"/>'+suffix,pathSVG=e,draw()}}function drawPath(){path=document.getElementById("path").value;var e=prefix+'<path fill="none" d="'+path+'" stroke="black" stroke-width="2"/>'+suffix;pathSVG=e,draw()}function drawGrid(){grid=document.getElementById("grid").value;var e=prefix;for(lines=grid.match(/[^\s]+/g),width=lines[0].length*scale,height=lines.length*scale,i=0;i<=lines.length;i++)e+='<path stroke="gray" d="M 0 '+i*scale+" L "+width+" "+i*scale+'"/>',e+='<path stroke="gray" d="M '+i*scale+" 0 L "+i*scale+" "+height+'"/>';for(j=0;j<lines.length;j++)for(line=lines[j],i=0;i<line.length;i++)"w"==line.charAt(i)&&(e+='<circle cx="'+(i*scale+offset)+'" cy="'+(j*scale+offset)+'" r="'+.8*offset+'" stroke="black" fill="white"/>'),"b"==line.charAt(i)&&(e+='<circle cx="'+(i*scale+offset)+'" cy="'+(j*scale+offset)+'" r="'+.8*offset+'" stroke="black" fill="black"/>');e+=suffix,gridSVG=e,draw()}var prefix='<svg height="400" width="400">',suffix="</svg>",scale=30,offset=scale/2,pathSVG="",gridSVG="";
svg{position: absolute;left: 50px;}
Paste grid input here<br><input id="grid" type="textarea" size="80" oninput='drawGrid()' /><br><br>Paste path output here<br><input id="coords" type="textarea" size="80" oninput='drawCoords()' /><br><br><div id="output"></div>
Test Cases
These test cases show one possible output for each input (except the last, which is shown unsolved). There may be other valid paths, you might go CW or CCW or start at a different point, etc. Solutions should be able to solve the test cases in seconds/minutes/hours, not days/weeks/eons.
...
w..
..b
0 0 1 0 2 0 2 1 2 2 1 2 0 2 0 1
.wb..b
......
..b...
w.ww..
......
b....b
0 0 2 0 2 2 4 2 4 1 3 1 3 0 5 0 5 5 3 5 3 4 4 4 4 3 1 3 1 4 2 4 2 5 0 5 0 2 1 2 1 1 0 1
.....w.b.w..
ww..b...b...
.w.....b....
...wbww..b.b
....b.......
w.w.........
..w......b.b
.....bb.....
.....b.....w
w.ww..b.....
...w......w.
b..w.....b..