Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release-bot
52019a24c2 chore(release): 4.4.3 [skip ci]
## [4.4.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.4.2...v4.4.3) (2025-10-17)

### Bug Fixes

* **complement-race:** train now moves in sprint mode ([54b46e7](54b46e771e))

### Code Refactoring

* simplify train debug logs to strings only ([334a49c](334a49c92e))
2025-10-17 12:15:31 +00:00
Thomas Hallock
54b46e771e fix(complement-race): train now moves in sprint mode
**THE BUG**: Validator was only updating momentum in Sprint mode,
but NEVER updating position! This caused trainPosition to stay at 0.

**THE FIX**: Added position calculation based on momentum:
- moveDistance = momentum / 20
- Starting momentum (50) → 2.5 units per answer
- Max momentum (100) → 5 units per answer
- Creates progression: higher momentum = faster train movement

Position updates per answer now work:
- Correct answer: momentum +15, then position +=(momentum/20)
- Wrong answer: momentum -10, then position +=(momentum/20)
- Position capped at 100 (end of route)

This matches the original single-player behavior where the train
speed was tied to momentum.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:14:30 -05:00
Thomas Hallock
334a49c92e refactor: simplify train debug logs to strings only
Changed from logging objects to simple string format:
- Before: { momentum: 50, trainPosition: 0, pressure: 60, ... }
- After: Sprint: momentum=50 pos=0 pressure=60

Issue identified from logs: trainPosition stuck at 0!
This is why train isn't appearing/moving.
2025-10-17 07:13:24 -05:00
semantic-release-bot
739e928c6e chore(release): 4.4.2 [skip ci]
## [4.4.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.4.1...v4.4.2) (2025-10-17)

### Code Refactoring

* **complement-race:** remove verbose logging, keep only train debug logs ([86af2fe](86af2fe902))
2025-10-17 12:07:39 +00:00
Thomas Hallock
86af2fe902 refactor(complement-race): remove verbose logging, keep only train debug logs
Removed all excessive console logging that was causing console overflow.

**Removed**:
- GameDisplay: All keyboard/answer validation logs (input bug is fixed)
- Context reducer: All action dispatched logs
- Provider: Verbose state transformation details
- Provider: Dispatch compatibility layer logs

**Kept (for train/pressure debugging)**:
- Provider: Sprint-specific values (momentum, trainPosition, pressure)
- SteamTrainJourney: Component props and state

This should give us minimal, focused logs to debug:
1. Why train isn't appearing
2. Why pressure is stuck at 100

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 07:06:30 -05:00
Thomas Hallock
60ce9c0eb1 debug(complement-race): add comprehensive logging for missing train issue
Added detailed console logging to debug why train isn't appearing:

**Provider.tsx**:
- State transformation details (localPlayer, all players, game phase)
- Transformed sprint-specific values (momentum, trainPosition, pressure)

**SteamTrainJourney.tsx**:
- Component props (momentum, trainPosition, pressure, etc.)
- State from provider (stations, passengers, currentRoute, gamePhase)

This will help identify:
1. If localPlayer is null/undefined
2. If momentum/position values are 0
3. If stations/passengers are empty
4. What game phase we're in

Note: User reports pressure is pinned at 100 - likely related to formula:
`pressure: localPlayer?.momentum ? Math.min(100, localPlayer.momentum + 10) : 0`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 06:59:51 -05:00
7 changed files with 36 additions and 76 deletions

View File

