UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

761 lines (690 loc) 23.2 kB
/** * Image Puzzle Game Component * Version 4.0.1 - Better Images + No Gaps Between Pieces */ // Import React safely let React; try { React = require('react'); console.log('✅ React loaded successfully for ImagePuzzleGame'); } catch (error) { console.error('❌ Failed to load React:', error); } const { View, Text, TouchableOpacity, StyleSheet, Alert, Dimensions, Image } = require('react-native'); const { generateImagePuzzlePieces, isImagePuzzleSolved, arePiecesAdjacent, getPieceCoordinates } = require('../utils/helpers'); // 🖼️ Default images for the puzzle - CUSTOMIZABLE! const DEFAULT_IMAGES = [ { id: 'landscape1', name: 'נוף הרים יפה', uri: 'https://images.unsplash.com/photo-1464822759844-d150baec93d5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🏔️' }, { id: 'ocean1', name: 'חוף ים טרופי', uri: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🏖️' }, { id: 'flowers1', name: 'פרחים צבעוניים', uri: 'https://images.unsplash.com/photo-1490750967868-88aa4486c946?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🌺' }, { id: 'sunset1', name: 'שקיעה מדהימה', uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🌅' }, { id: 'forest1', name: 'יער ירוק', uri: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🌲' }, { id: 'city1', name: 'עיר בלילה', uri: 'https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80', emoji: '🌃' }, { id: 'golda', name: 'Golda', uri: 'https://instagram.ftlv5-1.fna.fbcdn.net/v/t51.2885-15/465753040_18067270891639321_3863550448713008096_n.jpg?stp=dst-jpg_e35_tt6&efg=eyJ2ZW5jb2RlX3RhZyI6IkZFRUQuaW1hZ2VfdXJsZ2VuLjE0NDB4MTgwMC5zZHIuZjc1NzYxLmRlZmF1bHRfaW1hZ2UifQ&_nc_ht=instagram.ftlv5-1.fna.fbcdn.net&_nc_cat=101&_nc_oc=Q6cZ2QHKxeSQzkKX13akJo0hzfXy2-s09ghqSsZIoLLH3CebtcNk_6_NgDMQZmJ4VP2HBhO-kf-w3VsemLifm6tNOsVu&_nc_ohc=cb1qWw-JYzsQ7kNvwGrcCES&_nc_gid=rMAtm9z82M14_JpXibpJlA&edm=APs17CUBAAAA&ccb=7-5&ig_cache_key=MzQ5NDYxMzgyMTI1NzIyNjc3Nw%3D%3D.3-ccb7-5&oh=00_AfT1Af9-ViKk57jKDH1n0fosCZcberwJ_Jz7MxZffoxytQ&oe=687E9396&_nc_sid=10d13b', emoji: '🌃' }, ]; // =========================================== // REAL IMAGE PUZZLE GAME - CLASS COMPONENT! // =========================================== class ImagePuzzleGameComponent extends React.Component { constructor(props) { super(props); console.log('🖼️ ImagePuzzleGameComponent - FULL GAME initializing...'); const gridSize = props.gridSize || 3; const selectedImage = props.selectedImage || DEFAULT_IMAGES[0]; this.state = { currentScreen: props.showMenu !== false ? 'menu' : 'game', // Puzzle state pieces: [], moves: 0, isComplete: false, startTime: null, gameTime: 0, // Game settings gridSize: gridSize, selectedImage: selectedImage, showPreview: true, isGameStarted: false, // UI state imageLoaded: false, imageError: false, difficulty: props.difficulty || 'medium' }; this.initializeGame = this.initializeGame.bind(this); this.startGame = this.startGame.bind(this); this.handlePiecePress = this.handlePiecePress.bind(this); this.checkGameComplete = this.checkGameComplete.bind(this); this.resetGame = this.resetGame.bind(this); this.goToMenu = this.goToMenu.bind(this); this.selectImage = this.selectImage.bind(this); this.togglePreview = this.togglePreview.bind(this); this.onImageLoad = this.onImageLoad.bind(this); this.onImageError = this.onImageError.bind(this); } initializeGame() { console.log('🔄 Initializing image puzzle game...'); const pieces = generateImagePuzzlePieces(this.state.gridSize); this.setState({ pieces: pieces, moves: 0, isComplete: false, startTime: Date.now(), gameTime: 0, isGameStarted: true, imageLoaded: false, imageError: false }); this.timer = setInterval(() => { if (!this.state.isComplete && this.state.startTime) { const currentTime = (Date.now() - this.state.startTime) / 1000; this.setState({ gameTime: currentTime }); } }, 100); } startGame() { console.log('🚀 Starting image puzzle game...'); this.setState({ currentScreen: 'game' }); this.initializeGame(); if (this.props.onGameStart) { this.props.onGameStart(); } } handlePiecePress(pieceIndex) { console.log(`🧩 Piece pressed: ${pieceIndex}`); const { pieces, gridSize, isComplete } = this.state; if (isComplete || !pieces || pieces.length === 0) return; // Find empty piece const emptyPieceIndex = pieces.findIndex(piece => piece && piece.isEmpty); if (emptyPieceIndex === -1) { console.log('❌ No empty piece found'); return; } // Check if clicked piece is adjacent to empty space if (arePiecesAdjacent(pieceIndex, emptyPieceIndex, gridSize)) { console.log('✅ Valid move - swapping pieces'); // Swap pieces const newPieces = [...pieces]; // Swap positions [newPieces[pieceIndex], newPieces[emptyPieceIndex]] = [newPieces[emptyPieceIndex], newPieces[pieceIndex]]; // Update position tracking if (newPieces[pieceIndex]) newPieces[pieceIndex].position = pieceIndex; if (newPieces[emptyPieceIndex]) newPieces[emptyPieceIndex].position = emptyPieceIndex; this.setState({ pieces: newPieces, moves: this.state.moves + 1 }, () => { this.checkGameComplete(); }); } else { console.log('❌ Invalid move - not adjacent to empty space'); } } checkGameComplete() { if (isImagePuzzleSolved(this.state.pieces)) { console.log('🎉 Image puzzle game completed!'); if (this.timer) { clearInterval(this.timer); } const finalTime = (Date.now() - this.state.startTime) / 1000; const difficultyMultiplier = this.state.gridSize === 3 ? 1 : this.state.gridSize === 4 ? 1.5 : 2; const baseScore = 1000 * difficultyMultiplier; const score = Math.max(baseScore - this.state.moves * 8 - finalTime * 4, 100); this.setState({ isComplete: true, gameTime: finalTime }); const result = { score: Math.round(score), moves: this.state.moves, time: finalTime, gridSize: this.state.gridSize, imageName: this.state.selectedImage.name, completed: true }; if (this.props.onGameComplete) { this.props.onGameComplete(result); } setTimeout(() => { Alert.alert( '🎉 מדהים!', `השלמת את פאזל התמונה!\n\n` + `🖼️ תמונה: ${this.state.selectedImage.name}\n` + `🔲 גודל: ${this.state.gridSize}x${this.state.gridSize}\n` + `🕐 זמן: ${finalTime.toFixed(1)} שניות\n` + `🔄 מהלכים: ${this.state.moves}\n` + `🎯 ניקוד: ${Math.round(score)}`, [ { text: 'פאזל חדש', onPress: this.resetGame }, { text: 'תפריט', onPress: this.goToMenu } ] ); }, 500); } } resetGame() { console.log('🔄 Resetting image puzzle game...'); if (this.timer) { clearInterval(this.timer); } this.initializeGame(); } goToMenu() { console.log('🏠 Going to image puzzle menu...'); if (this.timer) { clearInterval(this.timer); } this.setState({ currentScreen: 'menu' }); } selectImage(image) { console.log('🖼️ Selecting image:', image.name); this.setState({ selectedImage: image, imageLoaded: false, imageError: false }); } togglePreview() { this.setState({ showPreview: !this.state.showPreview }); } onImageLoad() { console.log('✅ Image loaded successfully'); this.setState({ imageLoaded: true, imageError: false }); } onImageError() { console.log('❌ Image failed to load'); this.setState({ imageError: true, imageLoaded: false }); } componentWillUnmount() { if (this.timer) { clearInterval(this.timer); } } renderPiece(piece, index) { const styles = this.getImagePuzzleStyles(); const { gridSize, selectedImage } = this.state; if (!piece || piece.isEmpty) { return React.createElement(View, { key: `empty-${index}`, style: [styles.puzzlePiece, styles.emptyPiece] }); } // Calculate the piece's correct position for background positioning const { row, col } = getPieceCoordinates(piece.id, gridSize); const pieceSize = styles.puzzlePiece.width; const backgroundPosition = { marginLeft: -col * pieceSize, marginTop: -row * pieceSize }; return React.createElement(TouchableOpacity, { key: `piece-${piece.id}`, style: styles.puzzlePiece, onPress: () => this.handlePiecePress(index), activeOpacity: 0.8 }, React.createElement(View, { style: styles.pieceContainer }, React.createElement(Image, { source: { uri: selectedImage.uri }, style: [styles.pieceImage, backgroundPosition], resizeMode: 'cover' }) ) ); } renderImageSelector() { const styles = this.getImagePuzzleStyles(); return React.createElement(View, { style: styles.imageSelector }, React.createElement(Text, { style: styles.selectorTitle }, 'בחר תמונה:'), React.createElement(View, { style: styles.imageGrid }, ...DEFAULT_IMAGES.map(image => React.createElement(TouchableOpacity, { key: image.id, style: [ styles.imageOption, this.state.selectedImage.id === image.id && styles.selectedImageOption ], onPress: () => this.selectImage(image) }, React.createElement(Text, { style: styles.imageEmoji }, image.emoji), React.createElement(Text, { style: styles.imageName }, image.name) ) ) ) ); } renderMenu() { const styles = this.getImagePuzzleStyles(); const { gridSize, selectedImage } = this.state; return React.createElement(View, { style: styles.menuContainer }, React.createElement(Text, { style: styles.title }, '🖼️ פאזל תמונה'), React.createElement(Text, { style: styles.subtitle }, 'הרכיבו את התמונה מחדש!'), this.renderImageSelector(), React.createElement(View, { style: styles.gameInfo }, React.createElement(Text, { style: styles.infoText }, `🖼️ תמונה: ${selectedImage.name}`), React.createElement(Text, { style: styles.infoText }, `🔲 גודל: ${gridSize}x${gridSize} חלקים`), React.createElement(Text, { style: styles.infoText }, `🎯 מטרה: הרכבת התמונה השלמה`) ), React.createElement(View, { style: styles.previewContainer }, React.createElement(Text, { style: styles.previewTitle }, 'תצוגה מקדימה:'), React.createElement(View, { style: styles.previewImageContainer }, React.createElement(Image, { source: { uri: selectedImage.uri }, style: styles.previewImage, onLoad: this.onImageLoad, onError: this.onImageError, resizeMode: 'cover' }), !this.state.imageLoaded && !this.state.imageError && React.createElement(Text, { style: styles.loadingText }, 'טוען תמונה...'), this.state.imageError && React.createElement(Text, { style: styles.errorText }, 'שגיאה בטעינת התמונה') ) ), React.createElement(TouchableOpacity, { style: [styles.startButton, (!this.state.imageLoaded || this.state.imageError) && styles.disabledButton], onPress: this.startGame, disabled: !this.state.imageLoaded || this.state.imageError }, React.createElement(Text, { style: styles.buttonText }, '🚀 התחל פאזל') ) ); } renderGame() { const styles = this.getImagePuzzleStyles(); const { pieces, moves, gridSize, gameTime, isComplete, selectedImage, showPreview } = this.state; return React.createElement(View, { style: styles.gameContainer }, React.createElement(View, { style: styles.header }, React.createElement(Text, { style: styles.gameTitle }, '🖼️ פאזל תמונה'), React.createElement(View, { style: styles.stats }, React.createElement(Text, { style: styles.statText }, `${selectedImage.name}`), React.createElement(Text, { style: styles.statText }, `מהלכים: ${moves}`), React.createElement(Text, { style: styles.statText }, `זמן: ${gameTime.toFixed(1)}s`) ) ), // Preview toggle React.createElement(TouchableOpacity, { style: styles.previewToggle, onPress: this.togglePreview }, React.createElement(Text, { style: styles.previewToggleText }, showPreview ? '🙈 הסתר תצוגה מקדימה' : '👁️ הצג תצוגה מקדימה' ) ), // Preview image (if enabled) showPreview && React.createElement(View, { style: styles.gamePreview }, React.createElement(Image, { source: { uri: selectedImage.uri }, style: styles.gamePreviewImage, resizeMode: 'cover' }) ), // Puzzle board React.createElement(View, { style: styles.puzzleBoard }, ...this.renderPuzzleGrid() ), isComplete && React.createElement(View, { style: styles.completeIndicator }, React.createElement(Text, { style: styles.completeText }, '🎉 פאזל הושלם!') ), React.createElement(View, { style: styles.controls }, React.createElement(TouchableOpacity, { style: styles.controlButton, onPress: this.resetGame }, React.createElement(Text, { style: styles.controlButtonText }, '🔄 פאזל חדש') ), React.createElement(TouchableOpacity, { style: [styles.controlButton, styles.menuButton], onPress: this.goToMenu }, React.createElement(Text, { style: styles.controlButtonText }, '🏠 תפריט') ) ) ); } renderPuzzleGrid() { const { pieces, gridSize } = this.state; const rows = []; if (!pieces || pieces.length === 0) { return [React.createElement(Text, { key: 'loading', style: { textAlign: 'center', padding: 20 } }, 'טוען פאזל...')]; } for (let i = 0; i < gridSize; i++) { const rowPieces = []; for (let j = 0; j < gridSize; j++) { const index = i * gridSize + j; if (index < pieces.length) { rowPieces.push(this.renderPiece(pieces[index], index)); } } rows.push( React.createElement(View, { key: i, style: this.getImagePuzzleStyles().puzzleRow }, ...rowPieces) ); } return rows; } getImagePuzzleStyles() { const { width } = Dimensions.get('window'); const boardSize = Math.min(width - 60, 320); const pieceSize = (boardSize - 10) / this.state.gridSize; // Reduced padding for tighter fit const previewSize = 120; return StyleSheet.create({ menuContainer: { backgroundColor: '#f8f9fa', flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, minHeight: 400 }, title: { fontSize: 28, fontWeight: 'bold', color: '#2c3e50', textAlign: 'center', marginBottom: 10, }, subtitle: { fontSize: 16, color: '#7f8c8d', textAlign: 'center', marginBottom: 20, }, imageSelector: { backgroundColor: 'white', padding: 15, borderRadius: 15, marginBottom: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, width: '100%', }, selectorTitle: { fontSize: 16, fontWeight: 'bold', color: '#2c3e50', textAlign: 'center', marginBottom: 10, }, imageGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-around', }, imageOption: { backgroundColor: '#ecf0f1', padding: 8, borderRadius: 10, margin: 0, alignItems: 'center', minWidth: 70, borderWidth: 2, borderColor: 'transparent', }, selectedImageOption: { borderColor: '#16a085', backgroundColor: '#e8f8f5', }, imageEmoji: { fontSize: 20, marginBottom: 3, }, imageName: { fontSize: 10, color: '#2c3e50', textAlign: 'center', }, gameInfo: { backgroundColor: 'white', padding: 20, borderRadius: 15, marginBottom: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, width: '100%', }, infoText: { fontSize: 16, color: '#2c3e50', textAlign: 'center', marginBottom: 8, }, previewContainer: { backgroundColor: 'white', padding: 15, borderRadius: 15, marginBottom: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, alignItems: 'center', }, previewTitle: { fontSize: 14, fontWeight: 'bold', color: '#2c3e50', marginBottom: 10, }, previewImageContainer: { width: previewSize, height: previewSize, borderRadius: 10, overflow: 'hidden', backgroundColor: '#ecf0f1', justifyContent: 'center', alignItems: 'center', }, previewImage: { width: previewSize, height: previewSize, }, loadingText: { color: '#7f8c8d', fontSize: 12, }, errorText: { color: '#e74c3c', fontSize: 12, textAlign: 'center', }, startButton: { backgroundColor: '#16a085', paddingVertical: 15, paddingHorizontal: 30, borderRadius: 25, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, elevation: 5, }, disabledButton: { backgroundColor: '#95a5a6', }, buttonText: { color: 'white', fontSize: 18, fontWeight: 'bold', textAlign: 'center', }, gameContainer: { backgroundColor: '#f8f9fa', flex: 1, padding: 15, }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 15, backgroundColor: 'white', padding: 15, borderRadius: 10, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 2, elevation: 2, }, gameTitle: { fontSize: 20, fontWeight: 'bold', color: '#2c3e50', }, stats: { alignItems: 'flex-end', }, statText: { fontSize: 14, color: '#7f8c8d', marginBottom: 2, }, previewToggle: { backgroundColor: '#16a085', padding: 10, borderRadius: 8, alignItems: 'center', marginBottom: 10, }, previewToggleText: { color: 'white', fontSize: 14, fontWeight: 'bold', }, gamePreview: { alignItems: 'center', marginBottom: 15, }, gamePreviewImage: { width: 100, height: 100, borderRadius: 10, }, puzzleBoard: { width: boardSize, height: boardSize, backgroundColor: 'white', borderRadius: 15, padding: 5, // Reduced padding for tighter pieces alignSelf: 'center', marginBottom: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, puzzleRow: { flexDirection: 'row', justifyContent: 'space-around', marginBottom: 1, // Minimal gap between rows }, puzzlePiece: { width: pieceSize, height: pieceSize, margin: 0.5, // Minimal margin for almost no gap borderRadius: 2, // Smaller radius for tighter fit overflow: 'hidden', backgroundColor: '#ecf0f1', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 2, elevation: 2, }, emptyPiece: { backgroundColor: 'transparent', shadowOpacity: 0, elevation: 0, }, pieceContainer: { flex: 1, overflow: 'hidden', }, pieceImage: { width: boardSize - 10, height: boardSize - 10, }, completeIndicator: { backgroundColor: '#16a085', padding: 12, borderRadius: 10, alignItems: 'center', marginBottom: 15, }, completeText: { color: 'white', fontSize: 18, fontWeight: 'bold', }, controls: { flexDirection: 'row', justifyContent: 'space-around', marginTop: 10, }, controlButton: { backgroundColor: '#16a085', paddingVertical: 12, paddingHorizontal: 20, borderRadius: 20, minWidth: 120, alignItems: 'center', }, menuButton: { backgroundColor: '#95a5a6', }, controlButtonText: { color: 'white', fontSize: 14, fontWeight: 'bold', }, }); } render() { console.log('🎨 ImagePuzzleGameComponent rendering...', this.state.currentScreen); return this.state.currentScreen === 'menu' ? this.renderMenu() : this.renderGame(); } } module.exports = ImagePuzzleGameComponent; console.log('✅ ImagePuzzleGameComponent loaded successfully - v4.0.1 Better Images & No Gaps!');