Skip to content

Instantly share code, notes, and snippets.

@linus
Last active November 30, 2021 10:13
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 linus/a46e482daf7d6ef4bec848a0eda6634d to your computer and use it in GitHub Desktop.
Save linus/a46e482daf7d6ef4bec848a0eda6634d to your computer and use it in GitHub Desktop.
UK Bingo 90 board creator
// 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;
}
}
<!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