import React, { useRef, useEffect } from 'react'; import { View, StyleSheet, Dimensions, Animated, Text } from 'react-native'; import { PuzzleTileComponent } from './PuzzleTile'; import { PuzzleGameState } from '../SimplePuzzleGame'; const { width: screenWidth } = Dimensions.get('window'); interface PuzzleBoardProps { gameState: PuzzleGameState; gridSize: number; onTilePress: (row: number, col: number) => void; canMoveTile: (position: { row: number; col: number }) => boolean; useNumbers: boolean; imageUrl?: any; showHints?: boolean; animateLastMove?: boolean; } /** * Enhanced puzzle board component with animations and effects */ export const PuzzleBoard: React.FC = ({ gameState, gridSize, onTilePress, canMoveTile, useNumbers, imageUrl, showHints = true, animateLastMove = true }) => { const boardSize = Math.min(screenWidth - 40, 400); const tileSize = (boardSize - (gridSize + 1) * 4) / gridSize; const animatedValues = useRef<{ [key: string]: any }>({}); const pulseAnim = useRef(new Animated.Value(1)).current; const completionAnim = useRef(new Animated.Value(0)).current; // Initialize animated values for tiles useEffect(() => { gameState.tiles.forEach((tile) => { const key = `${tile.position.row}-${tile.position.col}`; if (!animatedValues.current[key]) { animatedValues.current[key] = new Animated.Value(1); } }); }, [gameState.tiles]); // Animate tile press const animateTilePress = (row: number, col: number) => { const key = `${row}-${col}`; const animValue = animatedValues.current[key]; if (animValue) { Animated.sequence([ Animated.timing(animValue, { toValue: 0.9, duration: 100, useNativeDriver: true, }), Animated.timing(animValue, { toValue: 1, duration: 100, useNativeDriver: true, }), ]).start(); } }; // Pulse animation for hints useEffect(() => { if (showHints) { const pulseAnimation = Animated.loop( Animated.sequence([ Animated.timing(pulseAnim, { toValue: 1.1, duration: 1000, useNativeDriver: true, }), Animated.timing(pulseAnim, { toValue: 1, duration: 1000, useNativeDriver: true, }), ]) ); pulseAnimation.start(); return () => pulseAnimation.stop(); } }, [showHints, pulseAnim]); // Completion animation useEffect(() => { if (gameState.isComplete) { Animated.spring(completionAnim, { toValue: 1, tension: 50, friction: 5, useNativeDriver: true, }).start(); } }, [gameState.isComplete, completionAnim]); const handleTilePress = (row: number, col: number) => { animateTilePress(row, col); onTilePress(row, col); }; const renderTile = (row: number, col: number) => { const tile = gameState.tiles.find(t => t.position.row === row && t.position.col === col ); const isEmpty = gameState.emptyTilePosition.row === row && gameState.emptyTilePosition.col === col; const key = `${row}-${col}`; const tileCanMove = tile ? canMoveTile({ row, col }) : false; // Use pulse animation for movable tiles if hints are enabled const shouldPulse = showHints && tileCanMove && !gameState.isComplete; const animValue = shouldPulse ? pulseAnim : animatedValues.current[key]; // Ensure we always show content - if no tile found, create a placeholder const displayTile = tile || { id: row * gridSize + col + 1, value: row * gridSize + col + 1, position: { row, col }, correctPosition: { row, col } }; return ( handleTilePress(row, col)} canMove={tileCanMove} useNumbers={useNumbers} imageUrl={imageUrl} isEmpty={isEmpty} animatedValue={animValue} /> ); }; const renderProgressIndicator = () => { const correctTiles = gameState.tiles.filter(tile => tile.position.row === tile.correctPosition.row && tile.position.col === tile.correctPosition.col ).length; const totalTiles = gameState.tiles.length; const progress = totalTiles > 0 ? (correctTiles / totalTiles) * 100 : 0; // Debug info if (totalTiles === 0) { console.warn('PuzzleBoard: No tiles in game state'); } return ( התקדמות: {Math.round(progress)}% ({correctTiles}/{totalTiles}) ); }; return ( {renderProgressIndicator()} {Array.from({ length: gridSize }, (_, row) => Array.from({ length: gridSize }, (_, col) => renderTile(row, col)) )} {/* Debug info - show if no tiles are visible */} {gameState.tiles.length === 0 && ( אין אריחים במשחק מספר אריחים: {gameState.tiles.length} )} {/* Completion overlay */} {gameState.isComplete && ( 🎉 מושלם! )} {/* Game info */} מהלכים {gameState.moves} זמן {gameState.startTime > 0 ? Math.floor((Date.now() - gameState.startTime) / 1000) + 's' : '0s' } ); }; const styles = StyleSheet.create({ container: { alignItems: 'center', }, progressContainer: { width: '100%', marginBottom: 16, paddingHorizontal: 20, }, progressText: { textAlign: 'center', fontSize: 14, fontWeight: '600', color: '#666', marginBottom: 8, }, progressBar: { height: 6, backgroundColor: '#E0E0E0', borderRadius: 3, overflow: 'hidden', }, progressFill: { height: '100%', backgroundColor: '#4CAF50', borderRadius: 3, }, board: { flexDirection: 'row', flexWrap: 'wrap', backgroundColor: '#ECEFF1', padding: 4, borderRadius: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.2, shadowRadius: 12, elevation: 12, position: 'relative', }, completionOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(76, 175, 80, 0.9)', justifyContent: 'center', alignItems: 'center', borderRadius: 16, }, completionText: { fontSize: 48, marginBottom: 8, }, completionSubtext: { fontSize: 20, fontWeight: 'bold', color: 'white', }, infoContainer: { flexDirection: 'row', justifyContent: 'space-around', width: '100%', marginTop: 16, paddingHorizontal: 20, }, infoItem: { alignItems: 'center', backgroundColor: '#F8F9FA', paddingVertical: 8, paddingHorizontal: 16, borderRadius: 8, minWidth: 80, }, infoLabel: { fontSize: 12, color: '#666', marginBottom: 2, }, infoValue: { fontSize: 16, fontWeight: 'bold', color: '#333', }, debugInfo: { position: 'absolute', top: '50%', left: '50%', transform: [{ translateX: -50 }, { translateY: -50 }], backgroundColor: 'rgba(255, 0, 0, 0.8)', padding: 16, borderRadius: 8, alignItems: 'center', }, debugText: { color: 'white', fontSize: 14, fontWeight: 'bold', textAlign: 'center', }, });