/** * Hexagonal grid map for the DiceWars game. * Browser version (ES Module) */ const MAP_SIZE = 20; const CELL_TYPES = { EMPTY: 0, BLOCKED: 1, PLAYER1: 2, PLAYER2: 3, }; class HexCell { constructor(q, r, type = CELL_TYPES.EMPTY) { this.q = q; this.r = r; this.type = type; this.dice = []; } getStrength() { if (this.dice.length === 0) return 0; const cnt = this.dice.length; const fullDice = 6; const currentDice = this.dice[this.dice.length - 1]; return (cnt - 1) * fullDice + currentDice; } isMaxStrength() { return this.dice.length >= 8 && this.getStrength() >= 48; } addDie(value) { if (this.dice.length < 8) { this.dice.push(value); return true; } return false; } setStrength(targetStrength) { if (targetStrength <= 0) { this.dice = []; return; } const fullDice = 6; const cnt = Math.floor((targetStrength - 1) / fullDice) + 1; const remainder = targetStrength - (cnt - 1) * fullDice; this.dice = new Array(cnt - 1).fill(6); if (remainder > 0) { this.dice.push(remainder); } } isPassable() { return this.type !== CELL_TYPES.BLOCKED; } isOwned() { return this.type === CELL_TYPES.PLAYER1 || this.type === CELL_TYPES.PLAYER2; } getOwner() { if (this.type === CELL_TYPES.PLAYER1) return 1; if (this.type === CELL_TYPES.PLAYER2) return 2; return 0; } } class HexMap { constructor(size = MAP_SIZE) { this.size = size; this.cells = new Map(); this.generate(); } generate() { this.cells.clear(); for (let q = 0; q < this.size; q++) { for (let r = 0; r < this.size; r++) { const key = this.getKey(q, r); const type = Math.random() < 0.15 ? CELL_TYPES.BLOCKED : CELL_TYPES.EMPTY; this.cells.set(key, new HexCell(q, r, type)); } } } getCell(q, r) { const key = this.getKey(q, r); return this.cells.get(key); } getCellByKey(key) { return this.cells.get(key); } getKey(q, r) { return `${q},${r}`; } getPassableCells() { return Array.from(this.cells.values()).filter(cell => cell.isPassable()); } getEmptyCells() { return Array.from(this.cells.values()).filter( cell => cell.isPassable() && !cell.isOwned() ); } getPlayerCells(playerId) { const targetType = playerId === 1 ? CELL_TYPES.PLAYER1 : CELL_TYPES.PLAYER2; return Array.from(this.cells.values()).filter( cell => cell.type === targetType ); } getNeighbors(q, r) { const directions = [ [1, 0], [1, -1], [0, -1], [-1, 0], [-1, 1], [0, 1] ]; const neighbors = []; for (const [dq, dr] of directions) { const nq = q + dq; const nr = r + dr; if (nq >= 0 && nq < this.size && nr >= 0 && nr < this.size) { const cell = this.getCell(nq, nr); if (cell && cell.isPassable()) { neighbors.push(cell); } } } return neighbors; } calculateSupply(playerId) { const playerCells = this.getPlayerCells(playerId); let supply = 0; for (const cell of playerCells) { supply += 1; } return supply; } setOwner(q, r, playerId) { const cell = this.getCell(q, r); if (cell && cell.isPassable()) { cell.type = playerId === 1 ? CELL_TYPES.PLAYER1 : CELL_TYPES.PLAYER2; return true; } return false; } clearOwner(q, r) { const cell = this.getCell(q, r); if (cell) { cell.type = CELL_TYPES.EMPTY; cell.dice = []; return true; } return false; } } export { HexMap, HexCell, CELL_TYPES, MAP_SIZE };