advanced-games-library
Version:
Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes
761 lines (690 loc) • 23.2 kB
JavaScript
/**
* 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!');