diff --git a/src/__fixtures__/dominion-lib-fixtures.ts b/src/__fixtures__/dominion-lib-fixtures.ts index ee9c6dd..deec11f 100644 --- a/src/__fixtures__/dominion-lib-fixtures.ts +++ b/src/__fixtures__/dominion-lib-fixtures.ts @@ -48,6 +48,7 @@ export function createMockGame(playerCount: number, overrides?: Partial): timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.START_GAME, }, ], diff --git a/src/components/DominionAssistant.tsx b/src/components/DominionAssistant.tsx index 8d8d14f..9fa7a0b 100644 --- a/src/components/DominionAssistant.tsx +++ b/src/components/DominionAssistant.tsx @@ -68,16 +68,10 @@ const DominionAssistant: React.FC = ({ route, navigation const nextTurn = () => { setGameState((prevGame: IGame) => { const nextPlayerIndex = getNextPlayerIndex(prevGame); - addLogEntry( - prevGame, - nextPlayerIndex, - prevGame.currentPlayerIndex, - GameLogActionWithCount.NEXT_TURN, - { - playerTurnDetails: gameState.players.map((player) => player.turn), - prevPlayerIndex: gameState.currentPlayerIndex, - } - ); + addLogEntry(prevGame, nextPlayerIndex, GameLogActionWithCount.NEXT_TURN, { + playerTurnDetails: gameState.players.map((player) => player.turn), + prevPlayerIndex: gameState.currentPlayerIndex, + }); const updatedGame = incrementTurnCountersAndPlayerIndices(prevGame); return resetPlayerTurnCounters(updatedGame); }); @@ -85,15 +79,9 @@ const DominionAssistant: React.FC = ({ route, navigation const endGame = () => { setGameState((prevState: IGame) => { - addLogEntry( - prevState, - NO_PLAYER, - prevState.currentPlayerIndex, - GameLogActionWithCount.END_GAME, - { - prevPlayerIndex: gameState.currentPlayerIndex, - } - ); + addLogEntry(prevState, NO_PLAYER, GameLogActionWithCount.END_GAME, { + prevPlayerIndex: gameState.currentPlayerIndex, + }); return { ...prevState, diff --git a/src/components/GameLog.tsx b/src/components/GameLog.tsx index cab4df3..ad11605 100644 --- a/src/components/GameLog.tsx +++ b/src/components/GameLog.tsx @@ -33,14 +33,20 @@ const GameLog: React.FC = () => { - {gameState.log.map((entry, index) => ( - - ))} + {gameState.log.map((entry, index) => { + const hasLinkedAction = gameState.log.some( + (logEntry) => logEntry.linkedActionId === entry.id + ); + return ( + + ); + })} diff --git a/src/components/GameLogEntry.tsx b/src/components/GameLogEntry.tsx index 6619410..9e622f3 100644 --- a/src/components/GameLogEntry.tsx +++ b/src/components/GameLogEntry.tsx @@ -30,9 +30,15 @@ interface GameLogEntryProps { logIndex: number; entry: ILogEntry; isCurrentPlayer: boolean; + hasLinkedAction: boolean; } -const GameLogEntry: React.FC = ({ logIndex, entry, isCurrentPlayer }) => { +const GameLogEntry: React.FC = ({ + logIndex, + entry, + isCurrentPlayer, + hasLinkedAction, +}) => { const { gameState, setGameState } = useGameContext(); const [openUndoDialog, setOpenUndoDialog] = useState(false); @@ -127,6 +133,8 @@ const GameLogEntry: React.FC = ({ logIndex, entry, isCurrentP {isNotTriggeredByPlayer && relevantPlayer !== undefined && ( )} + {entry.action === GameLogActionWithCount.NEXT_TURN && + `\u00A0(\u00A0${entry.turn}\u00A0)`} {isAttributeChangeOutOfTurn && ( = ({ logIndex, entry, isCurrentP - {entry.linkedActionId && } + {(hasLinkedAction || entry.linkedActionId) && ( + + )} {canUndoAction(gameState, logIndex) && ( diff --git a/src/components/Player.tsx b/src/components/Player.tsx index bc806e8..93c6cf4 100644 --- a/src/components/Player.tsx +++ b/src/components/Player.tsx @@ -14,13 +14,11 @@ import SettingsIcon from '@mui/icons-material/Settings'; import { useGameContext } from '@/components/GameContext'; import SuperCapsText from '@/components/SuperCapsText'; import IncrementDecrementControl from '@/components/IncrementDecrementControl'; -import { ILogEntry } from '@/game/interfaces/log-entry'; import { updatePlayerField } from '@/game/dominion-lib'; -import { addLogEntry, victoryFieldToGameLogAction } from '@/game/dominion-lib-log'; +import { addLogEntry, fieldSubfieldToGameLogAction } from '@/game/dominion-lib-log'; import { GameLogActionWithCount } from '@/game/enumerations/game-log-action-with-count'; import { PlayerFieldMap } from '@/game/types'; import { useAlert } from '@/components/AlertContext'; -import { FailedAddLogEntryError } from '@/game/errors/failed-add-log'; import '@/styles.scss'; import { IGame } from '@/game/interfaces/game'; @@ -76,46 +74,97 @@ const Player: React.FC = () => { subfield: PlayerFieldMap[T], increment: number, linkedActionId?: string - ): ILogEntry => { - let logEntry: ILogEntry | undefined; - setGameState((prevState: IGame) => { - try { - const updatedGame = updatePlayerField( - prevState, - prevState.selectedPlayerIndex, - field, - subfield, - increment - ); - if (!updatedGame) { - return prevState; - } - const action = victoryFieldToGameLogAction(field, subfield, increment); - logEntry = addLogEntry( - updatedGame, - updatedGame.selectedPlayerIndex, - updatedGame.currentPlayerIndex, - action, - { - count: Math.abs(increment), - correction: isCorrection, - linkedActionId, - } - ); - return updatedGame; - } catch (error) { - if (error instanceof Error) { - showAlert('Could not increment', error.message); - } else { - showAlert('Could not increment', 'Unknown error'); + ): void => { + const prevGame = { ...gameState }; + try { + const updatedGame = updatePlayerField( + prevGame, + prevGame.selectedPlayerIndex, + field, + subfield, + increment + ); + const action = fieldSubfieldToGameLogAction(field, subfield, increment); + addLogEntry(updatedGame, updatedGame.selectedPlayerIndex, action, { + count: Math.abs(increment), + correction: isCorrection, + linkedActionId, + }); + setGameState(updatedGame); + } catch (error) { + console.error('Error updating game state:', error); + if (error instanceof Error) { + showAlert('Could not increment', error.message); + } else { + showAlert('Could not increment', 'Unknown error'); + } + setGameState(prevGame); + } + }; + + const handleCombinedFieldChange = ( + decrementField: T, + decrementSubfield: PlayerFieldMap[T], + decrement: number, + incrementField: T, + incrementSubfield: PlayerFieldMap[T], + increment: number + ): void => { + const prevGame = { ...gameState }; + try { + // Perform the decrement action + const tempGame = updatePlayerField( + prevGame, + prevGame.selectedPlayerIndex, + decrementField, + decrementSubfield, + decrement + ); + const decrementAction = fieldSubfieldToGameLogAction( + decrementField, + decrementSubfield, + decrement + ); + const decrementLogEntry = addLogEntry( + tempGame, + tempGame.selectedPlayerIndex, + decrementAction, + { + count: Math.abs(decrement), + correction: isCorrection, } - return prevState; + ); + + // Perform the increment action using the logEntry ID from the decrement action + const updatedGame = updatePlayerField( + tempGame, + tempGame.selectedPlayerIndex, + incrementField, + incrementSubfield, + increment + ); + const incrementAction = fieldSubfieldToGameLogAction( + incrementField, + incrementSubfield, + increment + ); + addLogEntry(updatedGame, updatedGame.selectedPlayerIndex, incrementAction, { + count: Math.abs(increment), + correction: isCorrection, + linkedActionId: decrementLogEntry.id, + }); + + // Update the actual game state with the final updated game + setGameState(updatedGame); + } catch (error) { + if (error instanceof Error) { + showAlert('Could not update field', error.message); + } else { + showAlert('Could not update field', 'Unknown error'); } - }); - if (!logEntry) { - throw new FailedAddLogEntryError(); + // Rollback to the previous game state + setGameState(prevGame); } - return logEntry; }; const handleCorrectionChange = (event: React.ChangeEvent) => { @@ -137,7 +186,6 @@ const Player: React.FC = () => { addLogEntry( newGameState, newGameState.selectedPlayerIndex, - newGameState.currentPlayerIndex, GameLogActionWithCount.ADD_PROPHECY, { count: 1 } ); @@ -158,7 +206,6 @@ const Player: React.FC = () => { addLogEntry( newGameState, newGameState.selectedPlayerIndex, - newGameState.currentPlayerIndex, GameLogActionWithCount.REMOVE_PROPHECY, { count: 1 } ); @@ -241,12 +288,14 @@ const Player: React.FC = () => { value={player.turn.actions} onIncrement={() => handleFieldChange('turn', 'actions', 1)} onDecrement={() => { - const record = handleFieldChange('turn', 'actions', -1); + // greatLeaderProphecy gives unlimited actions when the prophecy is empty if ( gameState.risingSun.greatLeaderProphecy && gameState.risingSun.prophecy.suns === 0 ) { - handleFieldChange('turn', 'actions', 1, record.id); + handleCombinedFieldChange('turn', 'actions', -1, 'turn', 'actions', 1); + } else { + handleFieldChange('turn', 'actions', -1); } }} /> @@ -265,11 +314,13 @@ const Player: React.FC = () => { {(showMats || showGlobalMats) && ( - - - Mats - - + {showMats && ( + + + Mats + + + )} {gameState.options.mats.coffersVillagers && ( <> { onIncrement={() => handleFieldChange('mats', 'coffers', 1)} onDecrement={() => { // spending a coffer gives a coin - const record = handleFieldChange('mats', 'coffers', -1); - handleFieldChange('turn', 'coins', 1, record.id); + handleCombinedFieldChange('mats', 'coffers', -1, 'turn', 'coins', 1); }} /> { onIncrement={() => handleFieldChange('mats', 'villagers', 1)} onDecrement={() => { // spending a villager gives an action - const record = handleFieldChange('mats', 'villagers', -1); - handleFieldChange('turn', 'actions', 1, record.id); + handleCombinedFieldChange('mats', 'villagers', -1, 'turn', 'actions', 1); }} /> @@ -314,7 +363,7 @@ const Player: React.FC = () => { )} {showGlobalMats && ( <> - + diff --git a/src/components/Scoreboard.tsx b/src/components/Scoreboard.tsx index dfb4423..c2feac2 100644 --- a/src/components/Scoreboard.tsx +++ b/src/components/Scoreboard.tsx @@ -42,15 +42,9 @@ const Scoreboard: React.FC = () => { const handlePlayerSelect = (index: number) => { setGameState((prevState: IGame) => { - addLogEntry( - prevState, - index, - prevState.currentPlayerIndex, - GameLogActionWithCount.SELECT_PLAYER, - { - prevPlayerIndex: prevState.selectedPlayerIndex, - } - ); + addLogEntry(prevState, index, GameLogActionWithCount.SELECT_PLAYER, { + prevPlayerIndex: prevState.selectedPlayerIndex, + }); return { ...prevState, selectedPlayerIndex: index }; }); }; diff --git a/src/game/__tests__/dominion-lib-NewGameState.spec.ts b/src/game/__tests__/dominion-lib-NewGameState.spec.ts index 095daa1..6cf9ac2 100644 --- a/src/game/__tests__/dominion-lib-NewGameState.spec.ts +++ b/src/game/__tests__/dominion-lib-NewGameState.spec.ts @@ -42,6 +42,7 @@ describe('NewGameState', () => { action: GameLogActionWithCount.START_GAME, playerIndex: initialGameState.firstPlayerIndex, currentPlayerIndex: initialGameState.currentPlayerIndex, + turn: 1, } as ILogEntry, ]); expect(result.supply).toBeDefined(); diff --git a/src/game/__tests__/dominion-lib-victoryFieldToGameLogAction.spec.ts b/src/game/__tests__/dominion-lib-fieldSubfieldToGameLogAction.spec.ts similarity index 64% rename from src/game/__tests__/dominion-lib-victoryFieldToGameLogAction.spec.ts rename to src/game/__tests__/dominion-lib-fieldSubfieldToGameLogAction.spec.ts index afc9841..11eeb6f 100644 --- a/src/game/__tests__/dominion-lib-victoryFieldToGameLogAction.spec.ts +++ b/src/game/__tests__/dominion-lib-fieldSubfieldToGameLogAction.spec.ts @@ -1,227 +1,227 @@ -import { victoryFieldToGameLogAction } from '@/game/dominion-lib-log'; +import { fieldSubfieldToGameLogAction } from '@/game/dominion-lib-log'; import { InvalidFieldError } from '@/game/errors/invalid-field'; import { GameLogActionWithCount } from '@/game/enumerations/game-log-action-with-count'; import { PlayerSubField } from '../types'; describe('victoryFieldToGameLogAction', () => { it('should return ADD_ACTIONS for turn actions increment', () => { - expect(victoryFieldToGameLogAction('turn', 'actions', 1)).toBe( + expect(fieldSubfieldToGameLogAction('turn', 'actions', 1)).toBe( GameLogActionWithCount.ADD_ACTIONS ); }); it('should return REMOVE_ACTIONS for turn actions decrement', () => { - expect(victoryFieldToGameLogAction('turn', 'actions', -1)).toBe( + expect(fieldSubfieldToGameLogAction('turn', 'actions', -1)).toBe( GameLogActionWithCount.REMOVE_ACTIONS ); }); it('should return ADD_BUYS for turn buys increment', () => { - expect(victoryFieldToGameLogAction('turn', 'buys', 1)).toBe(GameLogActionWithCount.ADD_BUYS); + expect(fieldSubfieldToGameLogAction('turn', 'buys', 1)).toBe(GameLogActionWithCount.ADD_BUYS); }); it('should return REMOVE_BUYS for turn buys decrement', () => { - expect(victoryFieldToGameLogAction('turn', 'buys', -1)).toBe( + expect(fieldSubfieldToGameLogAction('turn', 'buys', -1)).toBe( GameLogActionWithCount.REMOVE_BUYS ); }); it('should return ADD_COINS for turn coins increment', () => { - expect(victoryFieldToGameLogAction('turn', 'coins', 1)).toBe(GameLogActionWithCount.ADD_COINS); + expect(fieldSubfieldToGameLogAction('turn', 'coins', 1)).toBe(GameLogActionWithCount.ADD_COINS); }); it('should return REMOVE_COINS for turn coins decrement', () => { - expect(victoryFieldToGameLogAction('turn', 'coins', -1)).toBe( + expect(fieldSubfieldToGameLogAction('turn', 'coins', -1)).toBe( GameLogActionWithCount.REMOVE_COINS ); }); it('should return ADD_COFFERS for mats coffers increment', () => { - expect(victoryFieldToGameLogAction('mats', 'coffers', 1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'coffers', 1)).toBe( GameLogActionWithCount.ADD_COFFERS ); }); it('should return REMOVE_COFFERS for mats coffers decrement', () => { - expect(victoryFieldToGameLogAction('mats', 'coffers', -1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'coffers', -1)).toBe( GameLogActionWithCount.REMOVE_COFFERS ); }); it('should return ADD_VILLAGERS for mats villagers increment', () => { - expect(victoryFieldToGameLogAction('mats', 'villagers', 1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'villagers', 1)).toBe( GameLogActionWithCount.ADD_VILLAGERS ); }); it('should return REMOVE_VILLAGERS for mats villagers decrement', () => { - expect(victoryFieldToGameLogAction('mats', 'villagers', -1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'villagers', -1)).toBe( GameLogActionWithCount.REMOVE_VILLAGERS ); }); it('should return ADD_DEBT for mats debt increment', () => { - expect(victoryFieldToGameLogAction('mats', 'debt', 1)).toBe(GameLogActionWithCount.ADD_DEBT); + expect(fieldSubfieldToGameLogAction('mats', 'debt', 1)).toBe(GameLogActionWithCount.ADD_DEBT); }); it('should return REMOVE_DEBT for mats debt decrement', () => { - expect(victoryFieldToGameLogAction('mats', 'debt', -1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'debt', -1)).toBe( GameLogActionWithCount.REMOVE_DEBT ); }); it('should return ADD_FAVORS for mats favors increment', () => { - expect(victoryFieldToGameLogAction('mats', 'favors', 1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'favors', 1)).toBe( GameLogActionWithCount.ADD_FAVORS ); }); it('should return REMOVE_FAVORS for mats favors decrement', () => { - expect(victoryFieldToGameLogAction('mats', 'favors', -1)).toBe( + expect(fieldSubfieldToGameLogAction('mats', 'favors', -1)).toBe( GameLogActionWithCount.REMOVE_FAVORS ); }); it('should fail for mats invalid field', () => { expect(() => - victoryFieldToGameLogAction('mats', 'invalid' as unknown as PlayerSubField<'mats'>, 1) + fieldSubfieldToGameLogAction('mats', 'invalid' as unknown as PlayerSubField<'mats'>, 1) ).toThrow(InvalidFieldError); }); it('should return ADD_CURSES for victory curses increment', () => { - expect(victoryFieldToGameLogAction('victory', 'curses', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'curses', 1)).toBe( GameLogActionWithCount.ADD_CURSES ); }); it('should return REMOVE_CURSES for victory curses decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'curses', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'curses', -1)).toBe( GameLogActionWithCount.REMOVE_CURSES ); }); it('should return ADD_ESTATES for victory estates increment', () => { - expect(victoryFieldToGameLogAction('victory', 'estates', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'estates', 1)).toBe( GameLogActionWithCount.ADD_ESTATES ); }); it('should return REMOVE_ESTATES for victory estates decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'estates', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'estates', -1)).toBe( GameLogActionWithCount.REMOVE_ESTATES ); }); it('should return ADD_DUCHIES for victory duchies increment', () => { - expect(victoryFieldToGameLogAction('victory', 'duchies', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'duchies', 1)).toBe( GameLogActionWithCount.ADD_DUCHIES ); }); it('should return REMOVE_DUCHIES for victory duchies decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'duchies', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'duchies', -1)).toBe( GameLogActionWithCount.REMOVE_DUCHIES ); }); it('should return ADD_PROVINCES for victory provinces increment', () => { - expect(victoryFieldToGameLogAction('victory', 'provinces', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'provinces', 1)).toBe( GameLogActionWithCount.ADD_PROVINCES ); }); it('should return REMOVE_PROVINCES for victory provinces decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'provinces', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'provinces', -1)).toBe( GameLogActionWithCount.REMOVE_PROVINCES ); }); it('should return ADD_COLONIES for victory colonies increment', () => { - expect(victoryFieldToGameLogAction('victory', 'colonies', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'colonies', 1)).toBe( GameLogActionWithCount.ADD_COLONIES ); }); it('should return REMOVE_COLONIES for victory colonies decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'colonies', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'colonies', -1)).toBe( GameLogActionWithCount.REMOVE_COLONIES ); }); it('should return ADD_VP_TOKENS for victory tokens increment', () => { - expect(victoryFieldToGameLogAction('victory', 'tokens', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'tokens', 1)).toBe( GameLogActionWithCount.ADD_VP_TOKENS ); }); it('should return REMOVE_VP_TOKENS for victory tokens decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'tokens', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'tokens', -1)).toBe( GameLogActionWithCount.REMOVE_VP_TOKENS ); }); it('should return ADD_OTHER_VP for victory other increment', () => { - expect(victoryFieldToGameLogAction('victory', 'other', 1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'other', 1)).toBe( GameLogActionWithCount.ADD_OTHER_VP ); }); it('should return REMOVE_OTHER_VP for victory other decrement', () => { - expect(victoryFieldToGameLogAction('victory', 'other', -1)).toBe( + expect(fieldSubfieldToGameLogAction('victory', 'other', -1)).toBe( GameLogActionWithCount.REMOVE_OTHER_VP ); }); it('should fail for victory invalid field', () => { expect(() => - victoryFieldToGameLogAction('victory', 'invalid' as unknown as PlayerSubField<'victory'>, 1) + fieldSubfieldToGameLogAction('victory', 'invalid' as unknown as PlayerSubField<'victory'>, 1) ).toThrow(InvalidFieldError); }); it('should throw InvalidFieldError for invalid field', () => { - expect(() => victoryFieldToGameLogAction('invalidField' as any, 'actions', 1)).toThrow( + expect(() => fieldSubfieldToGameLogAction('invalidField' as any, 'actions', 1)).toThrow( InvalidFieldError ); }); it('should throw InvalidFieldError for invalid subfield', () => { - expect(() => victoryFieldToGameLogAction('turn', 'invalidSubfield' as any, 1)).toThrow( + expect(() => fieldSubfieldToGameLogAction('turn', 'invalidSubfield' as any, 1)).toThrow( InvalidFieldError ); }); it('should return ADD_ACTIONS for newTurn actions increment', () => { - expect(victoryFieldToGameLogAction('newTurn', 'actions', 1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'actions', 1)).toBe( GameLogActionWithCount.ADD_NEXT_TURN_ACTIONS ); }); it('should return REMOVE_ACTIONS for newTurn actions decrement', () => { - expect(victoryFieldToGameLogAction('newTurn', 'actions', -1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'actions', -1)).toBe( GameLogActionWithCount.REMOVE_NEXT_TURN_ACTIONS ); }); it('should return ADD_BUYS for newTurn buys increment', () => { - expect(victoryFieldToGameLogAction('newTurn', 'buys', 1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'buys', 1)).toBe( GameLogActionWithCount.ADD_NEXT_TURN_BUYS ); }); it('should return REMOVE_BUYS for newTurn buys decrement', () => { - expect(victoryFieldToGameLogAction('newTurn', 'buys', -1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'buys', -1)).toBe( GameLogActionWithCount.REMOVE_NEXT_TURN_BUYS ); }); it('should return ADD_COINS for newTurn coins increment', () => { - expect(victoryFieldToGameLogAction('newTurn', 'coins', 1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'coins', 1)).toBe( GameLogActionWithCount.ADD_NEXT_TURN_COINS ); }); it('should return REMOVE_COINS for newTurn coins decrement', () => { - expect(victoryFieldToGameLogAction('newTurn', 'coins', -1)).toBe( + expect(fieldSubfieldToGameLogAction('newTurn', 'coins', -1)).toBe( GameLogActionWithCount.REMOVE_NEXT_TURN_COINS ); }); @@ -229,7 +229,7 @@ describe('victoryFieldToGameLogAction', () => { // Test for invalid subfield it('should throw InvalidFieldError for invalid subfield in newTurn', () => { expect(() => - victoryFieldToGameLogAction('newTurn', 'invalidSubfield' as PlayerSubField<'newTurn'>, 1) + fieldSubfieldToGameLogAction('newTurn', 'invalidSubfield' as PlayerSubField<'newTurn'>, 1) ).toThrow(InvalidFieldError); }); }); diff --git a/src/game/__tests__/dominion-lib-load-save-loadGame.spec.ts b/src/game/__tests__/dominion-lib-load-save-loadGame.spec.ts index 3a12043..a9ab78f 100644 --- a/src/game/__tests__/dominion-lib-load-save-loadGame.spec.ts +++ b/src/game/__tests__/dominion-lib-load-save-loadGame.spec.ts @@ -67,6 +67,7 @@ describe('loadGame', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, }, ], }); @@ -90,6 +91,7 @@ describe('loadGame', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, }, { id: saveGameLogId, @@ -97,6 +99,7 @@ describe('loadGame', () => { timestamp: new Date(), playerIndex: NO_PLAYER, currentPlayerIndex: 0, + turn: 1, }, ], }); @@ -113,6 +116,7 @@ describe('loadGame', () => { timestamp: expect.any(Date), playerIndex: NO_PLAYER, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.LOAD_GAME, linkedActionId: saveGameLogId, }, diff --git a/src/game/__tests__/dominion-lib-load-save-restoreSavedGame.spec.ts b/src/game/__tests__/dominion-lib-load-save-restoreSavedGame.spec.ts index 01551cf..3738c88 100644 --- a/src/game/__tests__/dominion-lib-load-save-restoreSavedGame.spec.ts +++ b/src/game/__tests__/dominion-lib-load-save-restoreSavedGame.spec.ts @@ -27,6 +27,7 @@ describe('restoreSavedGame', () => { timestamp: saveGameTime.toISOString(), playerIndex: NO_PLAYER, currentPlayerIndex: 0, + turn: 1, }; // Assuming `createMockGameRaw` returns an IGameRaw object with string timestamps diff --git a/src/game/__tests__/dominion-lib-log-addLogEntry.spec.ts b/src/game/__tests__/dominion-lib-log-addLogEntry.spec.ts index 322ae88..6c14975 100644 --- a/src/game/__tests__/dominion-lib-log-addLogEntry.spec.ts +++ b/src/game/__tests__/dominion-lib-log-addLogEntry.spec.ts @@ -11,7 +11,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with minimal fields', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.START_GAME); + addLogEntry(mockGame, 0, GameLogActionWithCount.START_GAME); expect(mockGame.log).toContainEqual( expect.objectContaining({ playerIndex: 0, @@ -21,7 +21,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with all fields', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, { + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, { count: 5, correction: false, linkedActionId: 'linkedActionId', @@ -40,7 +40,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with only some fields overridden', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, { count: 5 }); + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, { count: 5 }); expect(mockGame.log).toContainEqual( expect.objectContaining({ playerIndex: 0, @@ -52,12 +52,12 @@ describe('addLogEntry', () => { it('should handle invalid player index gracefully', () => { expect(() => { - addLogEntry(mockGame, 99, 0, GameLogActionWithCount.ADD_COINS, { count: 5 }); + addLogEntry(mockGame, 99, GameLogActionWithCount.ADD_COINS, { count: 5 }); }).toThrow('Player index is out of range'); }); it('should add a log entry with a correction flag', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, { + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, { count: 5, correction: true, }); @@ -73,7 +73,7 @@ describe('addLogEntry', () => { it('should add a log entry with player turn details', () => { const playerTurnDetails = [{ playerIndex: 0, actions: 1, buys: 1, coins: 1 }]; - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, { + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, { count: 5, playerTurnDetails, }); @@ -90,24 +90,24 @@ describe('addLogEntry', () => { // New edge cases it('should throw an error when player index is required but not provided', () => { expect(() => { - addLogEntry(mockGame, -1, 0, GameLogActionWithCount.ADD_COINS, { count: 5 }); + addLogEntry(mockGame, -1, GameLogActionWithCount.ADD_COINS, { count: 5 }); }).toThrow('Player index is required for this action'); }); it('should throw an error when player index is out of range', () => { expect(() => { - addLogEntry(mockGame, 99, 0, GameLogActionWithCount.ADD_COINS, { count: 5 }); + addLogEntry(mockGame, 99, GameLogActionWithCount.ADD_COINS, { count: 5 }); }).toThrow('Player index is out of range'); }); it('should throw an error when player index is provided for an action that does not require it', () => { expect(() => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.END_GAME, { count: 5 }); + addLogEntry(mockGame, 0, GameLogActionWithCount.END_GAME, { count: 5 }); }).toThrow('Player index is not relevant for this action'); }); it('should add a log entry with a valid NoPlayerActions action and playerIndex set to -1', () => { - addLogEntry(mockGame, -1, 0, GameLogActionWithCount.END_GAME); + addLogEntry(mockGame, -1, GameLogActionWithCount.END_GAME); expect(mockGame.log).toContainEqual( expect.objectContaining({ playerIndex: -1, @@ -118,18 +118,18 @@ describe('addLogEntry', () => { it('should throw an error when player index is provided for a NoPlayerActions action', () => { expect(() => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.END_GAME); + addLogEntry(mockGame, 0, GameLogActionWithCount.END_GAME); }).toThrow('Player index is not relevant for this action'); }); it('should throw an error when player index is out of range for a NoPlayerActions action', () => { expect(() => { - addLogEntry(mockGame, 99, 0, GameLogActionWithCount.END_GAME); + addLogEntry(mockGame, 99, GameLogActionWithCount.END_GAME); }).toThrow('Player index is not relevant for this action'); }); it('should add a log entry with a valid NoPlayerActions action and playerIndex set to -1 with overrides', () => { - addLogEntry(mockGame, -1, 0, GameLogActionWithCount.END_GAME, { + addLogEntry(mockGame, -1, GameLogActionWithCount.END_GAME, { correction: true, }); expect(mockGame.log).toContainEqual( @@ -141,7 +141,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with a valid player action and playerIndex set to 0 with overrides', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, { + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, { count: 5, correction: true, }); @@ -156,7 +156,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with a valid player action and playerIndex set to 0 without overrides', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS); + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS); expect(mockGame.log).toContainEqual( expect.objectContaining({ playerIndex: 0, @@ -166,7 +166,7 @@ describe('addLogEntry', () => { }); it('should add a log entry with a valid player action and playerIndex set to 0 with undefined overrides', () => { - addLogEntry(mockGame, 0, 0, GameLogActionWithCount.ADD_COINS, undefined); + addLogEntry(mockGame, 0, GameLogActionWithCount.ADD_COINS, undefined); expect(mockGame.log).toContainEqual( expect.objectContaining({ playerIndex: 0, diff --git a/src/game/__tests__/dominion-lib-log-getStartDateFromLog.spec.ts b/src/game/__tests__/dominion-lib-log-getStartDateFromLog.spec.ts index baf3b3d..6c3ee79 100644 --- a/src/game/__tests__/dominion-lib-log-getStartDateFromLog.spec.ts +++ b/src/game/__tests__/dominion-lib-log-getStartDateFromLog.spec.ts @@ -13,6 +13,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_ACTIONS, count: 1, }, @@ -26,6 +27,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-01T00:00:00Z'), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.START_GAME, }, ]; @@ -40,6 +42,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-02T00:00:00Z'), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.START_GAME, }, { @@ -47,6 +50,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-02T01:00:00Z'), playerIndex: 1, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, }, ]; @@ -61,6 +65,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-03T00:00:00Z'), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.START_GAME, }, { @@ -68,6 +73,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-03T01:00:00Z'), playerIndex: 1, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, }, { @@ -75,6 +81,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-03T02:00:00Z'), playerIndex: 2, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.REMOVE_COINS, }, ]; @@ -89,6 +96,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-01T00:00:00Z'), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.START_GAME, }, { @@ -96,6 +104,7 @@ describe('getStartDateFromLog', () => { timestamp: new Date('2023-01-01T00:00:00Z'), playerIndex: 1, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, }, ]; diff --git a/src/game/__tests__/dominion-lib-log-getTimeSpanFromStartGame.spec.ts b/src/game/__tests__/dominion-lib-log-getTimeSpanFromStartGame.spec.ts index 16e0624..753c085 100644 --- a/src/game/__tests__/dominion-lib-log-getTimeSpanFromStartGame.spec.ts +++ b/src/game/__tests__/dominion-lib-log-getTimeSpanFromStartGame.spec.ts @@ -9,6 +9,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -24,6 +25,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -39,6 +41,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T01:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -54,6 +57,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -69,6 +73,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -84,6 +89,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -99,6 +105,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -114,6 +121,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -121,6 +129,7 @@ describe('getTimeSpanFromStartGame', () => { id: '2', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T01:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, @@ -128,6 +137,7 @@ describe('getTimeSpanFromStartGame', () => { id: '3', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T02:00:00Z'), action: GameLogActionWithCount.LOAD_GAME, }, @@ -143,6 +153,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -150,6 +161,7 @@ describe('getTimeSpanFromStartGame', () => { id: '2', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T01:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, @@ -157,6 +169,7 @@ describe('getTimeSpanFromStartGame', () => { id: '3', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T02:00:00Z'), action: GameLogActionWithCount.LOAD_GAME, }, @@ -164,6 +177,7 @@ describe('getTimeSpanFromStartGame', () => { id: '4', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T03:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, @@ -171,6 +185,7 @@ describe('getTimeSpanFromStartGame', () => { id: '5', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T04:00:00Z'), action: GameLogActionWithCount.LOAD_GAME, }, @@ -186,6 +201,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -193,6 +209,7 @@ describe('getTimeSpanFromStartGame', () => { id: '2', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T01:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, @@ -200,6 +217,7 @@ describe('getTimeSpanFromStartGame', () => { id: '3', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T02:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, @@ -207,6 +225,7 @@ describe('getTimeSpanFromStartGame', () => { id: '4', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T03:00:00Z'), action: GameLogActionWithCount.LOAD_GAME, }, @@ -222,6 +241,7 @@ describe('getTimeSpanFromStartGame', () => { id: '1', playerIndex: 0, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T00:00:00Z'), action: GameLogActionWithCount.START_GAME, }, @@ -229,6 +249,7 @@ describe('getTimeSpanFromStartGame', () => { id: '2', playerIndex: -1, currentPlayerIndex: 0, + turn: 1, timestamp: new Date('2023-01-01T01:00:00Z'), action: GameLogActionWithCount.SAVE_GAME, }, diff --git a/src/game/__tests__/dominion-lib-log-logEntryToString.spec.ts b/src/game/__tests__/dominion-lib-log-logEntryToString.spec.ts index efe10b2..38f5638 100644 --- a/src/game/__tests__/dominion-lib-log-logEntryToString.spec.ts +++ b/src/game/__tests__/dominion-lib-log-logEntryToString.spec.ts @@ -10,6 +10,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 5, }; @@ -22,6 +23,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 5, }; @@ -34,6 +36,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, }; expect(logEntryToString(logEntry)).toBe('Added Coins'); @@ -45,6 +48,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, }; expect(logEntryToString(logEntry)).toBe('Added Coins'); @@ -56,6 +60,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 3, }; @@ -68,6 +73,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: undefined, }; @@ -80,6 +86,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 2, correction: true, @@ -93,6 +100,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 4, linkedActionId: 'some-linked-action-id', @@ -106,6 +114,7 @@ describe('logEntryToString', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, action: GameLogActionWithCount.ADD_COINS, count: 6, correction: true, diff --git a/src/game/__tests__/dominion-lib-undo-applyLogAction.spec.ts b/src/game/__tests__/dominion-lib-undo-applyLogAction.spec.ts index f6f8352..75006a0 100644 --- a/src/game/__tests__/dominion-lib-undo-applyLogAction.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-applyLogAction.spec.ts @@ -26,6 +26,7 @@ describe('applyLogAction', () => { playerTurnDetails: [{ ...DefaultTurnDetails }, { ...DefaultTurnDetails }], prevPlayerIndex: 0, currentPlayerIndex: 1, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -56,6 +57,7 @@ describe('applyLogAction', () => { timestamp: new Date(), prevPlayerIndex: 0, currentPlayerIndex: 1, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -73,6 +75,7 @@ describe('applyLogAction', () => { timestamp: new Date(), prevPlayerIndex: 1, currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -89,6 +92,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -105,6 +109,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -122,6 +127,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -139,6 +145,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -154,6 +161,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; expect(() => applyLogAction(mockGame, logEntry)).toThrow(NotEnoughSubfieldError); @@ -169,6 +177,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; expect(() => applyLogAction(mockGame, logEntry)).toThrow(NotEnoughProphecyError); @@ -183,6 +192,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -199,6 +209,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -213,6 +224,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const startingActions = mockGame.players[0].turn.actions; @@ -229,6 +241,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -244,6 +257,7 @@ describe('applyLogAction', () => { id: '1', timestamp: new Date(), currentPlayerIndex: 0, + turn: 1, }; const result = applyLogAction(mockGame, logEntry); @@ -262,6 +276,7 @@ describe('applyLogAction', () => { timestamp: new Date(), action: GameLogActionWithCount.SELECT_PLAYER, currentPlayerIndex: 2, + turn: 1, }; const updatedGame = applyLogAction(game, logEntry); @@ -279,6 +294,7 @@ describe('applyLogAction', () => { timestamp: new Date(), action: GameLogActionWithCount.SELECT_PLAYER, currentPlayerIndex: 0, + turn: 1, }; const updatedGame = applyLogAction(game, logEntry); @@ -297,6 +313,7 @@ describe('applyLogAction', () => { action: GameLogActionWithCount.ADD_ACTIONS, count: 1, currentPlayerIndex: 0, + turn: 1, }; const updatedGame = applyLogAction(game, logEntry); diff --git a/src/game/__tests__/dominion-lib-undo-canUndoAction.spec.ts b/src/game/__tests__/dominion-lib-undo-canUndoAction.spec.ts index ee083d4..ea52af3 100644 --- a/src/game/__tests__/dominion-lib-undo-canUndoAction.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-canUndoAction.spec.ts @@ -37,6 +37,7 @@ describe('canUndoAction', () => { count, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, playerTurnDetails: action === GameLogActionWithCount.NEXT_TURN ? [{ ...DefaultTurnDetails }, { ...DefaultTurnDetails }] diff --git a/src/game/__tests__/dominion-lib-undo-getLinkedActions.spec.ts b/src/game/__tests__/dominion-lib-undo-getLinkedActions.spec.ts index 63127d4..ce9ecc4 100644 --- a/src/game/__tests__/dominion-lib-undo-getLinkedActions.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-getLinkedActions.spec.ts @@ -13,6 +13,7 @@ describe('getLinkedActions', () => { action, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 1, linkedActionId, }); diff --git a/src/game/__tests__/dominion-lib-undo-reconstructGameState.spec.ts b/src/game/__tests__/dominion-lib-undo-reconstructGameState.spec.ts index 598c249..6a1e91c 100644 --- a/src/game/__tests__/dominion-lib-undo-reconstructGameState.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-reconstructGameState.spec.ts @@ -40,6 +40,7 @@ describe('reconstructGameState', () => { playerIndex: 0, count: 1, currentPlayerIndex: 0, + turn: 1, }, ], }; @@ -59,6 +60,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_COINS, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 2, }, { @@ -67,6 +69,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_ACTIONS, playerIndex: 1, currentPlayerIndex: 0, + turn: 1, count: 1, }, ], @@ -89,6 +92,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.NEXT_TURN, playerIndex: nextPlayerIndex, currentPlayerIndex: baseGame.currentPlayerIndex, + turn: 2, playerTurnDetails: [{ ...DefaultTurnDetails }, { ...DefaultTurnDetails }], prevPlayerIndex: baseGame.currentPlayerIndex, } as ILogEntry, @@ -113,6 +117,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_COINS, playerIndex: baseGame.currentPlayerIndex, currentPlayerIndex: baseGame.currentPlayerIndex, + turn: 1, count: 3, }, { @@ -126,6 +131,7 @@ describe('reconstructGameState', () => { ], prevPlayerIndex: baseGame.currentPlayerIndex, currentPlayerIndex: baseGame.currentPlayerIndex, + turn: 1, }, ], }; @@ -165,6 +171,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.REMOVE_PROPHECY, playerIndex: NO_PLAYER, currentPlayerIndex: 0, + turn: 1, count: 2, }, ], @@ -185,6 +192,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.REMOVE_COINS, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 5, // This would result in negative coins }, ], @@ -217,6 +225,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.REMOVE_PROPHECY, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 10, // This would result in negative prophecy }, ], @@ -237,6 +246,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_COINS, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 2, }, { @@ -245,6 +255,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_ACTIONS, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 1, linkedActionId: mainActionId, }, @@ -268,6 +279,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_COINS, playerIndex: 1, currentPlayerIndex: 1, + turn: 1, count: 3, }, { @@ -278,6 +290,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.NEXT_TURN, playerIndex: 0, currentPlayerIndex: 0, + turn: 2, playerTurnDetails: [{ ...DefaultTurnDetails, coins: 3 }, { ...DefaultTurnDetails }], prevPlayerIndex: 1, }, @@ -288,6 +301,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_ACTIONS, playerIndex: 1, currentPlayerIndex: 0, + turn: 2, count: 2, }, { @@ -298,6 +312,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.NEXT_TURN, playerIndex: 1, currentPlayerIndex: 1, + turn: 3, playerTurnDetails: [{ ...DefaultTurnDetails }, { ...DefaultTurnDetails, actions: 3 }], prevPlayerIndex: 0, }, @@ -308,6 +323,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_BUYS, playerIndex: 0, currentPlayerIndex: 1, + turn: 3, count: 1, }, { @@ -317,6 +333,7 @@ describe('reconstructGameState', () => { action: GameLogActionWithCount.ADD_COINS, playerIndex: 1, currentPlayerIndex: 1, + turn: 3, count: 3, }, ], diff --git a/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts b/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts index eccbc73..a6f473e 100644 --- a/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts @@ -21,6 +21,7 @@ describe('removeTargetAndLinkedActions', () => { timestamp: new Date(), playerIndex: 0, currentPlayerIndex: 0, + turn: 1, count: 1, linkedActionId, }); diff --git a/src/game/__tests__/dominion-lib-undoAction.spec.ts b/src/game/__tests__/dominion-lib-undoAction.spec.ts index beec0b2..3de9855 100644 --- a/src/game/__tests__/dominion-lib-undoAction.spec.ts +++ b/src/game/__tests__/dominion-lib-undoAction.spec.ts @@ -26,6 +26,7 @@ describe('undoAction', () => { count: 1, playerIndex: 0, currentPlayerIndex: 0, + turn: 1, playerTurnDetails: action === GameLogActionWithCount.NEXT_TURN ? [{ ...DefaultTurnDetails }, { ...DefaultTurnDetails }] diff --git a/src/game/dominion-lib-load-save.ts b/src/game/dominion-lib-load-save.ts index a7ebff7..b2de473 100644 --- a/src/game/dominion-lib-load-save.ts +++ b/src/game/dominion-lib-load-save.ts @@ -50,7 +50,7 @@ export function saveGame( existingId?: string ): boolean { // add the SAVE_GAME log entry - addLogEntry(game, NO_PLAYER, game.currentPlayerIndex, GameLogActionWithCount.SAVE_GAME); + addLogEntry(game, NO_PLAYER, GameLogActionWithCount.SAVE_GAME); try { const saveId = saveGameData(game, storageService, existingId); addToSavedGamesList( @@ -253,15 +253,9 @@ export function loadGameAddLog(gameState: IGame): IGame { if (savedGameLog.action !== GameLogActionWithCount.SAVE_GAME) { throw new InvalidLogSaveGameError(); } - addLogEntry( - gameState, - NO_PLAYER, - gameState.currentPlayerIndex, - GameLogActionWithCount.LOAD_GAME, - { - linkedActionId: savedGameLog.id, - } - ); + addLogEntry(gameState, NO_PLAYER, GameLogActionWithCount.LOAD_GAME, { + linkedActionId: savedGameLog.id, + }); return gameState; } diff --git a/src/game/dominion-lib-log.ts b/src/game/dominion-lib-log.ts index 1a43173..3113d84 100644 --- a/src/game/dominion-lib-log.ts +++ b/src/game/dominion-lib-log.ts @@ -15,7 +15,7 @@ import { NoPlayerActions } from '@/game/constants'; * @param increment - The amount to increment the field by * @returns The game log action */ -export function victoryFieldToGameLogAction( +export function fieldSubfieldToGameLogAction( field: T, subfield: PlayerFieldMap[T], increment: number @@ -213,7 +213,6 @@ export function getTimeSpanFromStartGame(log: ILogEntry[], eventTime: Date): str export function addLogEntry( game: IGame, playerIndex: number, - currentPlayerIndex: number, action: GameLogActionWithCount, overrides?: Partial ): ILogEntry { @@ -231,7 +230,8 @@ export function addLogEntry( timestamp: new Date(), action, playerIndex, - currentPlayerIndex, + currentPlayerIndex: game.currentPlayerIndex, + turn: game.currentTurn, ...overrides, }; game.log.push(newLog); diff --git a/src/game/dominion-lib.ts b/src/game/dominion-lib.ts index c1201d8..fdcfc70 100644 --- a/src/game/dominion-lib.ts +++ b/src/game/dominion-lib.ts @@ -183,6 +183,7 @@ export const NewGameState = (gameStateWithOptions: IGame): IGame => { ...gameStateWithOptions, players: gameStateWithOptions.players.map((player, index) => ({ ...newPlayer(player.name, index), + color: gameStateWithOptions.players[index].color, })), supply: initialSupply, currentStep: CurrentStep.GameScreen, @@ -193,6 +194,7 @@ export const NewGameState = (gameStateWithOptions: IGame): IGame => { timestamp: new Date(), playerIndex: gameStateWithOptions.firstPlayerIndex, currentPlayerIndex: gameStateWithOptions.firstPlayerIndex, + turn: 1, action: GameLogActionWithCount.START_GAME, }, ], diff --git a/src/game/interfaces/log-entry-raw.ts b/src/game/interfaces/log-entry-raw.ts index 7413bc1..d7d82a8 100644 --- a/src/game/interfaces/log-entry-raw.ts +++ b/src/game/interfaces/log-entry-raw.ts @@ -21,6 +21,10 @@ export interface ILogEntryRaw { * Index of the player whose turn it is */ currentPlayerIndex: number; + /** + * Turn number at the time of this log entry + */ + turn: number; /** * Count of value added/removed */ diff --git a/src/game/interfaces/log-entry.ts b/src/game/interfaces/log-entry.ts index bf73354..b9ea30e 100644 --- a/src/game/interfaces/log-entry.ts +++ b/src/game/interfaces/log-entry.ts @@ -22,6 +22,10 @@ export interface ILogEntry { * Index of the player whose turn it is */ currentPlayerIndex: number; + /** + * Turn number at the time of this log entry + */ + turn: number; /** * Count of value added/removed */