k-l-lambda's picture
updated
502af73
import { v4 as uuidv4 } from "uuid";
import type { BoardShape, Position } from "../../../inc/trigo";
import { TrigoGame, StepType, StoneType } from "../../../inc/trigo/game";
export interface Player {
id: string;
nickname: string;
color: "black" | "white";
connected: boolean;
}
export interface GameState {
gameStatus: "waiting" | "playing" | "finished";
winner: "black" | "white" | null;
}
export interface GameRoom {
id: string;
players: { [playerId: string]: Player };
game: TrigoGame; // The actual game instance
gameState: GameState; // Game status metadata
createdAt: Date;
startedAt: Date | null;
}
export class GameManager {
private rooms: Map<string, GameRoom> = new Map();
private playerRoomMap: Map<string, string> = new Map();
private defaultBoardShape: BoardShape = { x: 5, y: 5, z: 5 }; // Default 5x5x5 board
constructor() {
console.log("GameManager initialized");
}
createRoom(playerId: string, nickname: string, boardShape?: BoardShape): GameRoom | null {
const roomId = this.generateRoomId();
const shape = boardShape || this.defaultBoardShape;
const room: GameRoom = {
id: roomId,
players: {
[playerId]: {
id: playerId,
nickname,
color: "black",
connected: true
}
},
game: new TrigoGame(shape, {
onStepAdvance: (_step, history) => {
console.log(`Step ${history.length}: Player made move`);
},
onCapture: (captured) => {
console.log(`Captured ${captured.length} stones`);
},
onWin: (winner) => {
console.log(`Game won by ${winner}`);
}
}),
gameState: {
gameStatus: "waiting",
winner: null
},
createdAt: new Date(),
startedAt: null
};
this.rooms.set(roomId, room);
this.playerRoomMap.set(playerId, roomId);
console.log(`Room ${roomId} created by ${playerId}`);
return room;
}
joinRoom(roomId: string, playerId: string, nickname: string): GameRoom | null {
const room = this.rooms.get(roomId);
if (!room) {
return null;
}
const playerCount = Object.keys(room.players).length;
if (playerCount >= 2) {
return null; // Room is full
}
// Assign white color to the second player
room.players[playerId] = {
id: playerId,
nickname,
color: "white",
connected: true
};
this.playerRoomMap.set(playerId, roomId);
// Start the game when second player joins
if (playerCount === 1) {
room.gameState.gameStatus = "playing";
room.startedAt = new Date();
}
return room;
}
leaveRoom(roomId: string, playerId: string): void {
const room = this.rooms.get(roomId);
if (!room) return;
if (room.players[playerId]) {
room.players[playerId].connected = false;
}
this.playerRoomMap.delete(playerId);
// Check if room should be deleted
const connectedPlayers = Object.values(room.players).filter((p) => p.connected);
if (connectedPlayers.length === 0) {
this.rooms.delete(roomId);
console.log(`Room ${roomId} deleted - no players remaining`);
}
}
makeMove(roomId: string, playerId: string, move: { x: number; y: number; z: number }): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
const player = room.players[playerId];
if (!player) return false;
// Check game status
if (room.gameState.gameStatus !== "playing") {
return false;
}
// Convert player color to Stone type
const expectedPlayer = player.color === "black" ? StoneType.BLACK : StoneType.WHITE;
const currentPlayer = room.game.getCurrentPlayer();
// Check if it's the player's turn
if (currentPlayer !== expectedPlayer) {
return false;
}
// Attempt to make the move using TrigoGame
const position: Position = { x: move.x, y: move.y, z: move.z };
const success = room.game.drop(position);
return success;
}
passTurn(roomId: string, playerId: string): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
const player = room.players[playerId];
if (!player) return false;
// Check game status
if (room.gameState.gameStatus !== "playing") {
return false;
}
// Convert player color to Stone type
const expectedPlayer = player.color === "black" ? StoneType.BLACK : StoneType.WHITE;
const currentPlayer = room.game.getCurrentPlayer();
// Check if it's the player's turn
if (currentPlayer !== expectedPlayer) {
return false;
}
return room.game.pass();
}
resign(roomId: string, playerId: string): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
const player = room.players[playerId];
if (!player) return false;
// Surrender the game
room.game.surrender();
// Update room state
room.gameState.gameStatus = "finished";
room.gameState.winner = player.color === "black" ? "white" : "black";
return true;
}
/**
* Undo the last move (悔棋)
*/
undoMove(roomId: string, playerId: string): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
const player = room.players[playerId];
if (!player) return false;
// Check game status
if (room.gameState.gameStatus !== "playing") {
return false;
}
return room.game.undo();
}
/**
* Get game board state for a room
*/
getGameBoard(roomId: string): number[][][] | null {
const room = this.rooms.get(roomId);
if (!room) return null;
return room.game.getBoard();
}
/**
* Get game statistics for a room
*/
getGameStats(roomId: string) {
const room = this.rooms.get(roomId);
if (!room) return null;
return room.game.getStats();
}
/**
* Get current player for a room
*/
getCurrentPlayer(roomId: string): "black" | "white" | null {
const room = this.rooms.get(roomId);
if (!room) return null;
const currentStone = room.game.getCurrentPlayer();
return currentStone === StoneType.BLACK ? "black" : "white";
}
/**
* Calculate and get territory for a room
*/
getTerritory(roomId: string) {
const room = this.rooms.get(roomId);
if (!room) return null;
return room.game.getTerritory();
}
/**
* End the game and determine winner based on territory
*/
endGameByTerritory(roomId: string): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
if (room.gameState.gameStatus !== "playing") {
return false;
}
// Calculate final territory
const territory = room.game.getTerritory();
// Determine winner
if (territory.black > territory.white) {
room.gameState.winner = "black";
} else if (territory.white > territory.black) {
room.gameState.winner = "white";
} else {
// Draw - could set winner to null or handle differently
room.gameState.winner = null;
}
room.gameState.gameStatus = "finished";
console.log(
`Game ${roomId} ended. Black: ${territory.black}, White: ${territory.white}, Winner: ${room.gameState.winner}`
);
return true;
}
/**
* Check if both players passed consecutively (game should end)
* Returns true if game was ended
*/
checkConsecutivePasses(roomId: string): boolean {
const room = this.rooms.get(roomId);
if (!room) return false;
const history = room.game.getHistory();
if (history.length < 2) return false;
// Get last two moves
const lastMove = history[history.length - 1];
const secondLastMove = history[history.length - 2];
// Check if both were passes
if (lastMove.type === StepType.PASS && secondLastMove.type === StepType.PASS) {
// Two consecutive passes - end the game
this.endGameByTerritory(roomId);
return true;
}
return false;
}
getRoom(roomId: string): GameRoom | undefined {
return this.rooms.get(roomId);
}
getPlayerRoom(playerId: string): GameRoom | undefined {
const roomId = this.playerRoomMap.get(playerId);
if (!roomId) return undefined;
return this.rooms.get(roomId);
}
getActiveRooms(): GameRoom[] {
return Array.from(this.rooms.values()).filter(
(room) => room.gameState.gameStatus !== "finished"
);
}
private generateRoomId(): string {
return uuidv4().substring(0, 8).toUpperCase();
}
}