Skip to content

Instantly share code, notes, and snippets.

@3ki5tj
Created November 18, 2014 21:25
Show Gist options
  • Save 3ki5tj/e58dc6dd31329cc0ed4c to your computer and use it in GitHub Desktop.
Save 3ki5tj/e58dc6dd31329cc0ed4c to your computer and use it in GitHub Desktop.
Lightsout puzzle
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" >
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Lights out</title>
<style type="text/css">
/*<![CDATA[*/
body {
font: 90% "MS Trebunchet", "Lucida Grande", 'Verdana', 'Tahoma', 'Calibri', sans-serif;
width: 600px;
margin: auto;
}
h1 {
font: small-caps 160% "Constantia", "Times New Roman", serif;
}
h2 {
font: small-caps 130% "Geogia", "Times New Roman", serif;
margin: 0px 0px 10px 0px;
border-top: #e0e0e0 2px dotted;
padding-top: 10px;
}
table {
border-collapse: collapse;
margin: 10px 0px;
-moz-user-select: none;
-webkit-user-select: none;
}
td {
width: 30px;
height: 30px;
text-align: center;
padding: 0px;
margin: 0px;
color: #808080; /* color for hints */
background-color: #e0e0e0;
border: #ffffff 2px outset;
font-weight: bold;
}
td.off {
}
td.on {
background-color: #ff0033;
border: 2px groove #ff0033;
}
/*]]>*/
</style>
<script type="text/javascript">
// <![CDATA[
var n = 5;
var solmax = 1000;
var carr = new Array(); // if light is on
var hint = new Array(); // hinted region
var clicked = new Array(); // clicked odd number of times
function draw()
{
var s = '<table id="table">\n';
for (i = 0; i < n; i++) {
s += "<tr>\n"
for (j = 0; j < n; j++) { // draw 35*35 squares
var id = i*n + j;
s += '<td id="' + id + '" onclick="gridclick(id)">&nbsp;</' + 'td>\n';
}
s += '</' + 'tr>\n';
}
s += '</' + 'table>';
document.getElementById("tablewrapper").innerHTML = s;
update();
}
function update()
{
for (var id = 0; id < n*n; id++)
document.getElementById(""+id).innerHTML =
(!clicked[id] && hint[id]) ? "&times;" : "&nbsp;";
}
function getn()
{
o = document.getElementById("n");
n = parseInt(o.value);
if (n < 1) n = 1;
if (n > 30) n = 30;
o.value = n;
solmax = parseInt(document.getElementById("solmax").value);
}
function play()
{
getn();
for (i = 0; i < n*n; i++) clicked[i] = carr[i] = hint[i] = 0;
draw();
}
function flip(i, j)
{
if (i >= 0 && i < n && j >= 0 && j < n) {
var id = i*n+j;
carr[id] = !carr[id];
o = document.getElementById(id).style;
document.getElementById(id).className = carr[id] ? "on" : "off";
//o.borderColor = o.backgroundColor = (carr[id] ? "#ff0080" : "#e0e0e0");
}
}
function gridclick(id)
{
id = id*1;
i = (id/n)|0; j = id % n;
flip(i, j);
flip(i+1, j);
flip(i-1, j);
flip(i, j+1);
flip(i, j-1);
clicked[id] = !clicked[id];
update();
var win = true;
for (id = 0; id < n*n && win; id++)
if (carr[id] == 0) win = false;
if (win) alert("Congratulations! You win!");
}
function bitcount(i)
{
var tmp = i - ((i >> 1) & 033333333333) - ((i >> 2) & 011111111111);
return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}
var state = new Array();
function solve()
{
lst = document.getElementById("results");
lst.length = 0; // clear list
lst.options[0] = new Option("Searching...");
getn();
var count = 0, idbest = 0;
var hitmin = 1000000;
var MAX = (1 << n) - 1;
state[0] = 0; // the hidden 0th row
for (row = MAX; row >= 0; row--) { // to enumerate every possibility of the first row
for (me = row, j = 1; j <= n; j++) { // to derive other rows
state[j] = me^MAX; // inverse the color of the previous row
me = (state[j-1] ^ state[j] ^ (state[j]>>1) ^ (state[j]<<1)) & MAX;
}
if (me == MAX) { // on the last row lights are all on, found a solution
for (hits = 0, j = 1; j <= n; j++)
hits += bitcount(state[j]);
if (hits < hitmin) {
idbest = count;
hitmin = hits;
document.getElementById("best").innerHTML = "Best: solution " + count + ".";
}
lst.options[count] = new Option("" + count + " (" + hits + " clicks): " + state[1]);
count++;
if (count > solmax) break;
}
}
if (count) {
o = document.getElementById("results");
lst.style.visibility = "visible";
lst.selectedIndex = idbest;
selectsolution();
}
}
function init()
{
document.getElementById("results").style.visibility = "hidden";
play();
}
// parse option
function selectsolution()
{
lst = document.getElementById("results");
if (lst.selectedIndex < 0) return;
for (i = 0; i < n*n; i++) clicked[i] = carr[i] = hint[i] = 0;
s = lst.options[lst.selectedIndex].text;
s = s.substring(s.indexOf(":")+2, s.length);
arr = s.split(" ");
MAX = (1<<n)-1;
for (me = arr[0]^MAX, state[0] = 0, j = 1; j <= n; j++) { // to derive other rows
state[j] = me^MAX;
me = (state[j-1]^state[j]^(state[j]>>1)^(state[j]<<1)) & MAX;
for (i = 0; i < n; i++)
hint[(j-1)*n+i] = (state[j]>>i)&1;
}
draw();
}
window.onload = init;
// ]]>
</script>
</head>
<body>
<h1>Lights out</h1>
<p>On a N&times;N board, all lights are initially white.
By clicking a light, the color of this light and four adjacent ones (up,down,left,right)
will be flipped (from red to white, or from white to red).
The aim is to turn all lights to red.
<div id="appwrapper">
N: <input type="text" id="n" value="5" size="4">
<input type="button" onClick="play();" value="Play">
<div id="tablewrapper" onSelectStart="return false;"> </div>
<input type="button" onClick="solve();" value="Solve">
limit <input type="text" id="solmax" value="1000" size="8"> solutions<br>
<select id="results" onChange="selectsolution()">
<option value="null">&nbsp;</option>
</select>
<span id="best"></span><br>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment