Feature: Map size configuration
This commit is contained in:
@@ -8,7 +8,7 @@ import { AIBot } from './ai-bot.js';
|
|||||||
|
|
||||||
// Game constants
|
// Game constants
|
||||||
const HEX_SIZE = 14;
|
const HEX_SIZE = 14;
|
||||||
const MAP_SIZE = 20;
|
const DEFAULT_MAP_SIZE = 20;
|
||||||
const ANIMATION_DURATION = 300;
|
const ANIMATION_DURATION = 300;
|
||||||
|
|
||||||
// Player colors
|
// Player colors
|
||||||
@@ -45,6 +45,7 @@ class GameUI {
|
|||||||
this.canvas = document.getElementById('game-canvas');
|
this.canvas = document.getElementById('game-canvas');
|
||||||
this.ctx = this.canvas.getContext('2d');
|
this.ctx = this.canvas.getContext('2d');
|
||||||
this.map = null;
|
this.map = null;
|
||||||
|
this.mapSize = DEFAULT_MAP_SIZE;
|
||||||
this.selectedCell = null;
|
this.selectedCell = null;
|
||||||
this.currentTarget = null;
|
this.currentTarget = null;
|
||||||
this.currentPlayer = 1;
|
this.currentPlayer = 1;
|
||||||
@@ -95,6 +96,7 @@ class GameUI {
|
|||||||
startGame() {
|
startGame() {
|
||||||
// Get settings
|
// Get settings
|
||||||
this.playerCount = parseInt(document.getElementById('player-count').value);
|
this.playerCount = parseInt(document.getElementById('player-count').value);
|
||||||
|
this.mapSize = parseInt(document.getElementById('map-size').value);
|
||||||
|
|
||||||
for (let i = 1; i <= this.playerCount; i++) {
|
for (let i = 1; i <= this.playerCount; i++) {
|
||||||
const typeSelect = document.getElementById(`player${i}-type`);
|
const typeSelect = document.getElementById(`player${i}-type`);
|
||||||
@@ -109,7 +111,7 @@ class GameUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newGame() {
|
newGame() {
|
||||||
this.map = new HexMap(MAP_SIZE);
|
this.map = new HexMap(this.mapSize);
|
||||||
this.selectedCell = null;
|
this.selectedCell = null;
|
||||||
this.currentTarget = null;
|
this.currentTarget = null;
|
||||||
this.currentPlayer = 1;
|
this.currentPlayer = 1;
|
||||||
@@ -174,11 +176,13 @@ class GameUI {
|
|||||||
initializePlayers() {
|
initializePlayers() {
|
||||||
// Place starting units for each player at fixed positions that are always passable
|
// Place starting units for each player at fixed positions that are always passable
|
||||||
// Use corners of the map to ensure they don't overlap
|
// Use corners of the map to ensure they don't overlap
|
||||||
|
// Positions scale with map size
|
||||||
|
const offset = Math.max(1, Math.floor(this.mapSize / 10));
|
||||||
const positions = [
|
const positions = [
|
||||||
{ q: 1, r: 1 }, // Player 1 - top-left
|
{ q: offset, r: offset }, // Player 1 - top-left
|
||||||
{ q: MAP_SIZE - 2, r: MAP_SIZE - 2 }, // Player 2 - bottom-right
|
{ q: this.mapSize - 1 - offset, r: this.mapSize - 1 - offset }, // Player 2 - bottom-right
|
||||||
{ q: 1, r: MAP_SIZE - 2 }, // Player 3 - bottom-left
|
{ q: offset, r: this.mapSize - 1 - offset }, // Player 3 - bottom-left
|
||||||
{ q: MAP_SIZE - 2, r: 1 } // Player 4 - top-right
|
{ q: this.mapSize - 1 - offset, r: offset } // Player 4 - top-right
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 1; i <= this.playerCount; i++) {
|
for (let i = 1; i <= this.playerCount; i++) {
|
||||||
@@ -207,15 +211,15 @@ class GameUI {
|
|||||||
|
|
||||||
const sqrt3 = Math.sqrt(3);
|
const sqrt3 = Math.sqrt(3);
|
||||||
|
|
||||||
// Calculate actual map bounds
|
// Calculate actual map bounds based on dynamic map size
|
||||||
// For pointy-top hex grid:
|
// For pointy-top hex grid:
|
||||||
// - Width spans from q=0 to q=19, with r offset
|
// - Width spans from q=0 to q=mapSize-1, with r offset
|
||||||
// - Rightmost point: (q=19, r=19) at x = HEX_SIZE * sqrt3 * 28.5
|
// - Rightmost point: (q=mapSize-1, r=mapSize-1) at x = HEX_SIZE * sqrt3 * (mapSize - 1 + (mapSize - 1) / 2)
|
||||||
// - Height spans from r=0 to r=19
|
// - Height spans from r=0 to r=mapSize-1
|
||||||
// - Bottommost point: (any q, r=19) at y = HEX_SIZE * 1.5 * 19
|
// - Bottommost point: (any q, r=mapSize-1) at y = HEX_SIZE * 1.5 * (mapSize - 1)
|
||||||
|
|
||||||
const mapWidth = HEX_SIZE * sqrt3 * ((MAP_SIZE - 1) + (MAP_SIZE - 1) / 2);
|
const mapWidth = HEX_SIZE * sqrt3 * ((this.mapSize - 1) + (this.mapSize - 1) / 2);
|
||||||
const mapHeight = HEX_SIZE * 1.5 * (MAP_SIZE - 1);
|
const mapHeight = HEX_SIZE * 1.5 * (this.mapSize - 1);
|
||||||
|
|
||||||
// Add padding for hex radius (hex extends beyond center point)
|
// Add padding for hex radius (hex extends beyond center point)
|
||||||
const hexRadius = HEX_SIZE;
|
const hexRadius = HEX_SIZE;
|
||||||
@@ -256,7 +260,7 @@ class GameUI {
|
|||||||
const qi = Math.round(q);
|
const qi = Math.round(q);
|
||||||
const ri = Math.round(r);
|
const ri = Math.round(r);
|
||||||
|
|
||||||
if (qi >= 0 && qi < MAP_SIZE && ri >= 0 && ri < MAP_SIZE) {
|
if (qi >= 0 && qi < this.mapSize && ri >= 0 && ri < this.mapSize) {
|
||||||
return { q: qi, r: ri };
|
return { q: qi, r: ri };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -276,7 +280,7 @@ class GameUI {
|
|||||||
for (const [dq, dr] of directions) {
|
for (const [dq, dr] of directions) {
|
||||||
const nq = q + dq;
|
const nq = q + dq;
|
||||||
const nr = r + dr;
|
const nr = r + dr;
|
||||||
if (nq >= 0 && nq < MAP_SIZE && nr >= 0 && nr < MAP_SIZE) {
|
if (nq >= 0 && nq < this.mapSize && nr >= 0 && nr < this.mapSize) {
|
||||||
const cell = this.map.getCell(nq, nr);
|
const cell = this.map.getCell(nq, nr);
|
||||||
if (cell && cell.isPassable()) {
|
if (cell && cell.isPassable()) {
|
||||||
targets.push(cell);
|
targets.push(cell);
|
||||||
@@ -370,8 +374,8 @@ class GameUI {
|
|||||||
|
|
||||||
if (!this.map) return;
|
if (!this.map) return;
|
||||||
|
|
||||||
for (let r = 0; r < MAP_SIZE; r++) {
|
for (let r = 0; r < this.mapSize; r++) {
|
||||||
for (let q = 0; q < MAP_SIZE; q++) {
|
for (let q = 0; q < this.mapSize; q++) {
|
||||||
const cell = this.map.getCell(q, r);
|
const cell = this.map.getCell(q, r);
|
||||||
let color;
|
let color;
|
||||||
|
|
||||||
@@ -654,6 +658,9 @@ class GameUI {
|
|||||||
document.getElementById('current-turn').textContent = this.currentPlayer;
|
document.getElementById('current-turn').textContent = this.currentPlayer;
|
||||||
document.getElementById('game-phase').textContent = this.gamePhase;
|
document.getElementById('game-phase').textContent = this.gamePhase;
|
||||||
|
|
||||||
|
// Update map size display
|
||||||
|
document.getElementById('map-size-display').textContent = `${this.mapSize}x${this.mapSize}`;
|
||||||
|
|
||||||
const instruction = document.getElementById('action-instruction');
|
const instruction = document.getElementById('action-instruction');
|
||||||
if (this.selectedCell) {
|
if (this.selectedCell) {
|
||||||
instruction.textContent = `Select target (strength: ${this.selectedCell.getStrength()})`;
|
instruction.textContent = `Select target (strength: ${this.selectedCell.getStrength()})`;
|
||||||
|
|||||||
@@ -26,6 +26,16 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="setup-group">
|
||||||
|
<label for="map-size">Map Size:</label>
|
||||||
|
<select id="map-size">
|
||||||
|
<option value="10">Small (10x10)</option>
|
||||||
|
<option value="15">Medium (15x15)</option>
|
||||||
|
<option value="20" selected>Large (20x20)</option>
|
||||||
|
<option value="25">Extra Large (25x25)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setup-group">
|
<div class="setup-group">
|
||||||
<label>Player Types:</label>
|
<label>Player Types:</label>
|
||||||
<div id="player-types">
|
<div id="player-types">
|
||||||
@@ -91,6 +101,10 @@
|
|||||||
<span>Phase:</span>
|
<span>Phase:</span>
|
||||||
<span id="game-phase">Movement</span>
|
<span id="game-phase">Movement</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<span>Map Size:</span>
|
||||||
|
<span id="map-size-display">20x20</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" id="end-turn-btn">End Turn</button>
|
<button class="btn btn-primary" id="end-turn-btn">End Turn</button>
|
||||||
|
|||||||
@@ -1228,3 +1228,240 @@ describe('Multiple AI Bots Turn Chain', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('AIBot - Different Map Sizes', () => {
|
||||||
|
describe('AI works on 10x10 map', () => {
|
||||||
|
it('should find valid moves on 10x10 map', () => {
|
||||||
|
const map = new HexMap(10);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
// Clear map
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 2, 2, 1, 8);
|
||||||
|
setupPlayerCell(map, 2, 1, 2, 4);
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
assert.ok(moves.length > 0, 'Should find moves on 10x10 map');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect boundaries on 10x10 map', () => {
|
||||||
|
const map = new HexMap(10);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 0, 0, 1, 8);
|
||||||
|
map.getCell(0, 1).type = CELL_TYPES.EMPTY;
|
||||||
|
map.getCell(1, 0).type = CELL_TYPES.EMPTY;
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
moves.forEach(move => {
|
||||||
|
assert.ok(move.to.q >= 0 && move.to.q < 10, 'Target q should be in bounds');
|
||||||
|
assert.ok(move.to.r >= 0 && move.to.r < 10, 'Target r should be in bounds');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AI works on 15x15 map', () => {
|
||||||
|
it('should find valid moves on 15x15 map', () => {
|
||||||
|
const map = new HexMap(15);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 5, 5, 1, 8);
|
||||||
|
setupPlayerCell(map, 5, 4, 2, 4);
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
assert.ok(moves.length > 0, 'Should find moves on 15x15 map');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect boundaries on 15x15 map', () => {
|
||||||
|
const map = new HexMap(15);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 14, 14, 1, 8);
|
||||||
|
map.getCell(13, 14).type = CELL_TYPES.EMPTY;
|
||||||
|
map.getCell(14, 13).type = CELL_TYPES.EMPTY;
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
moves.forEach(move => {
|
||||||
|
assert.ok(move.to.q >= 0 && move.to.q < 15, 'Target q should be in bounds');
|
||||||
|
assert.ok(move.to.r >= 0 && move.to.r < 15, 'Target r should be in bounds');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AI works on 20x20 map', () => {
|
||||||
|
it('should find valid moves on 20x20 map', () => {
|
||||||
|
const map = new HexMap(20);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 10, 10, 1, 8);
|
||||||
|
setupPlayerCell(map, 10, 9, 2, 4);
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
assert.ok(moves.length > 0, 'Should find moves on 20x20 map');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect boundaries on 20x20 map', () => {
|
||||||
|
const map = new HexMap(20);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 19, 19, 1, 8);
|
||||||
|
map.getCell(18, 19).type = CELL_TYPES.EMPTY;
|
||||||
|
map.getCell(19, 18).type = CELL_TYPES.EMPTY;
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
moves.forEach(move => {
|
||||||
|
assert.ok(move.to.q >= 0 && move.to.q < 20, 'Target q should be in bounds');
|
||||||
|
assert.ok(move.to.r >= 0 && move.to.r < 20, 'Target r should be in bounds');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AI works on 25x25 map', () => {
|
||||||
|
it('should find valid moves on 25x25 map', () => {
|
||||||
|
const map = new HexMap(25);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 12, 12, 1, 8);
|
||||||
|
setupPlayerCell(map, 12, 11, 2, 4);
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
assert.ok(moves.length > 0, 'Should find moves on 25x25 map');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect boundaries on 25x25 map', () => {
|
||||||
|
const map = new HexMap(25);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 24, 24, 1, 8);
|
||||||
|
map.getCell(23, 24).type = CELL_TYPES.EMPTY;
|
||||||
|
map.getCell(24, 23).type = CELL_TYPES.EMPTY;
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
|
||||||
|
moves.forEach(move => {
|
||||||
|
assert.ok(move.to.q >= 0 && move.to.q < 25, 'Target q should be in bounds');
|
||||||
|
assert.ok(move.to.r >= 0 && move.to.r < 25, 'Target r should be in bounds');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate move priorities correctly on large map', () => {
|
||||||
|
const map = new HexMap(25);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 12, 12, 1, 10);
|
||||||
|
// Weak enemy
|
||||||
|
setupPlayerCell(map, 12, 11, 2, 3);
|
||||||
|
// Strong enemy
|
||||||
|
setupPlayerCell(map, 11, 12, 2, 9);
|
||||||
|
// Empty cell
|
||||||
|
map.getCell(13, 12).type = CELL_TYPES.EMPTY;
|
||||||
|
|
||||||
|
const playerCells = map.getPlayerCells(1);
|
||||||
|
const moves = bot.findPossibleMoves(playerCells);
|
||||||
|
moves.sort((a, b) => bot.movePriority(b) - bot.movePriority(a));
|
||||||
|
|
||||||
|
// Best move should be attack on weak enemy
|
||||||
|
assert.strictEqual(moves[0].type, 'attack');
|
||||||
|
assert.strictEqual(moves[0].defenseStrength, 3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AI playTurn works on different map sizes', () => {
|
||||||
|
it('should complete turn on 10x10 map', async () => {
|
||||||
|
const map = new HexMap(10);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 2, 2, 1, 8);
|
||||||
|
setupPlayerCell(map, 2, 1, 2, 2);
|
||||||
|
|
||||||
|
bot.thinkingTime = 0;
|
||||||
|
|
||||||
|
await bot.playTurn();
|
||||||
|
|
||||||
|
assert.strictEqual(gameUI.turnEnded, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should complete turn on 25x25 map', async () => {
|
||||||
|
const map = new HexMap(25);
|
||||||
|
const gameUI = new MockGameUI();
|
||||||
|
const bot = new AIBot(1, map, gameUI);
|
||||||
|
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
setupPlayerCell(map, 12, 12, 1, 8);
|
||||||
|
setupPlayerCell(map, 12, 11, 2, 2);
|
||||||
|
|
||||||
|
bot.thinkingTime = 0;
|
||||||
|
|
||||||
|
await bot.playTurn();
|
||||||
|
|
||||||
|
assert.strictEqual(gameUI.turnEnded, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,6 +2,95 @@ const { describe, it } = require('node:test');
|
|||||||
const assert = require('node:assert');
|
const assert = require('node:assert');
|
||||||
const { HexMap, HexCell, CELL_TYPES, MAP_SIZE } = require('../public/map.js');
|
const { HexMap, HexCell, CELL_TYPES, MAP_SIZE } = require('../public/map.js');
|
||||||
|
|
||||||
|
describe('HexMap - Dynamic Map Sizes', () => {
|
||||||
|
it('should create a 10x10 map', () => {
|
||||||
|
const map = new HexMap(10);
|
||||||
|
assert.strictEqual(map.size, 10);
|
||||||
|
assert.strictEqual(map.cells.size, 10 * 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a 15x15 map', () => {
|
||||||
|
const map = new HexMap(15);
|
||||||
|
assert.strictEqual(map.size, 15);
|
||||||
|
assert.strictEqual(map.cells.size, 15 * 15);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a 20x20 map', () => {
|
||||||
|
const map = new HexMap(20);
|
||||||
|
assert.strictEqual(map.size, 20);
|
||||||
|
assert.strictEqual(map.cells.size, 20 * 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a 25x25 map', () => {
|
||||||
|
const map = new HexMap(25);
|
||||||
|
assert.strictEqual(map.size, 25);
|
||||||
|
assert.strictEqual(map.cells.size, 25 * 25);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate cells with correct coordinates for all map sizes', () => {
|
||||||
|
const sizes = [10, 15, 20, 25];
|
||||||
|
|
||||||
|
for (const size of sizes) {
|
||||||
|
const map = new HexMap(size);
|
||||||
|
|
||||||
|
for (let q = 0; q < size; q++) {
|
||||||
|
for (let r = 0; r < size; r++) {
|
||||||
|
const cell = map.getCell(q, r);
|
||||||
|
assert.ok(cell, `Cell at ${q},${r} should exist for size ${size}`);
|
||||||
|
assert.strictEqual(cell.q, q);
|
||||||
|
assert.strictEqual(cell.r, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have correct neighbor counts for different map sizes', () => {
|
||||||
|
const sizes = [10, 15, 20, 25];
|
||||||
|
|
||||||
|
for (const size of sizes) {
|
||||||
|
const map = new HexMap(size);
|
||||||
|
|
||||||
|
// Clear all blocks for predictable testing
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
if (cell.type === CELL_TYPES.BLOCKED) {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Center cell should have 6 neighbors
|
||||||
|
const centerQ = Math.floor(size / 2);
|
||||||
|
const centerR = Math.floor(size / 2);
|
||||||
|
const centerNeighbors = map.getNeighbors(centerQ, centerR);
|
||||||
|
assert.strictEqual(centerNeighbors.length, 6, `Center cell should have 6 neighbors for size ${size}`);
|
||||||
|
|
||||||
|
// Corner cell should have 2 neighbors
|
||||||
|
const cornerNeighbors = map.getNeighbors(0, 0);
|
||||||
|
assert.strictEqual(cornerNeighbors.length, 2, `Corner cell should have 2 neighbors for size ${size}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate supply correctly for different map sizes', () => {
|
||||||
|
const sizes = [10, 15, 20, 25];
|
||||||
|
|
||||||
|
for (const size of sizes) {
|
||||||
|
const map = new HexMap(size);
|
||||||
|
|
||||||
|
// Clear any existing ownership
|
||||||
|
map.cells.forEach(cell => {
|
||||||
|
cell.type = CELL_TYPES.EMPTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a connected territory of 5 cells
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
map.setOwner(i, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const supply = map.calculateSupply(1);
|
||||||
|
assert.strictEqual(supply, 5, `Supply should be 5 for connected territory in size ${size}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('HexCell', () => {
|
describe('HexCell', () => {
|
||||||
it('should create a cell with axial coordinates', () => {
|
it('should create a cell with axial coordinates', () => {
|
||||||
const cell = new HexCell(3, 5);
|
const cell = new HexCell(3, 5);
|
||||||
|
|||||||
Reference in New Issue
Block a user