Debug stuff

This commit is contained in:
Andrea Cavalli 2024-05-24 17:58:16 +02:00
parent 7b920dd6a9
commit 3f0688d0aa
3 changed files with 372 additions and 5 deletions

View File

@ -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() { function randomID() {
var u8 = crypto.getRandomValues(new Uint8Array(3)); const u8 = crypto.getRandomValues(new Uint8Array(3));
return Array.from(u8).map(n => chars[n % chars.length]).reduce((a, b) => a + b); 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 => { document.addEventListener("DOMContentLoaded", e => {
@ -10,6 +160,158 @@ document.addEventListener("DOMContentLoaded", e => {
const gameCodeLink = document.getElementById("game-code-link"); const gameCodeLink = document.getElementById("game-code-link");
const newGameLink = document.getElementById("new-game-link"); const newGameLink = document.getElementById("new-game-link");
const mainBody = document.getElementById("main-body"); 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`); const eb = new EventBus(`${document.location.origin}/eventbus`);
eb.enableReconnect(true); eb.enableReconnect(true);

View File

@ -12,8 +12,16 @@
<div id="main-body"> <div id="main-body">
<h1>Connect 2x</h1> <h1>Connect 2x</h1>
<p>Game code: <span id="game-code"></span> <a id="game-code-link" href="#">[share]</a> <a id="new-game" href="/">[new game]</a></p> <p>Game code: <span id="game-code"></span> <a id="game-code-link" href="#">[share]</a> <a id="new-game" href="/">[new game]</a></p>
<main id="board"> <main id="game">
<h2 id="turn-label">Please wait...</h2>
<h3 id="move-points-title">Move points remaining: <span id="move-points-value">0</span></h3>
<div id="board-viewer">
<div id="board"></div>
</div>
<div id="buttons-container">
<button id="rotate-left" class="btn"></button>
<button id="rotate-right" class="btn"></button>
</div>
</main> </main>
</div> </div>
</body> </body>

View File

@ -21,3 +21,60 @@ h1 {
font-size: 75%; font-size: 75%;
font-weight: bold; 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);
}