First Commit

This commit is contained in:
evanpelle
2024-08-04 19:51:23 -07:00
commit 05f55c490f
53 changed files with 15862 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
import {GameImpl, PlayerImpl} from '../src/core/GameImpl';
import {EventBus} from '../src/core/EventBus';
import {Game, Cell, MutablePlayer, PlayerInfo, TerrainMap, TerrainTypes, Tile} from '../src/core/Game';
describe('borderTilesWith', () => {
let game: GameImpl;
let player1: PlayerImpl;
let player2: PlayerImpl;
let terrainMap: TerrainMap;
beforeEach(() => {
// Create a 5x5 terrain map
terrainMap = {
terrain: jest.fn().mockReturnValue(TerrainTypes.Land),
width: jest.fn().mockReturnValue(5),
height: jest.fn().mockReturnValue(5)
};
const eventBus = new EventBus();
game = new GameImpl(terrainMap, eventBus);
player1 = game.addPlayer(new PlayerInfo('Player 1', false)) as PlayerImpl;
player2 = game.addPlayer(new PlayerInfo('Player 2', false)) as PlayerImpl;
});
test('should return an empty set when players have no bordering tiles', () => {
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(0);
});
test('should return correct border tiles when players are adjacent', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
const p1BorderTile = Array.from(borderTilesP1)[0];
const p2BorderTile = Array.from(borderTilesP2)[0];
expect(p1BorderTile.cell()).toEqual(new Cell(0, 0));
expect(p2BorderTile.cell()).toEqual(new Cell(1, 0));
});
test('should update border tiles when a new tile is conquered', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(2, 0));
expect(player1.borderTilesWith(player2).size).toBe(0);
game.conquer(player2, new Cell(1, 0));
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(1);
expect(Array.from(borderTiles)[0].cell()).toEqual(new Cell(0, 0));
});
test('should handle multiple border tiles correctly', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(0, 1));
game.conquer(player2, new Cell(1, 0));
game.conquer(player2, new Cell(1, 1));
const borderTiles = player1.borderTilesWith(player2);
expect(borderTiles.size).toBe(2);
const borderCells = Array.from(borderTiles).map(tile => tile.cell());
expect(borderCells).toEqual(expect.arrayContaining([new Cell(0, 0), new Cell(0, 1)]));
});
test('should update border tiles when a tile changes ownership', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player1, new Cell(1, 0));
game.conquer(player2, new Cell(2, 0));
expect(player1.borderTilesWith(player2).size).toBe(1);
game.conquer(player2, new Cell(1, 0));
const borderTilesP1 = player1.borderTilesWith(player2);
const borderTilesP2 = player2.borderTilesWith(player1);
expect(borderTilesP1.size).toBe(1);
expect(borderTilesP2.size).toBe(1);
expect(Array.from(borderTilesP1)[0].cell()).toEqual(new Cell(0, 0));
expect(Array.from(borderTilesP2).map(t => t.cell())).toEqual(
expect.arrayContaining([new Cell(1, 0)])
);
});
test('should handle border tiles with TerraNullius', () => {
game.conquer(player1, new Cell(0, 0));
const borderWithTerraNullius = player1.borderTilesWith(game.terraNullius());
expect(borderWithTerraNullius.size).toBe(1);
const borderCells = Array.from(borderWithTerraNullius).map(tile => tile.cell());
expect(borderCells).toEqual(expect.arrayContaining([new Cell(0, 0)]));
});
test('should not include diagonal tiles as borders', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 1));
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
// test('should handle complex border scenarios', () => {
// // Create a more complex border scenario
// // 0 1 2 3 4
// // 0 1 1 2 2 2
// // 1 1 1 2 2 2
// // 2 1 1 1 2 2
// // 3 1 1 1 1 2
// // 4 1 1 1 1 1
// for (let y = 0; y < 5; y++) {
// for (let x = 0; x < 5; x++) {
// if (x + y < 6) {
// game.conquer(player1, new Cell(x, y));
// } else {
// game.conquer(player2, new Cell(x, y));
// }
// }
// }
// const borderTilesP1 = player1.borderTilesWith(player2);
// const borderTilesP2 = player2.borderTilesWith(player1);
// expect(borderTilesP1.size).toBe(5);
// expect(borderTilesP2.size).toBe(5);
// const expectedBorderP1 = [
// new Cell(2, 0),
// new Cell(2, 1),
// new Cell(3, 2),
// new Cell(3, 3),
// new Cell(4, 3)
// ];
// const expectedBorderP2 = [
// new Cell(2, 2),
// new Cell(3, 1),
// new Cell(3, 2),
// new Cell(4, 1),
// new Cell(4, 2)
// ];
// const actualBorderP1 = Array.from(borderTilesP1).map(t => t.cell());
// const actualBorderP2 = Array.from(borderTilesP2).map(t => t.cell());
// expect(actualBorderP1).toEqual(expect.arrayContaining(expectedBorderP1));
// expect(actualBorderP2).toEqual(expect.arrayContaining(expectedBorderP2));
// });
test('should handle border updates when a player loses all tiles', () => {
game.conquer(player1, new Cell(0, 0));
game.conquer(player2, new Cell(1, 0));
expect(player1.borderTilesWith(player2).size).toBe(1);
game.conquer(player1, new Cell(1, 0)); // Player 1 takes Player 2's only tile
expect(player1.borderTilesWith(player2).size).toBe(0);
expect(player2.borderTilesWith(player1).size).toBe(0);
});
});
+194
View File
@@ -0,0 +1,194 @@
// import {Game, Player, Tile, Cell, TerraNullius, PlayerInfo} from '../src/core/GameApi';
// import {placeName, calculateBoundingBox, createGrid, findLargestInscribedRectangle, largestRectangleInHistogram, calculateFontSize} from '../src/client/NameBoxCalculator';
// class MockPlayer implements Player {
// constructor(private playerTiles: [number, number][]) { }
// info(): PlayerInfo {
// return new PlayerInfo("TestPlayer", false);
// }
// id(): PlayerID {
// return 1;
// }
// troops(): number {
// return 0;
// }
// ownsTile(cell: Cell): boolean {
// return this.playerTiles.some(([x, y]) => x === cell.x && y === cell.y);
// }
// isAlive(): boolean {
// return true;
// }
// gameState(): Game {
// return {} as Game; // This should be properly implemented
// }
// executions(): ExecutionView[] {
// return [];
// }
// borderTilesWith(other: Player | TerraNullius): ReadonlySet<Tile> {
// return new Set();
// }
// isPlayer(): this is Player {
// return true;
// }
// neighbors(): (Player | TerraNullius)[] {
// return [];
// }
// }
// class MockGame implements Game {
// private tiles: Tile[][] = [];
// private mockPlayer: Player;
// constructor(width: number, height: number, playerTiles: [number, number][]) {
// this.tiles = Array(height).fill(null).map(() => Array(width).fill(null));
// this.mockPlayer = new MockPlayer(playerTiles);
// for (let y = 0; y < height; y++) {
// for (let x = 0; x < width; x++) {
// this.tiles[y][x] = {
// owner: () => playerTiles.some(([px, py]) => px === x && py === y) ? this.mockPlayer : this.terraNullius(),
// hasOwner: () => playerTiles.some(([px, py]) => px === x && py === y),
// isBorder: () => false,
// isInterior: () => false,
// cell: () => new Cell(x, y),
// terrain: () => ({expansionCost: 1, expansionTime: 1}),
// game: () => this,
// neighbors: () => []
// };
// }
// }
// }
// player(id: PlayerID): Player {return this.mockPlayer;}
// tile(cell: Cell): Tile {return this.tiles[cell.y][cell.x];}
// isOnMap(cell: Cell): boolean {return cell.x >= 0 && cell.x < this.width() && cell.y >= 0 && cell.y < this.height();}
// neighbors(cell: Cell): Cell[] {return [];}
// width(): number {return this.tiles[0].length;}
// height(): number {return this.tiles.length;}
// forEachTile(fn: (tile: Tile) => void): void {this.tiles.flat().forEach(fn);}
// executions(): ExecutionView[] {return [];}
// terraNullius(): TerraNullius {return {ownsTile: () => false, isPlayer: () => false};}
// tick() { }
// addExecution(...exec: Execution[]) { }
// }
// // Mock implementations
// class MockGame implements Game {
// private tiles: Tile[][] = [];
// private mockPlayer: Player;
// constructor(width: number, height: number, playerTiles: [number, number][]) {
// this.tiles = Array(height).fill(null).map(() => Array(width).fill(null));
// this.mockPlayer = {
// info: () => new PlayerInfo("TestPlayer", false),
// id: () => 1,
// troops: () => 0,
// ownsTile: (cell: Cell) => playerTiles.some(([x, y]) => x === cell.x && y === cell.y),
// isAlive: () => true,
// gameState: () => this,
// executions: () => [],
// borderTilesWith: () => new Set(),
// isPlayer: function (this: Player): this is Player {return true},
// neighbors: () => []
// };
// for (let y = 0; y < height; y++) {
// for (let x = 0; x < width; x++) {
// this.tiles[y][x] = {
// owner: () => playerTiles.some(([px, py]) => px === x && py === y) ? this.mockPlayer : this.terraNullius(),
// hasOwner: () => playerTiles.some(([px, py]) => px === x && py === y),
// isBorder: () => false,
// isInterior: () => false,
// cell: () => new Cell(x, y),
// terrain: () => ({expansionCost: 1, expansionTime: 1}),
// game: () => this,
// neighbors: () => []
// };
// }
// }
// }
// player(id: number): Player {return this.mockPlayer;}
// tile(cell: Cell): Tile {return this.tiles[cell.y][cell.x];}
// isOnMap(cell: Cell): boolean {return cell.x >= 0 && cell.x < this.width() && cell.y >= 0 && cell.y < this.height();}
// neighbors(cell: Cell): Cell[] {return [];}
// width(): number {return this.tiles[0].length;}
// height(): number {return this.tiles.length;}
// forEachTile(fn: (tile: Tile) => void): void {this.tiles.flat().forEach(fn);}
// executions(): any[] {return [];}
// terraNullius(): TerraNullius {return {ownsTile: () => false, isPlayer: () => false};}
// tick() { }
// addExecution(...exec: any[]) { }
// }
// describe('Territory Name Placement', () => {
// test('placeName should return a position and font size', () => {
// const game = new MockGame(5, 5, [[1, 1], [2, 1], [3, 1], [2, 2], [2, 3]]);
// const player = game.player(1);
// const result = placeName(game, player);
// expect(result).toHaveProperty('position');
// expect(result).toHaveProperty('fontSize');
// expect(result.position).toHaveProperty('x');
// expect(result.position).toHaveProperty('y');
// expect(typeof result.fontSize).toBe('number');
// });
// test('calculateBoundingBox should return correct bounding box', () => {
// const game = new MockGame(5, 5, [[1, 1], [3, 3]]);
// const player = game.player(1);
// const boundingBox = calculateBoundingBox(game, player);
// expect(boundingBox).toEqual({min: {x: 1, y: 1}, max: {x: 3, y: 3}});
// });
// test('createGrid should create correct boolean grid', () => {
// const game = new MockGame(3, 3, [[0, 0], [1, 1], [2, 2]]);
// const player = game.player(1);
// const boundingBox = {min: {x: 0, y: 0}, max: {x: 2, y: 2}};
// const grid = createGrid(game, player, boundingBox);
// expect(grid).toEqual([
// [true, false, false],
// [false, true, false],
// [false, false, true]
// ]);
// });
// test('findLargestInscribedRectangle should find correct rectangle', () => {
// const grid = [
// [true, true, true],
// [true, true, false],
// [true, true, false]
// ];
// const result = findLargestInscribedRectangle(grid);
// expect(result).toEqual({x: 0, y: 0, width: 2, height: 3});
// });
// test('largestRectangleInHistogram should find correct rectangle', () => {
// const heights = [2, 1, 5, 6, 2, 3];
// const result = largestRectangleInHistogram(heights);
// expect(result).toEqual({x: 2, y: 0, width: 2, height: 5});
// });
// test('calculateFontSize should return correct font size', () => {
// const rectangle = {x: 0, y: 0, width: 100, height: 50};
// const name = "TestPlayer";
// const fontSize = calculateFontSize(rectangle, name);
// expect(fontSize).toBe(25); // 50 / 2 = 25 (height constrained)
// });
// });