Skip to content

Commit

Permalink
v0.7.0: Add Import, resolve #38 by creating a time cache
Browse files Browse the repository at this point in the history
  • Loading branch information
JessicaMulein committed Oct 30, 2024
1 parent 1f30757 commit 830ee13
Show file tree
Hide file tree
Showing 31 changed files with 1,683 additions and 340 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ Join our community of developers.
## Changelog
### Mon Oct 28 20:00:00 2024
- Version 0.7.0
- Add import functionality
- Add caching of game time calculations so that game log is rendered faster on a big game
### Sun Oct 27 16:13:00 2024
- Version 0.6.1
Expand Down
67 changes: 59 additions & 8 deletions src/__fixtures__/dominion-lib-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import { IExpansionsEnabled } from '@/game/interfaces/expansions-enabled';
import { CurrentStep } from '@/game/enumerations/current-step';
import { ILogEntry } from '@/game/interfaces/log-entry';
import { deepClone } from '@/game/utils';
import { IVictoryDetails } from '@/game/interfaces/victory-details';
import { IGameRaw } from '@/game/interfaces/game-raw';
import { ILogEntryRaw } from '@/game/interfaces/log-entry-raw';
import { IEventTimeCacheRaw } from '@/game/interfaces/event-time-cache-raw';

export function createMockGame(playerCount: number, overrides?: Partial<IGame>): IGame {
const options: IGameOptions = {
Expand All @@ -32,7 +34,7 @@ export function createMockGame(playerCount: number, overrides?: Partial<IGame>):
const game: IGame = {
players: Array(playerCount)
.fill(null)
.map((value, index) => createMockPlayer(undefined, index)),
.map((value, index) => createMockPlayer(index, overrides?.players?.[index])),
supply,
options,
risingSun: {
Expand All @@ -55,14 +57,15 @@ export function createMockGame(playerCount: number, overrides?: Partial<IGame>):
action: GameLogAction.START_GAME,
},
],
timeCache: [],
currentStep: CurrentStep.GameScreen,
setsRequired: 1,
...overrides,
...(overrides ? deepClone<Partial<IGame>>(overrides) : {}),
};
return distributeInitialSupply(game);
}

