import { LotomaniaGame } from '../types'; /** * Implementa o algoritmo estratégico da Lotomania * Baseado no sistema de movimentação de colunas/linhas consecutivas do usuário */ export class LotomaniaAlgorithm { private games: LotomaniaGame[] = []; private verticalGames: LotomaniaGame[] = []; private horizontalGames: LotomaniaGame[] = []; private totalGames = 0; /** * Gera todos os jogos seguindo a estratégia específica do usuário * Implementa a lógica para jogos verticais E horizontais */ generateAllGames(): LotomaniaGame[] { this.games = []; this.verticalGames = []; this.horizontalGames = []; this.totalGames = 0; // Gerar jogos verticais (colunas) this.generateSequentialGames('vertical'); // Gerar jogos horizontais (linhas) this.generateSequentialGames('horizontal'); // Combinar todos os jogos this.games = [...this.verticalGames, ...this.horizontalGames]; // Renumerar IDs para sequência contínua this.games.forEach((game, index) => { game.id = index + 1; }); this.totalGames = this.games.length; return this.games; } private generateSequentialGames(type: 'vertical' | 'horizontal'): void { // Começamos com o padrão inicial let currentPattern = [1, 2, 3, 4, 5]; let phase = 1; let cycle = 1; // Primeiro jogo this.addGame(currentPattern, phase, cycle, type); // Gerar todos os jogos seguindo a lógica exata while (!this.isFinished(currentPattern)) { currentPattern = this.getNextPattern(currentPattern); // Incrementar fase quando necessário if (this.isNewPhase(currentPattern, type)) { phase++; cycle = 1; } else { cycle++; } this.addGame(currentPattern, phase, cycle, type); } } private addGame(pattern: number[], phase: number, cycle: number, type: 'vertical' | 'horizontal'): void { const gameArray = type === 'vertical' ? this.verticalGames : this.horizontalGames; const game: LotomaniaGame = { id: gameArray.length + 1, // Será renumerado depois markedColumns: type === 'vertical' ? [...pattern].sort((a, b) => a - b) : [], markedRows: type === 'horizontal' ? [...pattern].sort((a, b) => a - b) : undefined, phase, cycle, gameInPhase: gameArray.length + 1, type }; gameArray.push(game); } private getNextPattern(currentPattern: number[]): number[] { const pattern = [...currentPattern]; // Se a última coluna pode avançar if (pattern[4] < 10) { pattern[4] = pattern[4] + 1; return pattern; } // Se chegou no 10, precisamos avançar a penúltima coluna if (pattern[3] < 9) { pattern[3] = pattern[3] + 1; pattern[4] = pattern[3] + 1; return pattern; } // Se chegou no final dessa sequência, avançar a terceira coluna if (pattern[2] < 8) { pattern[2] = pattern[2] + 1; pattern[3] = pattern[2] + 1; pattern[4] = pattern[3] + 1; return pattern; } // Se chegou no final, avançar a segunda coluna if (pattern[1] < 7) { pattern[1] = pattern[1] + 1; pattern[2] = pattern[1] + 1; pattern[3] = pattern[2] + 1; pattern[4] = pattern[3] + 1; return pattern; } // Se chegou no final, avançar a primeira coluna if (pattern[0] < 6) { pattern[0] = pattern[0] + 1; pattern[1] = pattern[0] + 1; pattern[2] = pattern[1] + 1; pattern[3] = pattern[2] + 1; pattern[4] = pattern[3] + 1; return pattern; } // Padrão final return [6, 7, 8, 9, 10]; } private isNewPhase(pattern: number[], type: 'vertical' | 'horizontal'): boolean { const gameArray = type === 'vertical' ? this.verticalGames : this.horizontalGames; if (gameArray.length <= 1) return false; const previousGame = gameArray[gameArray.length - 2]; const previousPattern = type === 'vertical' ? previousGame.markedColumns : previousGame.markedRows!; // Se as três primeiras posições mudaram, é uma nova fase return ( pattern[0] !== previousPattern[0] || pattern[1] !== previousPattern[1] || pattern[2] !== previousPattern[2] ); } private isFinished(pattern: number[]): boolean { // A estratégia termina quando chegamos em [6,7,8,9,10] return ( pattern[0] === 6 && pattern[1] === 7 && pattern[2] === 8 && pattern[3] === 9 && pattern[4] === 10 ); } /** * Retorna apenas jogos verticais */ getVerticalGames(): LotomaniaGame[] { if (this.games.length === 0) { this.generateAllGames(); } return this.games.filter(game => game.type === 'vertical'); } /** * Retorna apenas jogos horizontais */ getHorizontalGames(): LotomaniaGame[] { if (this.games.length === 0) { this.generateAllGames(); } return this.games.filter(game => game.type === 'horizontal'); } /** * Calcula o número total de jogos necessários */ calculateTotalGames(): number { if (this.totalGames === 0) { this.generateAllGames(); } return this.totalGames; } /** * Retorna um jogo específico pelo ID */ getGameById(id: number): LotomaniaGame | undefined { if (this.games.length === 0) { this.generateAllGames(); } return this.games.find(game => game.id === id); } /** * Retorna jogos de uma fase específica */ getGamesByPhase(phase: number, type?: 'vertical' | 'horizontal'): LotomaniaGame[] { if (this.games.length === 0) { this.generateAllGames(); } let filteredGames = this.games.filter(game => game.phase === phase); if (type) { filteredGames = filteredGames.filter(game => game.type === type); } return filteredGames; } /** * Retorna estatísticas dos jogos */ getGameStatistics() { if (this.games.length === 0) { this.generateAllGames(); } const verticalGames = this.getVerticalGames(); const horizontalGames = this.getHorizontalGames(); const phases = Math.max(...this.games.map(g => g.phase)); const gamesPerPhase = this.games.reduce((acc, game) => { const key = `${game.type}_${game.phase}`; acc[key] = (acc[key] || 0) + 1; return acc; }, {} as Record); return { totalGames: this.totalGames, verticalGames: verticalGames.length, horizontalGames: horizontalGames.length, phases, gamesPerPhase, averageGamesPerPhase: this.totalGames / phases }; } /** * Converte as colunas marcadas em números da Lotomania (0-99) */ getNumbersFromColumns(markedColumns: number[]): number[] { const numbers: number[] = []; markedColumns.forEach(column => { // Números 0-99: coluna 1 = 0-9, coluna 2 = 10-19, etc. const startNumber = (column - 1) * 10; for (let i = 0; i < 10; i++) { numbers.push(startNumber + i); } }); return numbers.sort((a, b) => a - b); } /** * Converte as linhas marcadas em números da Lotomania (0-99) */ getNumbersFromRows(markedRows: number[]): number[] { const numbers: number[] = []; markedRows.forEach(row => { // Números 0-99: linha 1 = 0,10,20,...,90; linha 2 = 1,11,21,...,91; etc. for (let col = 0; col < 10; col++) { const number = (row - 1) + col * 10; numbers.push(number); } }); return numbers.sort((a, b) => a - b); } /** * Obtém os números marcados de um jogo (vertical ou horizontal) */ getNumbersFromGame(game: LotomaniaGame): number[] { if (game.type === 'vertical') { return this.getNumbersFromColumns(game.markedColumns); } else { return this.getNumbersFromRows(game.markedRows!); } } /** * Encontra o jogo baseado nas colunas marcadas */ findGameByColumns(markedColumns: number[]): LotomaniaGame | undefined { if (this.games.length === 0) { this.generateAllGames(); } const sortedColumns = [...markedColumns].sort((a, b) => a - b); return this.games.find(game => game.type === 'vertical' && game.markedColumns.length === sortedColumns.length && game.markedColumns.every((col, index) => col === sortedColumns[index]) ); } /** * Encontra o jogo baseado nas linhas marcadas */ findGameByRows(markedRows: number[]): LotomaniaGame | undefined { if (this.games.length === 0) { this.generateAllGames(); } const sortedRows = [...markedRows].sort((a, b) => a - b); return this.games.find(game => game.type === 'horizontal' && game.markedRows && game.markedRows.length === sortedRows.length && game.markedRows.every((row, index) => row === sortedRows[index]) ); } /** * Calcula quantos jogos restam */ getRemainingGames(currentGameId: number): number { return Math.max(0, this.totalGames - currentGameId); } /** * Obtém o próximo jogo */ getNextGame(currentGameId: number): LotomaniaGame | undefined { return this.getGameById(currentGameId + 1); } /** * Obtém o jogo anterior */ getPreviousGame(currentGameId: number): LotomaniaGame | undefined { return this.getGameById(currentGameId - 1); } /** * Obtém próximo jogo do mesmo tipo */ getNextGameOfType(currentGameId: number, type: 'vertical' | 'horizontal'): LotomaniaGame | undefined { if (this.games.length === 0) { this.generateAllGames(); } const currentIndex = this.games.findIndex(game => game.id === currentGameId); if (currentIndex === -1) return undefined; for (let i = currentIndex + 1; i < this.games.length; i++) { if (this.games[i].type === type) { return this.games[i]; } } return undefined; } /** * Obtém jogo anterior do mesmo tipo */ getPreviousGameOfType(currentGameId: number, type: 'vertical' | 'horizontal'): LotomaniaGame | undefined { if (this.games.length === 0) { this.generateAllGames(); } const currentIndex = this.games.findIndex(game => game.id === currentGameId); if (currentIndex === -1) return undefined; for (let i = currentIndex - 1; i >= 0; i--) { if (this.games[i].type === type) { return this.games[i]; } } return undefined; } }