Skip to content

Instantly share code, notes, and snippets.

@pcornier
Last active April 19, 2021 07:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcornier/8c6a41b33c798280c7e328b1c4606bb6 to your computer and use it in GitHub Desktop.
Save pcornier/8c6a41b33c798280c7e328b1c4606bb6 to your computer and use it in GitHub Desktop.
Futoshiki
// A basic Futoshiki prototype
let size = 5 // decrease for easier games
let width = 300
let height = 300
let padding = width*0.03
let cellwidth = (width/size)-padding*2;
let cellheight = (height/size)-padding*2
let hv = 4 // vertical helpers
let hh = 4 // horizontal helpers
let style = `
<style>
body { padding: 10px; font-family: Tahomas }
game {
position: relative;
}
.grid {
position: relative;
width: ${width}px;
height: ${height}px;
}
.cells {
width: ${cellwidth}px;
height: ${cellheight}px;
margin: ${padding}px;
float: left;
font-size: ${cellheight}px;
text-align: center;
outline: 1px solid;
cursor: pointer;
}
.ctrl {
width: ${cellwidth}px;
height: ${cellheight}px;
margin: ${padding}px;
float: left;
font-size: ${cellheight}px;
text-align: center;
outline: 1px solid #ccc;
background: #888;
color: #fff;
cursor: pointer;
}
.helpers {
position: absolute;
pointer-events: none;
}
</style>
`
let selected_cell
let grid = []
// generate a latin grid
// 1 2 3 4
// 2 3 4 1
// 3 4 1 2
// 4 1 2 3
let latin = []
for (var i = 0; i < size*size; i++) {
latin.push(1+((i%size)+((i/size)|0))%size)
}
// do row and column permutations
let max_permutations = Math.floor(size/2)
let nb1 = [...Array(size).keys()]
let nb2 = [...Array(size).keys()]
for (var i = 0; i < max_permutations; i++) {
let p1 = nb1.splice((Math.random()*nb1.length)|0, 1)[0]
let p2 = nb1.splice((Math.random()*nb1.length)|0, 1)[0]
for (var j = 0; j < size; j++) { // rows
let c = latin[p1*size+j]
latin[p1*size+j] = latin[p2*size+j]
latin[p2*size+j] = c
}
let p3 = nb2.splice((Math.random()*nb2.length)|0, 1)[0]
let p4 = nb2.splice((Math.random()*nb2.length)|0, 1)[0]
for (var j = 0; j < size; j++) { //cols
let c = latin[j*size+p3]
latin[j*size+p3] = latin[j*size+p4]
latin[j*size+p4] = c
}
}
// generate helpers: greater than, less than
let helpers = ''
// horizontal helpers between columns
let nb3 = [...Array((size-1) * size).keys()]
for (var i = 0; i < hh; i++) {
let h1 = nb3.splice((Math.random()*nb3.length)|0, 1)[0]
let y = (h1 / (size-1))|0
let x = h1 % (size-1)
let yy = y * (cellheight + padding * 2) + cellheight / 2
let xx = x * (cellwidth + padding * 2) + cellwidth + padding
let d = y * size + x
let a = latin[d]
let b = latin[d+1]
helpers += `<div class="h-helper" style="position: absolute;left:${xx}px;top:${yy}px">${a>b?'>':'<'}</div>`
}
// vertical helpers between rows
let nb4 = [...Array(size*size-size).keys()]
for (var i = 0; i < hv; i++) {
let v1 = nb4.splice((Math.random()*nb4.length)|0, 1)[0]
let y = (v1 / size)|0
let x = v1 % size
let yy = y * (cellheight + padding * 2) + cellheight + padding
let xx = x * (cellwidth + padding * 2) + cellwidth/2
let d = y * size + x
let a = latin[d]
let b = latin[d+size]
helpers += `<div class="v-helper" style="position: absolute;left:${xx}px;top:${yy}px">${a>b?'\\/':'/\\'}</div>`
}
// draw the grid
let cells = ''
for (var i = 0; i < size*size; i++) {
let v = Math.random()*10 < 2 // if v then show number
if (v) grid[i] = [latin[i]]
cells += `<div ${!v ? 'onclick="sel_cell(this)"' : ''} class="cells" id="c${i}">${v ? latin[i] : ''}</div>`
}
// draw the controls
let controls = ''
for (var i = 0; i < size; i++)
controls += `<div onclick="setn(${i+1})" class="ctrl">${i+1}</div>`
// html
let markup = `
${style}
<game>
<div class="helpers">${helpers}</div>
<div class='grid'>${cells}</div>
</game>
${controls}
`
document.body.innerHTML = markup
// select a cell (click on grid)
function sel_cell(e) {
selected_cell = e.id.substr(1)
let cells = document.querySelectorAll('.cells')
for (var c of cells) c.style.background = '#fff'
e.style.background = '#eee'
}
// assign a number and check if the grid is complete (click on controls)
// todo: to avoid the creation of a solver do not compare with the grid
// solution, it is better to check for grid validity.
function setn(n) {
let r = grid[selected_cell] || []
if (r.indexOf(n) !== -1) r.splice(r.indexOf(n), 1)
else r.push(n)
grid[selected_cell] = r
let cell = document.querySelector('#c' + selected_cell)
r.sort()
cell.innerHTML = r.join(' ')
cell.style.fontSize = (r.length == 1 ? cellheight : cellheight*0.4) + 'px'
let cnt = 0
for (var i = 0; i < size*size; i++) {
if (grid[i] && grid[i].length == 1) {
if (grid[i][0] == latin[i]) cnt++
}
}
if (cnt == size*size) {
let cells = document.querySelectorAll('.cells')
for (var c of cells) c.style.background = '#b0e873'
setTimeout(_ => alert('you win!'), 1000)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment