JSFiddle - React, Tailwind, and code Playground
HTML
<div>Level: <select id="level"></select></div>
<div id="main" class="cont"></div>
<p style="clear:both">Available Patterns:</p>
<div id="sols"></div>
CSS
.cont {
float: left;
display: inline-block;
width: 90px;
height: 90px;
padding: 10px;
}
#main.cont {
width: 150px;
height: 150px;
font-size: 25px;
}
.blk {
float: left;
text-align: center;
}
JavaScript
function random(n) {
return Math.floor(Math.random() * (n || 0))
}
// core
var M = 3, N = 3, NUM0 = 0, NUM1 = ~((~1 >> M * N) << (M * N))
var num = 0
var sol = [ ]
var masks = [
'1',
'1/1/1',
'01/11', '10/11', '11/10', '11/01',
'10/11/01', '01/11/10',
'10/10/11', '01/01/11', '11/10/10', '11/01/01',
'111',
'110/011', '011/110',
]
function parseMask(list) {
var mask = 0
list.forEach(function(s, i) {
mask |= (parseInt(s, 2) << (M * N - s.length)) >> (M * i)
})
return mask
}
function applyFlip(num, mask, i, j) {
return num ^ (mask >> (i * M + j))
}
function makeNewPuzzle(level) {
num = [NUM0, NUM1][random(2)]
sol = [ ]
for (var i = 0; i < level; i ++) {
var mst = masks[random(masks.length)],
list = mst.split('/')
var data = {
mask: parseMask(list),
width: list[0].length,
height: list.length,
i: random(N - list.length + 1),
j: random(M - list[0].length + 1)
}
num = applyFlip(num, data.mask, data.i, data.j)
sol.push(data)
}
}
// ui
function testBit(num, i) {
return num & (1 << (M * N - i - 1))
}
function updateView() {
var main = $('#main').empty()
for (var i = 0; i < M * N; i ++) {
$('<div></div>').addClass('blk')
.attr('index', i)
.width(100/M+'%').height(100/N+'%')
.text(testBit(num, i) ? 'X' : 'O')
.appendTo(main)
}
var sols = $('#sols').empty()
for (var i = 0; i < sol.length; i ++) {
var data = sol[i],
mask = $('<div></div>').addClass('cont').appendTo(sols)
for (var j = 0; j < M * N; j ++) {
$('<div></div>').addClass('blk')
.width(100/M+'%').height(100/N+'%')
.css('background', testBit(data.mask, j) ? 'red' : 'white')
.appendTo(mask)
}
}
sols.children().first().css('background', '#aaa')
...