Add playback speed for best game

This commit is contained in:
Peter Stockings
2026-01-10 11:56:13 +11:00
parent de1563dae6
commit e9cb8b52df
2 changed files with 24 additions and 3 deletions

View File

@@ -1,3 +1,4 @@
import { useState } from 'react';
import SnakeCanvas from './SnakeCanvas'; import SnakeCanvas from './SnakeCanvas';
import type { Network } from '../../lib/snakeAI/network'; import type { Network } from '../../lib/snakeAI/network';
@@ -8,6 +9,8 @@ interface BestSnakeDisplayProps {
} }
export default function BestSnakeDisplay({ network, gridSize, fitness }: BestSnakeDisplayProps) { export default function BestSnakeDisplay({ network, gridSize, fitness }: BestSnakeDisplayProps) {
const [playbackSpeed, setPlaybackSpeed] = useState(15);
if (!network) return null; if (!network) return null;
return ( return (
@@ -19,6 +22,22 @@ export default function BestSnakeDisplay({ network, gridSize, fitness }: BestSna
<span className="value">{Math.round(fitness)}</span> <span className="value">{Math.round(fitness)}</span>
</div> </div>
</div> </div>
<div className="playback-controls" style={{ padding: '0 10px 10px 10px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.8rem', color: '#888' }}>
<span>Replay Speed:</span>
<input
type="range"
min="1"
max="100"
value={playbackSpeed}
onChange={(e) => setPlaybackSpeed(Number(e.target.value))}
style={{ flex: 1, accentColor: '#4ecdc4' }}
/>
<span style={{ minWidth: '3ch', textAlign: 'right' }}>{playbackSpeed}x</span>
</div>
</div>
<div className="best-canvas-wrapper"> <div className="best-canvas-wrapper">
<SnakeCanvas <SnakeCanvas
network={network} network={network}
@@ -26,6 +45,7 @@ export default function BestSnakeDisplay({ network, gridSize, fitness }: BestSna
size="large" size="large"
showGrid={true} showGrid={true}
showStats={true} showStats={true}
playbackSpeed={playbackSpeed}
/> />
</div> </div>
</div> </div>

View File

@@ -9,6 +9,7 @@ interface SnakeCanvasProps {
showGrid?: boolean; showGrid?: boolean;
size?: 'small' | 'normal' | 'large'; size?: 'small' | 'normal' | 'large';
showStats?: boolean; // Show score/length/steps even in small mode showStats?: boolean; // Show score/length/steps even in small mode
playbackSpeed?: number; // Steps per second (default: 15)
} }
const CELL_SIZES = { const CELL_SIZES = {
@@ -19,7 +20,7 @@ const CELL_SIZES = {
const CANVAS_PADDING = 10; const CANVAS_PADDING = 10;
export default function SnakeCanvas({ network, gridSize, showGrid = true, size = 'normal', showStats = false }: SnakeCanvasProps) { export default function SnakeCanvas({ network, gridSize, showGrid = true, size = 'normal', showStats = false, playbackSpeed = 15 }: SnakeCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const [currentGame, setCurrentGame] = useState<GameState | null>(null); const [currentGame, setCurrentGame] = useState<GameState | null>(null);
const animationFrameRef = useRef<number>(); const animationFrameRef = useRef<number>();
@@ -43,7 +44,7 @@ export default function SnakeCanvas({ network, gridSize, showGrid = true, size =
useEffect(() => { useEffect(() => {
if (!network || !currentGame) return; if (!network || !currentGame) return;
const STEPS_PER_SECOND = 10; // Speed of game playback const STEPS_PER_SECOND = playbackSpeed; // Use prop
const UPDATE_INTERVAL = 1000 / STEPS_PER_SECOND; const UPDATE_INTERVAL = 1000 / STEPS_PER_SECOND;
const animate = (timestamp: number) => { const animate = (timestamp: number) => {
@@ -82,7 +83,7 @@ export default function SnakeCanvas({ network, gridSize, showGrid = true, size =
cancelAnimationFrame(animationFrameRef.current); cancelAnimationFrame(animationFrameRef.current);
} }
}; };
}, [network?.id, !!currentGame, gridSize]); // Use ID and boolean existence check to prevent loop restart on every frame }, [network?.id, !!currentGame, gridSize, playbackSpeed]); // Added playbackSpeed dependency
// Set canvas size once when props change (not on every render) // Set canvas size once when props change (not on every render)
useEffect(() => { useEffect(() => {