Refactor BSC Score to Astro, TypeScript, and modular architecture
This commit is contained in:
155
src/services/gameService.ts
Normal file
155
src/services/gameService.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import type { Game, GameType, StandardGame, EndlosGame, NewGameData } from '../types/game';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'bscscore_games';
|
||||
|
||||
export class GameService {
|
||||
/**
|
||||
* Load games from localStorage
|
||||
*/
|
||||
static loadGames(): Game[] {
|
||||
try {
|
||||
const savedGames = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||
return savedGames ? JSON.parse(savedGames) : [];
|
||||
} catch (error) {
|
||||
console.error('Error loading games from localStorage:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save games to localStorage
|
||||
*/
|
||||
static saveGames(games: Game[]): void {
|
||||
try {
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(games));
|
||||
} catch (error) {
|
||||
console.error('Error saving games to localStorage:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new game
|
||||
*/
|
||||
static createGame(gameData: NewGameData): Game {
|
||||
const baseGame = {
|
||||
id: Date.now(),
|
||||
gameType: gameData.gameType as GameType,
|
||||
raceTo: parseInt(gameData.raceTo, 10),
|
||||
status: 'active' as const,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
log: [],
|
||||
undoStack: [],
|
||||
};
|
||||
|
||||
if (gameData.gameType === '14/1 endlos') {
|
||||
const players = [
|
||||
{ name: gameData.player1, score: 0, consecutiveFouls: 0 },
|
||||
{ name: gameData.player2, score: 0, consecutiveFouls: 0 }
|
||||
];
|
||||
|
||||
if (gameData.player3) {
|
||||
players.push({ name: gameData.player3, score: 0, consecutiveFouls: 0 });
|
||||
}
|
||||
|
||||
return {
|
||||
...baseGame,
|
||||
players,
|
||||
currentPlayer: null,
|
||||
ballsOnTable: 15,
|
||||
} as EndlosGame;
|
||||
} else {
|
||||
const standardGame: StandardGame = {
|
||||
...baseGame,
|
||||
player1: gameData.player1,
|
||||
player2: gameData.player2,
|
||||
score1: 0,
|
||||
score2: 0,
|
||||
};
|
||||
|
||||
if (gameData.player3) {
|
||||
standardGame.player3 = gameData.player3;
|
||||
standardGame.score3 = 0;
|
||||
}
|
||||
|
||||
return standardGame;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a game's score (for standard games)
|
||||
*/
|
||||
static updateGameScore(game: StandardGame, player: number, change: number): StandardGame {
|
||||
const updated = { ...game };
|
||||
|
||||
if (player === 1) updated.score1 = Math.max(0, updated.score1 + change);
|
||||
if (player === 2) updated.score2 = Math.max(0, updated.score2 + change);
|
||||
if (player === 3 && updated.score3 !== undefined) {
|
||||
updated.score3 = Math.max(0, updated.score3 + change);
|
||||
}
|
||||
|
||||
updated.updatedAt = new Date().toISOString();
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a game is completed based on raceTo
|
||||
*/
|
||||
static isGameCompleted(game: Game): boolean {
|
||||
if (game.status === 'completed') return true;
|
||||
|
||||
if ('players' in game) {
|
||||
// EndlosGame
|
||||
return game.players.some(player => player.score >= game.raceTo);
|
||||
} else {
|
||||
// StandardGame
|
||||
const scores = [game.score1, game.score2, game.score3].filter(score => score !== undefined);
|
||||
return scores.some(score => score >= game.raceTo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the winner of a completed game
|
||||
*/
|
||||
static getGameWinner(game: Game): string | null {
|
||||
if (!this.isGameCompleted(game)) return null;
|
||||
|
||||
if ('players' in game) {
|
||||
// EndlosGame
|
||||
const winner = game.players.find(player => player.score >= game.raceTo);
|
||||
return winner?.name || null;
|
||||
} else {
|
||||
// StandardGame
|
||||
if (game.score1 >= game.raceTo) return game.player1;
|
||||
if (game.score2 >= game.raceTo) return game.player2;
|
||||
if (game.player3 && game.score3 && game.score3 >= game.raceTo) return game.player3;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract player name history from games
|
||||
*/
|
||||
static getPlayerNameHistory(games: Game[]): string[] {
|
||||
const nameLastUsed: Record<string, number> = {};
|
||||
|
||||
games.forEach(game => {
|
||||
const timestamp = new Date(game.updatedAt).getTime();
|
||||
|
||||
if ('players' in game) {
|
||||
// EndlosGame
|
||||
game.players.forEach(player => {
|
||||
nameLastUsed[player.name] = Math.max(nameLastUsed[player.name] || 0, timestamp);
|
||||
});
|
||||
} else {
|
||||
// StandardGame
|
||||
if (game.player1) nameLastUsed[game.player1] = Math.max(nameLastUsed[game.player1] || 0, timestamp);
|
||||
if (game.player2) nameLastUsed[game.player2] = Math.max(nameLastUsed[game.player2] || 0, timestamp);
|
||||
if (game.player3) nameLastUsed[game.player3] = Math.max(nameLastUsed[game.player3] || 0, timestamp);
|
||||
}
|
||||
});
|
||||
|
||||
return [...new Set(Object.keys(nameLastUsed))].sort((a, b) => nameLastUsed[b] - nameLastUsed[a]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user