Fixes production error "Cannot read properties of undefined (reading 'carryBoxes')" that occurred when users tried to adjust difficulty settings. Root cause: displayRules was undefined for new users or users with old V1 config in database. Difficulty adjustment buttons accessed displayRules.carryBoxes without checking if displayRules existed first. Changes: - AdditionWorksheetClient: Initialize displayRules with defaults when missing - ConfigPanel: Use null-coalescing operators instead of non-null assertions - ConfigPanel: Add error logging when required fields are missing - NEW: WorksheetErrorBoundary component to catch all errors in worksheet page - page.tsx: Wrap client component with error boundary This ensures users see helpful error messages instead of blank pages, and never need to open the browser console to understand what went wrong. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
26 KiB
Game Migration Playbook
Version: 1.0 Last Updated: 2025-01-16 For: Migrating legacy arcade games to the Modular Game Platform
Overview
This playbook provides step-by-step instructions for migrating a legacy arcade game to the new modular game system. Follow these steps in order to ensure a smooth, low-risk migration.
⚠️ Important: Local mode is deprecated. All games must operate in room mode only (even solo play is a single-player room).
Estimated Time: 4-8 hours per game (varies by complexity)
Prerequisites
Before starting, ensure:
- You understand the Game SDK architecture (
/src/arcade-games/README.md) - You've read
ARCHITECTURAL_IMPROVEMENTS.md - The game is currently working in production
- You have a test plan for the game
Phase 1: Analysis (30-60 minutes)
Step 1.1: Document Current Architecture
Create a migration plan document (docs/[GAME_NAME]_MIGRATION_PLAN.md) with:
Current File Structure:
List all files the game currently uses:
- State types
- Move types
- Providers (local, room, unified?)
- Components
- Validator (if exists)
- Page routes
Current State Shape:
// Document the current state interface
interface CurrentGameState {
// List all fields
// Mark which are UI-only vs game logic
// Identify multiplayer fields
}
Current Move Types:
// Document all move types
type CurrentGameMove =
| { type: 'MOVE_1', ... }
| { type: 'MOVE_2', ... }
Current Config:
// Document game configuration
interface CurrentGameConfig {
// List all settings
}
Step 1.2: Identify Validator Status
Check if validator exists:
grep -r "YourGameValidator" src/lib/arcade/validation/
Status:
- ✅ Validator exists: Migration is easier (skip validator creation)
- ❌ No validator: Need to create one (add 2-3 hours)
Step 1.3: Assess Current Provider Pattern
Which pattern does the game use?
- Reducer Pattern: Has
reducer.ts, usesuseReducer, dispatches actions - ArcadeSession Pattern: Uses
useArcadeSession, sends moves - Hybrid: Uses both (uh oh)
How many providers?
- Single Provider: One provider (room mode only)
- Dual Providers: Separate
LocalProvider.tsxandRoomProvider.tsx(both will be replaced)
Note: All providers will be replaced with a single room-mode provider.
Step 1.4: Identify Complexity Factors
Rate each factor (Low/Medium/High):
- UI State Complexity: Animations, keyboard state, timeouts
- Timing Requirements: Card sequences, turn timers, synchronization
- Input Handling: Real-time input, debouncing, local optimization
- Multiplayer Scoring: Cooperative vs competitive modes, player attribution
- Configuration Persistence: Settings saved per room/game
Complexity Score:
- 0-2 High: Easy (2-4 hours)
- 3-4 High: Medium (4-6 hours)
- 5+ High: Hard (6-8+ hours)
Phase 2: Preparation (1 hour)
Step 2.1: Create New Directory Structure
mkdir -p src/arcade-games/[game-name]
mkdir -p src/arcade-games/[game-name]/components
Directory structure:
src/arcade-games/[game-name]/
├── index.ts # Game definition (will create)
├── Validator.ts # Move existing or create new
├── Provider.tsx # Unified provider (will create)
├── types.ts # Copy and update
├── game.yaml # Optional manifest
└── components/
├── GameComponent.tsx # Main wrapper (will create)
├── SetupPhase.tsx # Copy and update
├── PlayingPhase.tsx # Copy and update
└── ResultsPhase.tsx # Copy and update
Step 2.2: Copy Validator
If validator exists:
cp src/lib/arcade/validation/[Game]Validator.ts \
src/arcade-games/[game-name]/Validator.ts
Update imports in the validator:
- import type { SomeOldType } from '@/app/arcade/[game]'
+ import type { SomeNewType } from './types'
If validator doesn't exist: See Appendix A for validator creation guide.
Step 2.3: Copy Types
cp src/app/arcade/[game-name]/types.ts \
src/arcade-games/[game-name]/types.ts
Note: You'll update these types in Phase 3.
Step 2.4: Document Migration Checklist
In your migration plan, add:
## Migration Checklist
- [ ] Phase 1: Analysis complete
- [ ] Phase 2: Preparation complete
- [ ] Phase 3: Game definition created
- [ ] Phase 4: Types updated
- [ ] Phase 5: Provider created
- [ ] Phase 6: Components updated
- [ ] Phase 7: Page route updated
- [ ] Phase 8: Testing complete
- [ ] Phase 9: Cleanup done
Phase 3: Game Definition (1 hour)
Step 3.1: Create Manifest
Option A: YAML file (recommended):
# src/arcade-games/[game-name]/game.yaml
name: game-name
displayName: Game Display Name
icon: 🎮
description: Short one-line description
longDescription: |
Longer description explaining gameplay,
rules, and what makes the game fun.
maxPlayers: 8
difficulty: Beginner | Intermediate | Advanced
chips:
- 👥 Multiplayer
- ⚡ Fast-Paced
- 🧠 Mental Challenge
color: blue | purple | orange | green
gradient: linear-gradient(135deg, #color1, #color2)
borderColor: blue.200
available: true
Option B: Inline object:
const manifest: GameManifest = {
name: "game-name",
displayName: "Game Display Name",
// ... all fields from above
};
Step 3.2: Define Default Config
const defaultConfig: YourGameConfig = {
setting1: defaultValue1,
setting2: defaultValue2,
// ... all game settings with sensible defaults
};
Step 3.3: Create Config Validator
function validateYourGameConfig(config: unknown): config is YourGameConfig {
return (
typeof config === "object" &&
config !== null &&
// Check each field exists
"setting1" in config &&
"setting2" in config &&
// Validate each field's type and value
typeof (config as any).setting1 === "string" &&
typeof (config as any).setting2 === "number" &&
// Validate constraints
(config as any).setting2 >= 1 &&
(config as any).setting2 <= 100
);
}
Step 3.4: Create Game Definition
// src/arcade-games/[game-name]/index.ts
import { defineGame } from "@/lib/arcade/game-sdk";
import type { GameManifest } from "@/lib/arcade/game-sdk";
import { GameComponent } from "./components/GameComponent";
import { YourGameProvider } from "./Provider";
import type { YourGameConfig, YourGameMove, YourGameState } from "./types";
import { yourGameValidator } from "./Validator";
const manifest: GameManifest = {
// ... manifest from Step 3.1
};
const defaultConfig: YourGameConfig = {
// ... config from Step 3.2
};
function validateYourGameConfig(config: unknown): config is YourGameConfig {
// ... validator from Step 3.3
}
export const yourGame = defineGame<YourGameConfig, YourGameState, YourGameMove>(
{
manifest,
Provider: YourGameProvider,
GameComponent,
validator: yourGameValidator,
defaultConfig,
validateConfig: validateYourGameConfig,
},
);
Step 3.5: Register Game
In src/lib/arcade/game-registry.ts:
import { yourGame } from "@/arcade-games/[game-name]";
registerGame(yourGame);
In src/lib/arcade/validators.ts:
import { yourGameValidator } from "@/arcade-games/[game-name]/Validator";
export const validatorRegistry = {
// ... other games
"game-name": yourGameValidator,
} as const;
Step 3.6: Add Type Inference
In src/lib/arcade/game-configs.ts:
// Add type-only import
import type { yourGame } from "@/arcade-games/[game-name]";
// Infer config type
export type YourGameConfig = InferGameConfig<typeof yourGame>;
// Add to GameConfigByName
export type GameConfigByName = {
// ... other games
"game-name": YourGameConfig;
};
// Add default config constant
export const DEFAULT_YOUR_GAME_CONFIG: YourGameConfig = {
setting1: defaultValue1,
setting2: defaultValue2,
};
Phase 4: Update Types (30 minutes)
Step 4.1: Update State Interface
// src/arcade-games/[game-name]/types.ts
import type { GameConfig, GameState, GameMove } from "@/lib/arcade/game-sdk";
export interface YourGameConfig extends GameConfig {
setting1: string;
setting2: number;
// ... all game settings
}
export interface YourGameState extends GameState {
// Game-specific fields
gamePhase: "setup" | "playing" | "results";
// Multiplayer fields (from GameState)
activePlayers: string[];
playerMetadata: Record<string, PlayerMetadata>;
// Your game's custom fields
score: Record<string, number>;
// ...
}
Step 4.2: Update Move Types
Ensure all moves have required fields:
export type YourGameMove =
| {
type: "START_GAME";
playerId: string; // Required
userId: string; // Required
timestamp: number; // Added by SDK
data: {
activePlayers: string[];
playerMetadata: Record<string, any>;
};
}
| {
type: "MAKE_MOVE";
playerId: string;
userId: string;
timestamp: number;
data: {
// Move-specific data
};
};
Key Requirements:
- ✅ Every move must have:
playerId,userId,timestamp,data - ✅ Use
TEAM_MOVEconstant for moves where specific player doesn't matter - ✅
datais always an object (never primitive)
Step 4.3: Export Types
export type { PlayerMetadata } from "@/lib/arcade/player-ownership.client";
// ... any other needed types
Phase 5: Create Provider (2 hours)
Step 5.1: Provider Template (Room Mode Only)
// src/arcade-games/[game-name]/Provider.tsx
'use client'
import { createContext, useCallback, useContext, useMemo } from 'react'
import {
useArcadeSession,
useGameMode,
useRoomData,
useViewerId,
useUpdateGameConfig,
buildPlayerMetadata,
TEAM_MOVE,
} from '@/lib/arcade/game-sdk'
import type { YourGameState, YourGameMove } from './types'
// Context interface
interface YourGameContextValue {
state: YourGameState
lastError: string | null
clearError: () => void
exitSession: () => void
// Game-specific actions
startGame: () => void
makeMove: (data: any) => void
// ... all your game's actions
}
const YourGameContext = createContext<YourGameContextValue | null>(null)
export function useYourGame() {
const context = useContext(YourGameContext)
if (!context) throw new Error('useYourGame must be used within YourGameProvider')
return context
}
export function YourGameProvider({ children }: { children: React.ReactNode }) {
const { data: viewerId } = useViewerId()
const { roomData } = useRoomData()
const { activePlayers: activePlayerIds, players } = useGameMode()
const { mutate: updateGameConfig } = useUpdateGameConfig()
const activePlayers = Array.from(activePlayerIds)
// Merge saved config from room
const initialState = useMemo(() => {
const gameConfig = roomData?.gameConfig?.['game-name']
return {
// Default state
gamePhase: 'setup' as const,
activePlayers: [],
playerMetadata: {},
score: {},
// ... your default state
// Override with saved config from room
setting1: gameConfig?.setting1 ?? defaultValue1,
setting2: gameConfig?.setting2 ?? defaultValue2,
}
}, [roomData])
const { state, sendMove, exitSession, lastError, clearError } =
useArcadeSession<YourGameState>({
userId: viewerId || '',
roomId: roomData?.id, // Always provided (room mode only)
initialState,
applyMove: (state) => state, // Server handles all state updates
})
// Action: Start Game
const startGame = useCallback(() => {
const playerMetadata = buildPlayerMetadata(activePlayers, {}, players, viewerId)
sendMove({
type: 'START_GAME',
playerId: activePlayers[0], // Or TEAM_MOVE
userId: viewerId || '',
data: { activePlayers, playerMetadata },
})
}, [activePlayers, players, viewerId, sendMove])
// Action: Make Move
const makeMove = useCallback((data: any) => {
sendMove({
type: 'MAKE_MOVE',
playerId: state.currentPlayer, // Or TEAM_MOVE
userId: viewerId || '',
data,
})
}, [state.currentPlayer, viewerId, sendMove])
// ... more action creators
return (
<YourGameContext.Provider value={{
state,
lastError,
clearError,
exitSession,
startGame,
makeMove,
// ... all actions
}}>
{children}
</YourGameContext.Provider>
)
}
Step 5.2: Handle Config Persistence
For any setting that should persist:
const setConfig = useCallback(
(field: keyof YourGameConfig, value: any) => {
// Send move to update state immediately
sendMove({
type: "SET_CONFIG",
playerId: TEAM_MOVE,
userId: viewerId || "",
data: { field, value },
});
// Persist to database (always - room mode only)
if (roomData?.id) {
const currentGameConfig =
(roomData.gameConfig as Record<string, any>) || {};
const currentConfig =
(currentGameConfig["game-name"] as Record<string, any>) || {};
updateGameConfig({
roomId: roomData.id,
gameConfig: {
...currentGameConfig,
"game-name": {
...currentConfig,
[field]: value,
},
},
});
}
},
[viewerId, sendMove, roomData, updateGameConfig],
);
Step 5.3: Handle Local-Only State
For UI state that doesn't need network sync:
// Example: Input field that updates every keystroke
const [localInput, setLocalInput] = useState("");
// Merge with network state
const mergedState = {
...state,
currentInput: localInput, // Override with local value
};
// Only send to network when submitting
const submitAnswer = useCallback(
(answer: string) => {
sendMove({
type: "SUBMIT_ANSWER",
playerId: state.currentPlayer,
userId: viewerId || "",
data: { answer },
});
setLocalInput(""); // Clear local state
},
[state.currentPlayer, viewerId, sendMove],
);
Phase 6: Update Components (1 hour)
Step 6.1: Create GameComponent
// src/arcade-games/[game-name]/components/GameComponent.tsx
'use client'
import { useRouter } from 'next/navigation'
import { PageWithNav } from '@/components/PageWithNav'
import { useYourGame } from '../Provider'
import { SetupPhase } from './SetupPhase'
import { PlayingPhase } from './PlayingPhase'
import { ResultsPhase } from './ResultsPhase'
export function GameComponent() {
const router = useRouter()
const { state, exitSession } = useYourGame()
// Determine current player for turn indicator (if turn-based)
const currentPlayerId = state.gamePhase === 'playing' ? state.currentPlayer : undefined
return (
<PageWithNav
navTitle="Your Game Name"
navEmoji="🎮"
emphasizePlayerSelection={state.gamePhase === 'setup'}
currentPlayerId={currentPlayerId}
playerScores={state.score}
onExitSession={() => {
exitSession()
router.push('/arcade')
}}
>
{state.gamePhase === 'setup' && <SetupPhase />}
{state.gamePhase === 'playing' && <PlayingPhase />}
{state.gamePhase === 'results' && <ResultsPhase />}
</PageWithNav>
)
}
Step 6.2: Update Phase Components
Pattern for each component:
import { useYourGame } from '../Provider'
export function YourPhase() {
const { state, actionName1, actionName2 } = useYourGame()
// Replace dispatch() calls with action creators
// Before: dispatch({ type: 'SOME_ACTION', data })
// After: actionName1(data)
return (
// ... JSX (mostly unchanged)
)
}
Common Changes:
- const { state, dispatch } = useYourGame()
+ const { state, actionName } = useYourGame()
- dispatch({ type: 'START_GAME' })
+ startGame()
- dispatch({ type: 'MAKE_MOVE', data: { value } })
+ makeMove({ value })
- dispatch({ type: 'SET_CONFIG', field, value })
+ setConfig(field, value)
Step 6.3: Handle Error Display
Add error banner to appropriate phase(s):
const { lastError, clearError } = useYourGame()
{lastError && (
<div className="error-banner">
<span>⚠️ {lastError}</span>
<button onClick={clearError}>Dismiss</button>
</div>
)}
Phase 7: Update Page Route (15 minutes)
Step 7.1: Update Arcade Page
// src/app/arcade/[game-name]/page.tsx
'use client'
import { yourGame } from '@/arcade-games/[game-name]'
const { Provider, GameComponent } = yourGame
export default function YourGamePage() {
return (
<Provider>
<GameComponent />
</Provider>
)
}
That's it! The game is now on the modular platform.
Step 7.2: Update Game Selector (if needed)
Check if GameSelector.tsx needs updates. It should automatically pick up registered games from the registry.
Phase 8: Testing (1 hour)
Step 8.1: Solo Play Testing
Test all functionality with a single player in a room:
-
Setup Phase
- All settings render correctly
- Can change all settings
- Settings have sensible defaults
- Settings persist across page reloads
- Can start game
-
Playing Phase
- Game progresses correctly
- All moves work
- Scoring works
- Turn indicators work (if turn-based)
- Animations work
- Input handling works
-
Results Phase
- Scores display correctly
- Can play again
- Can exit game
Step 8.2: Multiplayer Testing
Test with multiple players:
-
Settings Persistence
- Settings save when changed
- Settings persist across page reloads
- Settings don't leak across games
-
Synchronization
- All players see same state
- Moves from any player work
- Turn indicators work (if turn-based)
- Timing is synchronized
-
Scoring
- Player attribution works
- Scores track correctly per player
- Multiplayer modes work (if applicable)
Step 8.3: Edge Case Testing
-
Navigation
- Can exit mid-game
- Can switch games
- Settings preserved when returning
-
Error Handling
- Invalid moves rejected gracefully
- Network errors handled
- Server validation errors shown
Step 8.4: Pre-Commit Checks
npm run pre-commit
Must pass:
- TypeScript compilation (0 errors)
- Format check (all files formatted)
- Lint check (0 errors, 0 warnings)
Phase 9: Cleanup (30 minutes)
Step 9.1: Delete Old Files
# If reducer pattern was used
rm src/app/arcade/[game-name]/reducer.ts
# Delete any local mode providers (deprecated)
rm src/app/arcade/[game-name]/context/LocalProvider.tsx
rm src/app/arcade/[game-name]/context/Local*Provider.tsx
# If dual providers were used
rm src/app/arcade/[game-name]/context/RoomProvider.tsx
# Delete old local mode page (if exists)
rm src/app/games/[game-name]/page.tsx
# If validator was moved
rm src/lib/arcade/validation/[Game]Validator.ts
Note: Only the arcade version (/app/arcade/[game-name]/page.tsx) should remain, using the new modular game definition.
Step 9.2: Update Documentation
In ARCHITECTURAL_IMPROVEMENTS.md:
### Migrated Games
- ✅ Number Guesser (v4.0.0)
- ✅ Math Sprint (v4.0.1)
- ✅ Memory Quiz (v4.1.0) - Completed 2025-01-16
In your migration plan:
## Migration Complete ✅
**Date Completed**: YYYY-MM-DD
**Final Commit**: [commit hash]
**Testing**: All tests passed
**Status**: Live in production
Step 9.3: Commit Migration
git add -A
git commit -m "feat(arcade): migrate [game-name] to modular platform
Migrates [game-name] from legacy architecture to modular game SDK.
Changes:
- Moved from /app/arcade/[game-name] to /arcade-games/[game-name]
- Created game definition with defineGame()
- Unified provider for local and room modes
- Added config validation
- Updated all components to use SDK patterns
Closes #[issue-number]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
git push
Troubleshooting
Issue: TypeScript errors after migration
Symptom: Lots of type errors in components
Solution:
- Check that move types include
playerId,userId,timestamp - Verify state extends
GameStatefrom SDK - Update component imports to use new provider location
Issue: State not syncing across players
Symptom: Players see different states
Solution:
- Verify
roomIdis passed touseArcadeSession(should always be present) - Check validator is handling moves correctly
- Ensure
applyMovereturns state (doesn't mutate) - Verify room actually has a roomId (not undefined)
Issue: Settings not persisting
Symptom: Settings reset on page reload
Solution:
- Check
useUpdateGameConfigis called insetConfig - Verify config is scoped by game name:
gameConfig['game-name'] - Ensure
initialStateuseMemo depends onroomData
Issue: Moves rejected with "Unknown move type"
Symptom: Validator rejects all moves
Solution:
- Check move type strings match validator switch cases
- Verify validator is registered correctly
- Check move structure includes all required fields
Issue: Components crash on mount
Symptom: useContext errors or null reference errors
Solution:
- Verify Provider wraps all components
- Check context exports match usage
- Ensure hooks are called within Provider
Appendix A: Creating a Validator
If your game doesn't have a validator, create one:
// src/arcade-games/[game-name]/Validator.ts
import type { GameValidator, ValidationResult } from "@/lib/arcade/game-sdk";
import type { YourGameState, YourGameMove } from "./types";
export class YourGameValidator
implements GameValidator<YourGameState, YourGameMove>
{
validateMove(
state: YourGameState,
move: YourGameMove,
context?: { userId?: string },
): ValidationResult {
switch (move.type) {
case "START_GAME":
return this.validateStartGame(state, move.data);
case "MAKE_MOVE":
return this.validateMakeMove(state, move.playerId, move.data);
// ... handle each move type
default:
return { valid: false, error: "Unknown move type" };
}
}
private validateStartGame(state: YourGameState, data: any): ValidationResult {
// Check preconditions
if (state.gamePhase !== "setup") {
return { valid: false, error: "Can only start from setup phase" };
}
// Create new state
const newState: YourGameState = {
...state,
gamePhase: "playing",
activePlayers: data.activePlayers || [],
playerMetadata: data.playerMetadata || {},
// ... initialize game state
};
return { valid: true, newState };
}
// ... more validation methods
isGameComplete(state: YourGameState): boolean {
return state.gamePhase === "results";
}
getInitialState(config: YourGameConfig): YourGameState {
return {
gamePhase: "setup",
activePlayers: [],
playerMetadata: {},
// ... all initial state from config
};
}
}
export const yourGameValidator = new YourGameValidator();
Key Principles:
- ✅ Validator is authoritative (client is display only)
- ✅ Always return new state (never mutate)
- ✅ Validate all preconditions before state changes
- ✅ Return clear error messages for invalid moves
- ✅ Handle all edge cases
Appendix B: Common Patterns
Pattern: Turn-Based Games
// Validator
if (move.playerId !== state.currentPlayer) {
return { valid: false, error: "Not your turn" };
}
// After valid move, rotate turn
const nextPlayerIndex = (currentIndex + 1) % state.activePlayers.length;
const newState = {
...state,
currentPlayer: state.activePlayers[nextPlayerIndex],
};
Pattern: Team Moves
// Use TEAM_MOVE for moves where any player can act
sendMove({
type: "SUBMIT_ANSWER",
playerId: TEAM_MOVE, // Not attributed to specific player
userId: viewerId || "",
data: { answer },
});
Pattern: Timed Actions
// Store timestamp in state, check elapsed time
const elapsedTime = Date.now() - state.startTime;
if (elapsedTime > state.timeLimit) {
return { valid: false, error: "Time expired" };
}
Pattern: Scoring by User
// Track scores by userId (not playerId) for multi-character games
const newScores = { ...state.scores };
if (move.userId) {
newScores[move.userId] = (newScores[move.userId] || 0) + points;
}
Summary Checklist
Use this as a final verification before declaring migration complete:
Code Quality
- TypeScript compiles with 0 errors
- Linter passes with 0 errors, 0 warnings
- Formatter has been run
- No console errors in browser
- No console warnings in terminal
Architecture
- Game definition created with
defineGame() - Validator implements
GameValidatorinterface - Provider uses SDK hooks exclusively
- Types extend SDK base types
- Config validation function exists
Registration
- Game registered in
game-registry.ts - Validator registered in
validators.ts - Config type inferred in
game-configs.ts - Default config constant added
Functionality
- All game phases work in local mode
- All game phases work in room mode
- Settings persist in room mode
- Multiplayer synchronization works
- Scoring tracks correctly
- Can exit and re-enter game
Documentation
- Migration plan created
- Breaking changes documented
- Testing results recorded
- Commit messages are clear
Conclusion
You've now successfully migrated a game to the modular platform! 🎉
The game is now:
- ✅ Self-contained in
/arcade-games/[game-name]/ - ✅ Using the Game SDK for all functionality
- ✅ Room mode only (no local mode code)
- ✅ Automatically type-checked
- ✅ Validated server-side
- ✅ Settings always persist to room config
- ✅ Easier to maintain and extend
Next Steps:
- Monitor game in production for any issues
- Consider adding game-specific settings
- Migrate the next legacy game!
Questions or issues? Refer to:
- Game SDK Docs:
/src/arcade-games/README.md - Architecture Docs:
/docs/ARCHITECTURAL_IMPROVEMENTS.md - Example Games: Number Guesser, Math Sprint, Memory Quiz