Last active
November 30, 2021 10:13
-
-
Save linus/a46e482daf7d6ef4bec848a0eda6634d to your computer and use it in GitHub Desktop.
UK Bingo 90 board creator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Takes a board generated by the bingo() function below, and returns | |
// a HTML table. | |
function boardToHTML(board) { | |
return ` | |
<table> | |
${chunk(board, 3).map(card => `<tbody> | |
${card.map(row => `<tr> | |
${row.map(cell => `<td> | |
${cell ? cell.toString().padStart(2, '0') : ''} | |
</td>`).join('')} | |
</tr>`).join('')} | |
</tbody>`).join('')} | |
</table> | |
`; | |
// Utility to chunk an array into n size-sized sub-arrays | |
function chunk(array, size) { | |
const result = []; | |
for (let i = 0, l = array.length; i < l; i += size) { | |
result.push(array.slice(i, i + size)); | |
} | |
return result; | |
} | |
} | |
// Generate a UK-style Bingo board, with 18 rows and 9 columns, each | |
// row having exactly five (5) numbers, and column 1 having the numbers | |
// 1 through 9 (9 blanks), columns 2-8 having ten numbers each (8 blanks), | |
// and column 9 having the numbers 80 through 90 (7 blanks). | |
function bingo() { | |
// This is our "blank" row, just to set out the pattern | |
const row = [...new Array(5).fill(true), ...new Array(4).fill(false)]; | |
// We have all the numbers for each column here, randomly shuffled | |
const cols = [ | |
range(1, 10), | |
range(10, 20), | |
range(20, 30), | |
range(30, 40), | |
range(40, 50), | |
range(50, 60), | |
range(60, 70), | |
range(70, 80), | |
range(80, 91), | |
].map(col => shuffle(col)); | |
// Create our board as an array of 18 rows, each containing a shuffled | |
// transposition of the blank row above | |
const board = Array.from({ length: 18 }, () => shuffle(row.slice(0))); | |
// Switch two random cells in the same row, and check if the resulting | |
// board is closer to our constraints. If not, switch them back and continue | |
while (!valid(board)) { | |
const row = random(0, 18); | |
const col1 = random(0, 9); | |
const col2 = random(0, 9); | |
if (board[row][col1] === board[row][col2]) continue; | |
const previous = Math.abs(board.map(row => row[col1]).filter(Boolean).length - cols[col1].length) | |
+ Math.abs(board.map(row => row[col2]).filter(Boolean).length - cols[col2].length); | |
// try swapping | |
const temp = board[row][col1]; | |
board[row][col1] = board[row][col2]; | |
board[row][col2] = temp; | |
const score = Math.abs(board.map(row => row[col1]).filter(Boolean).length - cols[col1].length) | |
+ Math.abs(board.map(row => row[col2]).filter(Boolean).length - cols[col2].length); | |
if (score > previous) { | |
// it went bad, switch them back | |
board[row][col2] = board[row][col1]; | |
board[row][col1] = temp; | |
} | |
} | |
// Transform our board from the pattern (true if a number should be | |
// in a cell, false otherwise) into a full board with numbers from | |
// each column. Blanks are null. | |
return board.map(row => row.map((cell, col) => cell ? cols[col].pop() : null)); | |
// Check if board is valid: Each row contains exactly five numbers, | |
// column 1 contains exactly 9 numbers, columns 2-8 each contains exactly | |
// 10 numbers, and column 9 contains exactly 11 numbers. | |
function valid (board) { | |
const transposed = transpose(board); | |
return board.every(row => row.filter(Boolean).length === 5) | |
&& transposed.every((col, index) => col.filter(Boolean).length === cols[index].length); | |
} | |
// Get an array with numbers in range from and to | |
function range (from, to) { | |
return Array.from({ length: to - from }, (_, i) => i + from); | |
} | |
// Transpose two-dimensional array | |
function transpose (array) { | |
return array[0].map((_, i) => array.map((x) => x[i])); | |
} | |
// Shuffle array in place, while also returning the array | |
function shuffle (array) { | |
for (let i = array.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[array[i], array[j]] = [array[j], array[i]]; | |
} | |
return array; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<head> | |
<script src="bingo.js"></script> | |
<style> | |
table { | |
border: 1px solid black; | |
border-collapse: collapse; | |
} | |
tbody { | |
border: 2px solid red; | |
} | |
tr { | |
border: 1px solid black; | |
} | |
td { | |
border: 1px solid black; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="board"></div> | |
<button id="generate">Äh ge mig en ny bricka, va</button> | |
<script> | |
window.generate.onclick = () => { | |
const originalText = window.generate.innerHTML; | |
window.generate.innerHTML = "Räknar..."; | |
window.generate.disabled = true; | |
window.requestAnimationFrame(() => { | |
const bingoBoard = bingo(); | |
window.board.innerHTML = boardToHTML(bingoBoard); | |
window.generate.disabled = false; | |
window.generate.innerHTML = originalText; | |
}); | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment