From 3f0688d0aaf15020767711f881c52cd5dcdc1ae3 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 24 May 2024 17:58:16 +0200 Subject: [PATCH] Debug stuff --- .../resources/webroot/static/connect2x.js | 308 +++++++++++++++++- src/main/resources/webroot/static/index.html | 12 +- src/main/resources/webroot/static/style.css | 57 ++++ 3 files changed, 372 insertions(+), 5 deletions(-) diff --git a/src/main/resources/webroot/static/connect2x.js b/src/main/resources/webroot/static/connect2x.js index 70622da..99424f7 100644 --- a/src/main/resources/webroot/static/connect2x.js +++ b/src/main/resources/webroot/static/connect2x.js @@ -1,7 +1,157 @@ -const chars = ["AN","AR","AT","CA","CH","CO","DE","DI","EL","EN","ER","ES","HE","IA","IL","IN","IO","LA","LE","LI","LL","MA","ME","NA","NE","NO","NT","OL","ON","OR","PE","RA","RE","RI","RO","SE","SI","SO","ST","TA","TE","TI","TO","TR","TT","UN","TO","RE","ER","ON","CO","DI","TA","EN","IN","TE","AT","RA","AN","NO","NT","ST","LA","AR","AL","OR","CH","RI","TI","IO","LE","DE","ES","NE","ME","TT","EL","PE","IL","UN","IA","LI","SE","SO","LL","SI","OL","RO","MA","CA","NA","TR","HE","ALE","ALL","ANC","AND","ANT","ARE","ATO","ATT","CHE","CHI","COM","CON","DEL","ELL","ENT","ERA","ERE","ESS","EST","ETT","GLI","ION","LLA","MEN","NON","NTE","NTI","NTO","OLO","ONE","ONO","PER","QUE","SON","STA","STO","TAT","TRA","TTO","UNA","VER","ZIO","ENT","CHE","ATO","PER","NTE","CON","ELL","STA","ARE","MEN","ION","DEL","LLA","TTO","TAT","ESS","ERE","ETT","EST","ONE","ONO","ZIO","NON","ERA","CHI","GLI","COM","TRA","STO","NTI","SON","VER","ATT","UNA","QUE","NTO","AND","ALL","OLO","ANC","ANT","ALE"]; +"use strict"; + +const SYLLABE_LIST = ["AN","AR","AT","CA","CH","CO","DE","DI","EL","EN","ER","ES","HE","IA","IL","IN","IO","LA","LE","LI","LL","MA","ME","NA","NE","NO","NT","OL","ON","OR","PE","RA","RE","RI","RO","SE","SI","SO","ST","TA","TE","TI","TO","TR","TT","UN","TO","RE","ER","ON","CO","DI","TA","EN","IN","TE","AT","RA","AN","NO","NT","ST","LA","AR","AL","OR","CH","RI","TI","IO","LE","DE","ES","NE","ME","TT","EL","PE","IL","UN","IA","LI","SE","SO","LL","SI","OL","RO","MA","CA","NA","TR","HE","ALE","ALL","ANC","AND","ANT","ARE","ATO","ATT","CHE","CHI","COM","CON","DEL","ELL","ENT","ERA","ERE","ESS","EST","ETT","GLI","ION","LLA","MEN","NON","NTE","NTI","NTO","OLO","ONE","ONO","PER","QUE","SON","STA","STO","TAT","TRA","TTO","UNA","VER","ZIO","ENT","CHE","ATO","PER","NTE","CON","ELL","STA","ARE","MEN","ION","DEL","LLA","TTO","TAT","ESS","ERE","ETT","EST","ONE","ONO","ZIO","NON","ERA","CHI","GLI","COM","TRA","STO","NTI","SON","VER","ATT","UNA","QUE","NTO","AND","ALL","OLO","ANC","ANT","ALE"]; +const BOARD_DEFINITION = ` +# # 1 1 1 2 1 1 # #; +# # 1 1 1 1 1 1 # #; +1 1 2 2 2 2 2 2 1 1; +1 1 2 2 2 # 2 2 1 1; +2 1 2 2 1 2 2 2 1 1; +1 1 2 2 2 2 2 2 1 1; +1 1 2 2 2 2 2 2 1 1; +1 1 2 2 2 2 2 2 1 1; +# # 1 1 1 1 1 1 # #; +# # 1 1 1 1 1 1 # #; +` + +Array.prototype.removeIf = function(callback) { + var i = this.length; + while (i--) { + if (callback(this[i], i)) { + this.splice(i, 1); + } + } +}; + +function parseBoard(boardString) { + boardString = boardString.replaceAll(/[^#12;]/gi, ""); + let boardStringArray = boardString.split(";").filter(x => x.length > 0); + const boardHeight = boardStringArray.length; + if (boardHeight <= 0) { + throw new Error("Board parsing error: empty board"); + } + const boardWidth = boardStringArray[0].length; + if (boardWidth <= 0) { + throw new Error("Board parsing error: empty board"); + } + boardStringArray.forEach(v => { + if (v.length !== boardWidth) { + throw new Error(`Board parsing error: expected width ${boardWidth}, got ${v.length}`); + } + }); + const board = boardStringArray.map(row => { + return Array.from(row).map(char => { + switch (char) { + case '#': { + return { + type: "black", + cost: NaN, + occludedOnRotation: [false, false, false, false] + } + } + case '1': { + return { + type: "occupiable", + cost: 1, + occludedOnRotation: [false, false, false, false] + } + } + case '2': { + return { + type: "occupiable", + cost: 2, + occludedOnRotation: [false, false, false, false] + } + } + default: + throw new Error(`Unknown character: ${char}`); + } + }); + }); + + // Blocks for rotation 0 + for (let col = 0; col < boardWidth; col++) { + let blockedOnThisRotation = false; + for (let row = 0; row < boardHeight; row++) { + const square = board[row][col]; + if (!blockedOnThisRotation) { + if (square.type === "black") { + blockedOnThisRotation = true; + } + } + if (square.type === "occupiable") { + square.occludedOnRotation[0] = blockedOnThisRotation; + } + } + } + + // Blocks for rotation 1 + for (let row = 0; row < boardHeight; row++) { + let blockedOnThisRotation = false; + for (let col = 0; col < boardWidth; col++) { + const square = board[row][col]; + if (!blockedOnThisRotation) { + if (square.type === "black") { + blockedOnThisRotation = true; + } + } + if (square.type === "occupiable") { + square.occludedOnRotation[1] = blockedOnThisRotation; + } + } + } + + // Blocks for rotation 2 + for (let col = 0; col < boardWidth; col++) { + let blockedOnThisRotation = false; + for (let row = boardHeight - 1; row >= 0; row--) { + const square = board[row][col]; + if (!blockedOnThisRotation) { + if (square.type === "black") { + blockedOnThisRotation = true; + } + } + if (square.type === "occupiable") { + square.occludedOnRotation[2] = blockedOnThisRotation; + } + } + } + + // Blocks for rotation 3 + for (let row = 0; row < boardHeight; row++) { + let blockedOnThisRotation = false; + for (let col = boardWidth - 1; col >= 0; col--) { + const square = board[row][col]; + if (!blockedOnThisRotation) { + if (square.type === "black") { + blockedOnThisRotation = true; + } + } + if (square.type === "occupiable") { + square.occludedOnRotation[3] = blockedOnThisRotation; + } + } + } + return board; +} + function randomID() { - var u8 = crypto.getRandomValues(new Uint8Array(3)); - return Array.from(u8).map(n => chars[n % chars.length]).reduce((a, b) => a + b); + const u8 = crypto.getRandomValues(new Uint8Array(3)); + return Array.from(u8).map(n => SYLLABE_LIST[n % SYLLABE_LIST.length]).reduce((a, b) => a + b); +} + +const INITIAL_BOARD = parseBoard(BOARD_DEFINITION); + +console.log(INITIAL_BOARD); + +const ACTIONS_QUEUE = [] + +function buildGridArray(rows, columns, defaultValue) { + const array = new Array(rows); + for (let i = 0; i < rows; i++) { + array[i] = new Array(columns).fill(defaultValue); + } + return array; } document.addEventListener("DOMContentLoaded", e => { @@ -10,6 +160,158 @@ document.addEventListener("DOMContentLoaded", e => { const gameCodeLink = document.getElementById("game-code-link"); const newGameLink = document.getElementById("new-game-link"); const mainBody = document.getElementById("main-body"); + const boardElem = document.getElementById("board"); + const rotateLeftElem = document.getElementById("rotate-left"); + const rotateRightElem = document.getElementById("rotate-right"); + + function updateDatasetValue(elem, name, value) { + if (elem.dataset[name] !== '' + value) { + elem.dataset[name] = value; + } + } + + function syncBlock(board, boardElems, rowIndex, colIndex, gameState) { + const square = board[rowIndex][colIndex]; + const squareElem = boardElems[rowIndex][colIndex]; + updateDatasetValue(squareElem, "cost", square.cost); + updateDatasetValue(squareElem, "row", rowIndex); + updateDatasetValue(squareElem, "col", colIndex); + const currentPlacement = gameState.boardPlacements[rowIndex][colIndex]; + updateDatasetValue(squareElem, "square_type", square.type); + updateDatasetValue(squareElem, "occluded", square.occludedOnRotation[gameState.rotation]); + + switch (square.type) { + case "occupiable": { + switch (currentPlacement) { + case "free": { + updateDatasetValue(squareElem, "occupation_state", "free"); + updateDatasetValue(squareElem, "occupied_by", -1); + break; + } + case "player0": { + updateDatasetValue(squareElem, "occupation_state", "occupied"); + updateDatasetValue(squareElem, "occupied_by", 0); + break; + } + case "player1": { + updateDatasetValue(squareElem, "occupation_state", "occupied"); + updateDatasetValue(squareElem, "occupied_by", 1); + break; + } + default: { + throw new Error("Undefined current placement type: " + currentPlacement); + } + } + break; + } + case "black": { + break; + } + default: { + throw new Error("Undefined square type: " + square.type); + } + } + } + + function cloneGridArray(array) { + const rows = array.length; + const cols = array[0].length; + const cloned = new Array(rows); + for (let row = 0; row < rows; row++) { + const clonedRow = new Array(cols); + for (let col = 0; col < cols; col++) { + clonedRow[col] = array[row][col]; + } + cols[row] = clonedRow; + } + return cloned; + } + + function editState(state, action) { + const copy = { + myTurn: state.myTurn, + rotation: state.rotation, + boardPlacements: cloneGridArray(state.boardPlacements) + } + switch (action) { + case "occupy_square": { + copy[action.row][action.col] = ""; + return copy; + } + } + } + + function enqueueAction(action) { + if (action.type === "occupy_square") { + ACTIONS_QUEUE.removeIf(action => action.type === "occupy_square"); + } + ACTIONS_QUEUE.push(action); + console.debug("Added an element to the actions queue", ACTIONS_QUEUE); + } + + function onRequestRotateBy(board, delta, initialGameState) { + initialGameState + } + + function onSquareClick(square, squareElem, rowIndex, colIndex) { + console.debug(`Clicked square at ${rowIndex},${colIndex}`, square, squareElem); + switch (square.type) { + case "occupiable": { + enqueueAction({ + actor: "me", + type: "occupy_square", + row: rowIndex, + col: colIndex + }); + break; + } + } + } + + function buildInitialBoard(board) { + const rows = board.length; + const columns = board[0].length; + boardElem.style.gridTemplateRows = `repeat(${rows}, 1fr)`; + boardElem.style.gridTemplateColumns = `repeat(${columns}, 1fr)`; + const boardElems = []; + const initialGameState = { + myTurn: true, + rotation: 0, + boardPlacements: buildGridArray(rows, columns, "free") + }; + initialGameState.boardPlacements[0][3] = "player1"; + initialGameState.boardPlacements[1][3] = "player1"; + initialGameState.boardPlacements[2][3] = "player1"; + initialGameState.boardPlacements[3][3] = "player1"; + initialGameState.boardPlacements[0][4] = "player0"; + initialGameState.boardPlacements[3][3] = "player1"; + initialGameState.boardPlacements[4][3] = "player1"; + initialGameState.boardPlacements[5][3] = "player1"; + board.forEach((row, rowIndex) => { + row.forEach((square, colIndex) => { + const squareElem = document.createElement("div"); + squareElem.classList.add("square"); + squareElem.onclick = () => { + onSquareClick(square, squareElem, rowIndex, colIndex); + } + if (boardElems[rowIndex] == null) { + boardElems[rowIndex] = []; + } + boardElems[rowIndex][colIndex] = squareElem; + syncBlock(board, boardElems, rowIndex, colIndex, initialGameState); + boardElem.appendChild(squareElem); + }); + }); + + rotateLeftElem.onclick = () => { + onRequestRotateBy(board, 1, initialGameState); + } + rotateRightElem.onclick = () => { + onRequestRotateBy(board, -1, initialGameState); + } + } + + buildInitialBoard(INITIAL_BOARD); const eb = new EventBus(`${document.location.origin}/eventbus`); eb.enableReconnect(true); diff --git a/src/main/resources/webroot/static/index.html b/src/main/resources/webroot/static/index.html index 053a904..e54decd 100644 --- a/src/main/resources/webroot/static/index.html +++ b/src/main/resources/webroot/static/index.html @@ -12,8 +12,16 @@

Connect 2x

Game code: [share] [new game]

-
- +
+

Please wait...

+

Move points remaining: 0

+
+
+
+
+ + +
diff --git a/src/main/resources/webroot/static/style.css b/src/main/resources/webroot/static/style.css index ee76bcb..0f7df46 100644 --- a/src/main/resources/webroot/static/style.css +++ b/src/main/resources/webroot/static/style.css @@ -21,3 +21,60 @@ h1 { font-size: 75%; font-weight: bold; } + +#game { + perspective: 2000px; +} + +#board-viewer { + width: 82vmin; + height: 82vmin; + margin: auto; + position: relative; + transform: translateY(-10vmin) rotate3d(1, 0, 0, 55deg); +} + +#board { + display: grid; + background: black; + box-sizing: border-box; + width: 82vmin; + height: 82vmin; + grid-column-gap: 0.13rem; + grid-row-gap: 0.13rem; + padding: 0.52rem; + border-radius: 0.6rem; + + position: absolute; + left: 0; + top: 0; + + transform: rotate(0deg); + transition: transform 0.5s ease-in-out; + box-shadow: 0 0 40px 0 #00000061; +} + +.square { + display: inline-block; + width: 100%; + height: 100%; + background: #b1b1b1; + border-radius: 0.3rem; +} + +.square[data-square_type="black"] { + visibility: hidden; +} +.square[data-square_type="occupiable"][data-cost="2"][data-occupation_state="free"] { + background: rgba(255, 230, 190, 1); +} +.square[data-square_type="occupiable"][data-occluded="true"] { + filter: brightness(0.5); +} +.square[data-square_type="occupiable"][data-occupation_state="occupied"][data-occupied_by="0"] { + background: rgb(43, 131, 240); +} +.square[data-square_type="occupiable"][data-occupation_state="occupied"][data-occupied_by="1"] { + background: rgb(122, 215, 0); +} +