export function createMockPlayer(victory?: Partial<IPlayer['victory']>, index?: number): IPlayer {
export function createMockPlayer(index?: number, overrides?: Partial<IPlayer>): IPlayer {
return {
name: faker.person.firstName(),
color:
Expand All @@ -72,10 +75,8 @@ export function createMockPlayer(victory?: Partial<IPlayer['victory']>, index?:
mats: EmptyMatDetails(),
turn: DefaultTurnDetails(),
newTurn: DefaultTurnDetails(),
victory: {
...EmptyVictoryDetails(),
...(victory ? deepClone<Partial<IVictoryDetails>>(victory) : {}),
},
victory: EmptyVictoryDetails(),
...deepClone<Partial<IPlayer>>(overrides ?? {}),
} as IPlayer;
}

Expand All @@ -94,3 +95,53 @@ export function createMockLog(log?: Partial<ILogEntry>): ILogEntry {
...(log ? deepClone<Partial<ILogEntry>>(log) : {}),
};
}

export function createMockGameRaw(numPlayers: number, overrides?: Partial<IGameRaw>): IGameRaw {
// First, create a mock game using createMockGame
const mockGame: IGame = createMockGame(numPlayers);

// Convert the IGame to IGameRaw
const baseGameRaw: IGameRaw = {
...mockGame,
log: mockGame.log.map((logEntry) => ({
...logEntry,
timestamp: logEntry.timestamp.toISOString(),
})),
timeCache: mockGame.timeCache.map((timeCache) => ({
...timeCache,
saveStartTime: timeCache.saveStartTime ? timeCache.saveStartTime.toISOString() : null,
pauseStartTime: timeCache.pauseStartTime ? timeCache.pauseStartTime.toISOString() : null,
})),
};

// Apply overrides if provided
if (overrides) {
// Merge overrides with baseGameRaw
Object.keys(overrides).forEach((key) => {
if (key === 'log' && Array.isArray(overrides.log)) {
baseGameRaw.log = overrides.log.map((logEntry) => ({
...logEntry,
timestamp:
typeof logEntry.timestamp === 'string'
? logEntry.timestamp
: new Date(logEntry.timestamp).toISOString(),
}));
} else if (key === 'timeCache' && Array.isArray(overrides.timeCache)) {
baseGameRaw.timeCache = overrides.timeCache.map((timeCache) => ({
...timeCache,
saveStartTime: timeCache.saveStartTime
? new Date(timeCache.saveStartTime).toISOString()
: null,
pauseStartTime: timeCache.pauseStartTime
? new Date(timeCache.pauseStartTime).toISOString()
: null,
}));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(baseGameRaw as any)[key] = (overrides as any)[key];
}
});
}

return baseGameRaw;
}
2 changes: 1 addition & 1 deletion src/components/DominionAssistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { useGameContext } from '@/components/GameContext';
import { CurrentStep } from '@/game/enumerations/current-step';
import { NO_PLAYER, StepTransitions } from '@/game/constants';
import {
EmptyGameState,
getNextPlayerIndex,
incrementTurnCountersAndPlayerIndices,
resetPlayerTurnCounters,
Expand All @@ -20,6 +19,7 @@ import { useAlert } from '@/components/AlertContext';
import { Location, NavigateFunction } from 'react-router-dom';
import { IGame } from '@/game/interfaces/game';
import { deepClone } from '@/game/utils';
import { EmptyGameState } from '@/game/constants';

interface DominionAssistantProps {
route: Location;
Expand Down
3 changes: 1 addition & 2 deletions src/components/GameContext.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { EmptyGameState } from '@/game/dominion-lib';
import { saveGame } from '@/game/dominion-lib-load-save';
import { IGame } from '@/game/interfaces/game';
import React, { createContext, useContext, useState, ReactNode, useMemo, useEffect } from 'react';
import { LocalStorageService } from '@/game/local-storage-service';
import { deepClone } from '@/game/utils';
import { AutoSaveGameSaveId, AutoSaveGameSaveName } from '@/game/constants';
import { AutoSaveGameSaveId, AutoSaveGameSaveName, EmptyGameState } from '@/game/constants';

// Define the shape of the context
interface GameContextProps {
Expand Down
1 change: 0 additions & 1 deletion src/components/GameLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const GameLog: React.FC = () => {
key={entry.id || index}
logIndex={index}
entry={entry}
isCurrentPlayer={index === gameState.currentPlayerIndex}
hasLinkedAction={hasLinkedAction}
/>
);
Expand Down
11 changes: 3 additions & 8 deletions src/components/GameLogEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,15 @@ import {
import { GameLogAction } from '@/game/enumerations/game-log-action';
import { AdjustmentActions } from '@/game/constants';
import ColoredPlayerName from '@/components/ColoredPlayerName';
import { getAdjustedDurationFromCache } from '@/game/dominion-lib-time';

interface GameLogEntryProps {
logIndex: number;
entry: ILogEntry;
isCurrentPlayer: boolean;
hasLinkedAction: boolean;
}

const GameLogEntry: React.FC<GameLogEntryProps> = ({
logIndex,
entry,
isCurrentPlayer,
hasLinkedAction,
}) => {
const GameLogEntry: React.FC<GameLogEntryProps> = ({ logIndex, entry, hasLinkedAction }) => {
const { gameState, setGameState } = useGameContext();
const [openUndoDialog, setOpenUndoDialog] = useState(false);

Expand Down Expand Up @@ -116,7 +111,7 @@ const GameLogEntry: React.FC<GameLogEntryProps> = ({
</TableCell>
<TableCell style={{ width: '15%' }}>
<Typography variant="caption">
{formatTimeSpan(calculateDurationUpToEvent(gameState.log, entry.timestamp))}
{formatTimeSpan(getAdjustedDurationFromCache(gameState, entry.id) ?? 0)}
</Typography>
</TableCell>
<TableCell style={{ width: '60%' }}>
Expand Down
Loading

0 comments on commit 830ee13

Please sign in to comment.