summaryrefslogtreecommitdiff
path: root/codenames/assets
diff options
context:
space:
mode:
authorBotond Hende <nettingman@gmail.com>2026-02-01 00:36:11 +0100
committerBotond Hende <nettingman@gmail.com>2026-02-01 00:36:11 +0100
commitcc6bdc68cb3ed40a6e51f885ea1c7c7a4e5fa6c5 (patch)
tree144ef9f11acd0b16b518c1ea55b548a6ed21430d /codenames/assets
parentaaee49cca9e771a7e0fbbdfe513711e94b4ff19a (diff)
codenames grid generator wipfeature/codenames
Diffstat (limited to 'codenames/assets')
-rw-r--r--codenames/assets/css/codenames.css28
-rw-r--r--codenames/assets/js/codenames.js211
2 files changed, 239 insertions, 0 deletions
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