@@ -1,3 +1,22 @@
## [4.4.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.4.2...v4.4.3) (2025-10-17)
### Bug Fixes
* **complement-race:** train now moves in sprint mode ([54b46e7](https://github.com/antialias/soroban-abacus-flashcards/commit/54b46e771e654721e7fabb1f45ecd45daf8e447f))
### Code Refactoring
* simplify train debug logs to strings only ([334a49c](https://github.com/antialias/soroban-abacus-flashcards/commit/334a49c92e112c852c483b5dbe3a3d0aef8a5c03))
## [4.4.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.4.1...v4.4.2) (2025-10-17)
### Code Refactoring
* **complement-race:** remove verbose logging, keep only train debug logs ([86af2fe](https://github.com/antialias/soroban-abacus-flashcards/commit/86af2fe902b3d3790b7b4659fdc698caed8e4dd9))
## [4.4.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.4.0...v4.4.1) (2025-10-17)

View File

@@ -70,12 +70,6 @@ export function GameDisplay() {
// Only process number keys
if (/^[0-9]$/.test(e.key)) {
const newInput = state.currentInput + e.key
console.log('⌨️ [KeyPress] Number key pressed:', {
key: e.key,
oldInput: state.currentInput,
newInput,
currentQuestion: state.currentQuestion?.number,
})
dispatch({ type: 'UPDATE_INPUT', input: newInput })
// Check if answer is complete
@@ -83,27 +77,12 @@ export function GameDisplay() {
const answer = parseInt(newInput, 10)
const correctAnswer = state.currentQuestion.correctAnswer
console.log('🔍 [KeyPress] Checking answer:', {
newInput,
newInputLength: newInput.length,
correctAnswer,
correctAnswerLength: correctAnswer.toString().length,
willSubmit: newInput.length >= correctAnswer.toString().length,
})
// If we have enough digits to match the answer, submit
if (newInput.length >= correctAnswer.toString().length) {
const responseTime = Date.now() - state.questionStartTime
const isCorrect = answer === correctAnswer
const pairKey = `${state.currentQuestion.number}_${state.currentQuestion.correctAnswer}_${state.currentQuestion.targetSum}`
console.log('📝 [KeyPress] Submitting answer:', {
answer,
correctAnswer,
isCorrect,
responseTime,
})
if (isCorrect) {
// Correct answer
dispatch({ type: 'SUBMIT_ANSWER', answer })
@@ -154,7 +133,6 @@ export function GameDisplay() {
dispatch({ type: 'SHOW_ADAPTIVE_FEEDBACK', feedback })
}
console.log('➡️ [KeyPress] Dispatching NEXT_QUESTION after correct answer')
dispatch({ type: 'NEXT_QUESTION' })
} else {
// Incorrect answer
@@ -172,16 +150,11 @@ export function GameDisplay() {
dispatch({ type: 'SHOW_ADAPTIVE_FEEDBACK', feedback })
}
console.log('❌ [KeyPress] Incorrect answer - clearing input')
dispatch({ type: 'UPDATE_INPUT', input: '' })
}
}
}
} else if (e.key === 'Backspace') {
console.log('⌫ [KeyPress] Backspace pressed:', {
oldInput: state.currentInput,
newInput: state.currentInput.slice(0, -1),
})
dispatch({
type: 'UPDATE_INPUT',
input: state.currentInput.slice(0, -1),
@@ -189,16 +162,8 @@ export function GameDisplay() {
}
}
console.log('🔄 [KeyPress Effect] Setting up keyboard listener with state:', {
currentInput: state.currentInput,
currentQuestion: state.currentQuestion?.number,
})
window.addEventListener('keydown', handleKeyPress)
return () => {
console.log('🧹 [KeyPress Effect] Cleaning up keyboard listener')
window.removeEventListener('keydown', handleKeyPress)
}
return () => window.removeEventListener('keydown', handleKeyPress)
}, [
state.currentInput,
state.currentQuestion,
@@ -231,19 +196,6 @@ export function GameDisplay() {
if (!state.currentQuestion) return null
// DEBUG: Log state on every render
console.log('🎮 [GameDisplay] Render:', {
currentInput: state.currentInput,
currentInputLength: state.currentInput?.length,
currentInputType: typeof state.currentInput,
currentQuestion: state.currentQuestion,
questionNumber: state.currentQuestion?.number,
correctAnswer: state.currentQuestion?.correctAnswer,
targetSum: state.currentQuestion?.targetSum,
score: state.score,
streak: state.streak,
})
return (
<div
data-component="game-display"

View File

@@ -94,6 +94,10 @@ export function SteamTrainJourney({
currentInput,
}: SteamTrainJourneyProps) {
const { state } = useComplementRace()
console.log(
`🚂 Train: mom=${momentum} pos=${trainPosition} stations=${state.stations.length} passengers=${state.passengers.length}`
)
const { getSkyGradient, getTimeOfDayPeriod } = useSteamJourney()
const _skyGradient = getSkyGradient()
const period = getTimeOfDayPeriod()

View File

@@ -113,13 +113,6 @@ const initialState: GameState = {
}
function gameReducer(state: GameState, action: GameAction): GameState {
console.log('🔄 [Reducer] Action dispatched:', {
type: action.type,
action,
currentInput: state.currentInput,
currentQuestion: state.currentQuestion?.number,
})
switch (action.type) {
case 'SET_MODE':
return { ...state, mode: action.mode }
@@ -178,8 +171,6 @@ function gameReducer(state: GameState, action: GameAction): GameState {
}
case 'NEXT_QUESTION': {
console.log('➡️ [Reducer] NEXT_QUESTION - clearing input and generating new question')
// Generate new question based on mode
const generateQuestion = () => {
let targetSum: number
@@ -221,28 +212,16 @@ function gameReducer(state: GameState, action: GameAction): GameState {
}
}
const newQuestion = generateQuestion()
console.log('📊 [Reducer] NEXT_QUESTION result:', {
oldQuestion: state.currentQuestion,
newQuestion,
oldInput: state.currentInput,
newInput: '',
})
return {
...state,
previousQuestion: state.currentQuestion,
currentQuestion: newQuestion,
currentQuestion: generateQuestion(),
questionStartTime: Date.now(),
currentInput: '',
}
}
case 'UPDATE_INPUT':
console.log('✏️ [Reducer] UPDATE_INPUT:', {
oldInput: state.currentInput,
newInput: action.input,
})
return { ...state, currentInput: action.input }
case 'SUBMIT_ANSWER': {

View File

@@ -330,6 +330,10 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
}
}, [multiplayerState, localPlayerId, localUIState])
console.log(
`🚂 Sprint: momentum=${compatibleState.momentum} pos=${compatibleState.trainPosition} pressure=${compatibleState.pressure}`
)
// Action creators
const startGame = useCallback(() => {
if (activePlayers.length === 0) {
@@ -482,8 +486,6 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
// Compatibility dispatch function for existing UI components
const dispatch = useCallback(
(action: { type: string; [key: string]: any }) => {
console.log('[ComplementRaceProvider] dispatch called (compatibility layer):', action.type)
// Map old reducer actions to new action creators
switch (action.type) {
case 'START_COUNTDOWN':
@@ -497,7 +499,6 @@ export function ComplementRaceProvider({ children }: { children: ReactNode }) {
}
break
case 'NEXT_QUESTION':
console.log('🧹 [Provider] NEXT_QUESTION - clearing local input state')
setLocalUIState((prev) => ({ ...prev, currentInput: '' }))
nextQuestion()
break

View File

@@ -317,12 +317,17 @@ export class ComplementRaceValidator
updatedPlayer.position = Math.min(100, player.position + 100 / state.config.raceGoal)
}
} else if (state.config.style === 'sprint') {
// Sprint: Update momentum
// Sprint: Update momentum AND position
if (correct) {
updatedPlayer.momentum = Math.min(100, player.momentum + 15)
} else {
updatedPlayer.momentum = Math.max(0, player.momentum - 10)
}
// Move train based on momentum (momentum/20 = position change per answer)
// Higher momentum = faster movement
const moveDistance = updatedPlayer.momentum / 20
updatedPlayer.position = Math.min(100, player.position + moveDistance)
} else if (state.config.style === 'survival') {
// Survival: Always move forward, speed based on accuracy
const moveDistance = correct ? 5 : 2

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "4.4.1",
"version": "4.4.3",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [