From cc6bdc68cb3ed40a6e51f885ea1c7c7a4e5fa6c5 Mon Sep 17 00:00:00 2001 From: Botond Hende Date: Sun, 1 Feb 2026 00:36:11 +0100 Subject: codenames grid generator wip --- codenames/assets/css/codenames.css | 28 +++++ codenames/assets/js/codenames.js | 211 +++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 codenames/assets/css/codenames.css create mode 100644 codenames/assets/js/codenames.js (limited to 'codenames/assets') diff --git a/codenames/assets/css/codenames.css b/codenames/assets/css/codenames.css new file mode 100644 index 0000000..db3fb71 --- /dev/null +++ b/codenames/assets/css/codenames.css @@ -0,0 +1,28 @@ +div.tile_row { + display: flex; +} + +div.tile { + width: 100px; + height: 100px; +} + +div.neutral { + background: antiquewhite; +} + +div.black { + background: black; +} + +div.green { + background: green; +} + +div.red { + background: red; +} + +div.blue { + background: blue; +} diff --git a/codenames/assets/js/codenames.js b/codenames/assets/js/codenames.js new file mode 100644 index 0000000..c838b48 --- /dev/null +++ b/codenames/assets/js/codenames.js @@ -0,0 +1,211 @@ +"use strict" + +const Tile = Object.freeze({ + NEUTRAL: 0, + BLACK: 1, + GREEN: 2, + RED: 3, + BLUE: 4, +}); + +function cyrb128(str) { + let h1 = 1779033703, h2 = 3144134277, + h3 = 1013904242, h4 = 2773480762; + for (let i = 0, k; i < str.length; i++) { + k = str.charCodeAt(i); + h1 = h2 ^ Math.imul(h1 ^ k, 597399067); + h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); + h3 = h4 ^ Math.imul(h3 ^ k, 951274213); + h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + } + h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); + h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); + h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); + h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); + h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1; + return [h1>>>0, h2>>>0, h3>>>0, h4>>>0]; +} + +function sfc32(a, b, c, d) { + return function() { + a |= 0; b |= 0; c |= 0; d |= 0; + let t = (a + b | 0) + d | 0; + d = d + 1 | 0; + a = b ^ b >>> 9; + b = c + (c << 3) | 0; + c = (c << 21 | c >>> 11); + c = c + t | 0; + return (t >>> 0) / 4294967296; + } +} + +function shuffle(array, rand) { + let currentIndex = array.length - 1; + + while (currentIndex !== 0) { + let otherIndex = Math.floor(rand() * (currentIndex + 1)); + [array[currentIndex], array[otherIndex]] = [array[otherIndex], array[currentIndex]]; + currentIndex--; + } +} + +function generateEmptyGrid(width, height){ + let grid = []; + for (let ii = 0; ii < height; ii++) { + let line = Array(width); + line.fill(Tile.NEUTRAL); + grid.push(line) + } + + return grid; +} + +function getPositions(width, height) { + let positions = []; + for (let ii = 0; ii < height; ii++) { + for (let jj = 0; jj < width; jj++) { + positions.push([ii, jj]); + } + } + + return positions; +} + +function getPosIndexByValue(array, pos) { + for (let ii = 0; ii < array.length; ii++) { + if (array[ii][0] === pos[0] && array[ii][1] === pos[1]) { + return ii; + } + } + + return -1; +} + +function generateDuetGrids(width, height, greenCount, blackCount, commonGreenCount, commonBlackCount, blackGreenCount, seed) { + let seed_array = cyrb128(seed); + let rand = sfc32(seed_array[0], seed_array[1], seed_array[2], seed_array[3]); + let blackNeutralCount = blackCount - commonBlackCount - blackGreenCount; + let greenNeutralCount = greenCount - commonGreenCount - blackGreenCount; + + let p1 = generateEmptyGrid(width, height); + let p2 = generateEmptyGrid(width, height); + + let p1NeutralPositions = getPositions(width, height); + let p2NeutralPositions = getPositions(width, height); + + shuffle(p1NeutralPositions, rand); + shuffle(p2NeutralPositions, rand); + + let p1GreenPositions = []; + let p1BlackPositions = []; + + // P1 generation + for (let ii = 0; ii < greenCount; ii++) { + let pos = p1NeutralPositions.pop(); + p1GreenPositions.push(pos); + p1[pos[0]][pos[1]] = Tile.GREEN; + } + + for (let ii = 0; ii < blackCount; ii++) { + let pos = p1NeutralPositions.pop(); + p1BlackPositions.push(pos); + p1[pos[0]][pos[1]] = Tile.BLACK; + } + + // P2 BLACK generation + for (let ii = 0; ii < commonBlackCount; ii++) { + let pos = p1BlackPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.BLACK; + } + + for (let ii = 0; ii < blackGreenCount; ii++) { + let pos = p1GreenPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.BLACK; + } + + for (let ii = 0; ii < blackNeutralCount; ii++) { + let pos = p1NeutralPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.BLACK; + } + + // P2 GREEN generation + for (let ii = 0; ii < commonGreenCount; ii++) { + let pos = p1GreenPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.GREEN; + } + + for (let ii = 0; ii < blackGreenCount; ii++) { + let pos = p1BlackPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.GREEN; + } + + for (let ii = 0; ii < greenNeutralCount; ii++) { + let pos = p1NeutralPositions.pop(); + p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1); + p2[pos[0]][pos[1]] = Tile.GREEN; + } + + return [p1, p2]; +} + +function parseAndGenerateDuet() +{ + let params = new URLSearchParams(window.location.search); + if (params.has("w") && params.has("h") && params.has("g") && params.has("b") && + params.has("cg") && params.has("cb") && params.has("bg") && params.has("s")) { + return generateDuetGrids(+params.get("w"), +params.get("h"), +params.get("g"), +params.get("b"), + +params.get("cg"), +params.get("cb"), +params.get("bg"), params.get("s")); + } + + return null; +} + +function generate_for_current_page() { + const duetP1 = document.getElementById("duet_p1"); + const duetP2 = document.getElementById("duet_p2"); + if (duetP1 !== null || duetP2 !== null) { + let grids = parseAndGenerateDuet(); + if (grids == null) { + return; + } + + const gridRoot = duetP1; + let grid = grids[0]; + + for (let ii = 0; ii < grid.length; ii++) { + let row = grid[ii]; + let rowDiv = document.createElement("div"); + rowDiv.classList.add("tile_row"); + gridRoot.appendChild(rowDiv); + for (let jj = 0; jj < row.length; jj++) { + let tileDiv = document.createElement("div"); + tileDiv.classList.add("tile"); + rowDiv.appendChild(tileDiv); + switch (grid[ii][jj]) { + case Tile.NEUTRAL: + tileDiv.classList.add("neutral"); + break; + case Tile.BLACK: + tileDiv.classList.add("black"); + break; + case Tile.GREEN: + tileDiv.classList.add("green"); + break; + case Tile.RED: + tileDiv.classList.add("red"); + break; + case Tile.BLUE: + tileDiv.classList.add("blue"); + break; + } + } + } + } +} + +generate_for_current_page(); \ No newline at end of file -- cgit v1.2.3-70-g09d2