react-native-games 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -368
- package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -167
- package/lib/module/games/balloon-blaster/BalloonBlaster.js.map +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js +1 -182
- package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js.map +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterService.js +1 -318
- package/lib/module/games/balloon-blaster/BalloonBlasterStore.js +1 -183
- package/lib/module/games/balloon-blaster/components/BalloonComponent.js +1 -237
- package/lib/module/games/balloon-blaster/components/GameArea.js +1 -156
- package/lib/module/games/balloon-blaster/components/GameBackground.js +1 -476
- package/lib/module/games/balloon-blaster/components/ScoreBoard.js +1 -112
- package/lib/module/games/balloon-blaster/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/balloon-blaster/components/index.js +1 -7
- package/lib/module/games/candy-crush/CandyCrush.js +1 -131
- package/lib/module/games/candy-crush/CandyCrush.js.map +1 -1
- package/lib/module/games/candy-crush/CandyCrushConstants.js +1 -125
- package/lib/module/games/candy-crush/CandyCrushConstants.js.map +1 -1
- package/lib/module/games/candy-crush/CandyCrushService.js +1 -370
- package/lib/module/games/candy-crush/CandyCrushStore.js +1 -303
- package/lib/module/games/candy-crush/components/CandyItem.js +1 -191
- package/lib/module/games/candy-crush/components/GameBackground.js +1 -85
- package/lib/module/games/candy-crush/components/GameGrid.js +1 -314
- package/lib/module/games/candy-crush/components/ScoreBoard.js +1 -79
- package/lib/module/games/candy-crush/components/index.js +1 -7
- package/lib/module/games/candy-crush/index.js +1 -6
- package/lib/module/games/colors-sort/ColorsSort.js +1 -143
- package/lib/module/games/colors-sort/ColorsSort.js.map +1 -1
- package/lib/module/games/colors-sort/ColorsSortConstants.js +1 -72
- package/lib/module/games/colors-sort/ColorsSortConstants.js.map +1 -1
- package/lib/module/games/colors-sort/ColorsSortService.js +1 -255
- package/lib/module/games/colors-sort/ColorsSortStore.js +1 -257
- package/lib/module/games/colors-sort/ColorsSortStore.js.map +1 -1
- package/lib/module/games/colors-sort/components/ColorContainer.js +1 -140
- package/lib/module/games/colors-sort/components/GameBackground.js +1 -135
- package/lib/module/games/colors-sort/components/ScoreBoard.js +1 -70
- package/lib/module/games/colors-sort/components/index.js +1 -6
- package/lib/module/games/dino-jump/DinoJump.js +1 -209
- package/lib/module/games/dino-jump/DinoJump.js.map +1 -1
- package/lib/module/games/dino-jump/DinoJumpConstants.js +1 -189
- package/lib/module/games/dino-jump/DinoJumpConstants.js.map +1 -1
- package/lib/module/games/dino-jump/DinoJumpService.js +1 -270
- package/lib/module/games/dino-jump/DinoJumpStore.js +1 -381
- package/lib/module/games/dino-jump/components/DinoSprite.js +1 -418
- package/lib/module/games/dino-jump/components/DinoSprite.js.map +1 -1
- package/lib/module/games/dino-jump/components/GameArea.js +1 -68
- package/lib/module/games/dino-jump/components/GameBackground.js +1 -444
- package/lib/module/games/dino-jump/components/ObstacleSprite.js +1 -306
- package/lib/module/games/dino-jump/components/ScoreBoard.js +1 -105
- package/lib/module/games/dino-jump/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/dino-jump/components/StarSprite.js +1 -45
- package/lib/module/games/dino-jump/components/index.js +1 -9
- package/lib/module/games/flappy-bird/FlappyBird.js +1 -126
- package/lib/module/games/flappy-bird/FlappyBird.js.map +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdConstants.js +1 -90
- package/lib/module/games/flappy-bird/FlappyBirdConstants.js.map +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdStore.js +1 -300
- package/lib/module/games/flappy-bird/components/Bird.js +1 -87
- package/lib/module/games/flappy-bird/components/GameArea.js +1 -87
- package/lib/module/games/flappy-bird/components/GameBackground.js +1 -79
- package/lib/module/games/flappy-bird/components/Pipes.js +1 -172
- package/lib/module/games/flappy-bird/components/ScoreBoard.js +1 -73
- package/lib/module/games/flappy-bird/components/index.js +1 -8
- package/lib/module/games/fruit-merger/FruitMerger.js +1 -120
- package/lib/module/games/fruit-merger/FruitMerger.js.map +1 -1
- package/lib/module/games/fruit-merger/FruitMergerConstants.js +1 -119
- package/lib/module/games/fruit-merger/FruitMergerConstants.js.map +1 -1
- package/lib/module/games/fruit-merger/FruitMergerService.js +1 -13
- package/lib/module/games/fruit-merger/FruitMergerStore.js +1 -315
- package/lib/module/games/fruit-merger/components/FruitItem.js +1 -102
- package/lib/module/games/fruit-merger/components/GameArea.js +1 -103
- package/lib/module/games/fruit-merger/components/GameBackground.js +1 -498
- package/lib/module/games/fruit-merger/components/ScoreBoard.js +1 -58
- package/lib/module/games/fruit-merger/components/index.js +1 -7
- package/lib/module/games/fruit-ninja/FruitNinja.js +1 -134
- package/lib/module/games/fruit-ninja/FruitNinja.js.map +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaConstants.js +1 -148
- package/lib/module/games/fruit-ninja/FruitNinjaConstants.js.map +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaService.js +1 -311
- package/lib/module/games/fruit-ninja/FruitNinjaStore.js +1 -191
- package/lib/module/games/fruit-ninja/FruitNinjaStore.js.map +1 -1
- package/lib/module/games/fruit-ninja/components/FruitComponent.js +1 -99
- package/lib/module/games/fruit-ninja/components/GameArea.js +1 -215
- package/lib/module/games/fruit-ninja/components/GameBackground.js +1 -1267
- package/lib/module/games/fruit-ninja/components/ScoreBoard.js +1 -92
- package/lib/module/games/fruit-ninja/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/fruit-ninja/components/index.js +1 -7
- package/lib/module/games/game-2048/Game2048.js +1 -149
- package/lib/module/games/game-2048/Game2048.js.map +1 -1
- package/lib/module/games/game-2048/Game2048Constants.js +1 -263
- package/lib/module/games/game-2048/Game2048Constants.js.map +1 -1
- package/lib/module/games/game-2048/Game2048Service.js +1 -457
- package/lib/module/games/game-2048/Game2048Store.js +1 -236
- package/lib/module/games/game-2048/components/GameBackground.js +1 -247
- package/lib/module/games/game-2048/components/GameGrid.js +1 -139
- package/lib/module/games/game-2048/components/GameTile.js +1 -72
- package/lib/module/games/game-2048/components/ScoreBoard.js +1 -52
- package/lib/module/games/game-2048/components/index.js +1 -7
- package/lib/module/games/maze-runner/MazeRunner.js +1 -267
- package/lib/module/games/maze-runner/MazeRunner.js.map +1 -1
- package/lib/module/games/maze-runner/MazeRunnerConstants.js +1 -100
- package/lib/module/games/maze-runner/MazeRunnerConstants.js.map +1 -1
- package/lib/module/games/maze-runner/MazeRunnerService.js +1 -586
- package/lib/module/games/maze-runner/components/EnhancedBallComponent.js +1 -150
- package/lib/module/games/maze-runner/components/EnhancedGameArea.js +1 -370
- package/lib/module/games/maze-runner/components/GameBackground.js +1 -175
- package/lib/module/games/maze-runner/components/ScoreBoard.js +1 -61
- package/lib/module/games/maze-runner/components/SkiaPipeComponent.js +1 -209
- package/lib/module/games/maze-runner/components/StaticGameBackground.js +1 -169
- package/lib/module/games/maze-runner/components/WallComponent.js +1 -91
- package/lib/module/games/maze-runner/components/index.js +1 -8
- package/lib/module/games/popit-fidget/PopitFidget.js +1 -285
- package/lib/module/games/popit-fidget/PopitFidget.js.map +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetConstants.js +1 -113
- package/lib/module/games/popit-fidget/PopitFidgetConstants.js.map +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetService.js +1 -132
- package/lib/module/games/popit-fidget/PopitFidgetStore.js +1 -125
- package/lib/module/games/popit-fidget/components/BubbleComponent.js +1 -198
- package/lib/module/games/popit-fidget/components/FidgetGrid.js +1 -165
- package/lib/module/games/popit-fidget/components/GameBackground.js +1 -177
- package/lib/module/games/popit-fidget/components/ScoreBoard.js +1 -61
- package/lib/module/games/popit-fidget/components/index.js +1 -7
- package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -159
- package/lib/module/games/sliding-numbers/SlidingNumbers.js.map +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js +1 -207
- package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js.map +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersService.js +1 -248
- package/lib/module/games/sliding-numbers/SlidingNumbersStore.js +1 -274
- package/lib/module/games/sliding-numbers/components/GameBackground.js +1 -259
- package/lib/module/games/sliding-numbers/components/NumbersGrid.js +1 -174
- package/lib/module/games/sliding-numbers/components/NumbersTile.js +1 -116
- package/lib/module/games/sliding-numbers/components/ScoreBoard.js +1 -64
- package/lib/module/games/sliding-numbers/components/index.js +1 -7
- package/lib/module/games/snake/Snake.js +1 -189
- package/lib/module/games/snake/Snake.js.map +1 -1
- package/lib/module/games/snake/SnakeConstants.js +1 -138
- package/lib/module/games/snake/SnakeConstants.js.map +1 -1
- package/lib/module/games/snake/SnakeService.js +1 -148
- package/lib/module/games/snake/SnakeStore.js +1 -182
- package/lib/module/games/snake/components/GameBackground.js +1 -221
- package/lib/module/games/snake/components/GameGrid.js +1 -153
- package/lib/module/games/snake/components/ScoreBoard.js +1 -51
- package/lib/module/games/snake/components/index.js +1 -6
- package/lib/module/games/snake/index.js +1 -6
- package/lib/module/games/space-fighter/SpaceFighter.js +1 -165
- package/lib/module/games/space-fighter/SpaceFighter.js.map +1 -1
- package/lib/module/games/space-fighter/SpaceFighterConstants.js +1 -108
- package/lib/module/games/space-fighter/SpaceFighterConstants.js.map +1 -1
- package/lib/module/games/space-fighter/SpaceFighterService.js +1 -326
- package/lib/module/games/space-fighter/SpaceFighterStore.js +1 -209
- package/lib/module/games/space-fighter/components/AsteroidComponent.js +1 -113
- package/lib/module/games/space-fighter/components/GameArea.js +1 -289
- package/lib/module/games/space-fighter/components/GameBackground.js +1 -239
- package/lib/module/games/space-fighter/components/ScoreBoard.js +1 -136
- package/lib/module/games/space-fighter/components/Spacecraft3D.js +1 -202
- package/lib/module/games/space-fighter/components/SpacecraftPath.js +1 -52
- package/lib/module/games/space-fighter/components/index.js +1 -9
- package/lib/module/games/whack-a-mole/WhackAMole.js +1 -270
- package/lib/module/games/whack-a-mole/WhackAMole.js.map +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleConstants.js +1 -115
- package/lib/module/games/whack-a-mole/WhackAMoleConstants.js.map +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleService.js +1 -120
- package/lib/module/games/whack-a-mole/WhackAMoleStore.js +1 -172
- package/lib/module/games/whack-a-mole/components/GameBackground.js +1 -477
- package/lib/module/games/whack-a-mole/components/GameGrid.js +1 -97
- package/lib/module/games/whack-a-mole/components/GameHole.js +1 -196
- package/lib/module/games/whack-a-mole/components/MoleCharacter.js +1 -241
- package/lib/module/games/whack-a-mole/components/ScoreBoard.js +1 -67
- package/lib/module/games/whack-a-mole/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/whack-a-mole/components/index.js +1 -8
- package/lib/module/helpers/AnimationFrame.js +1 -120
- package/lib/module/helpers/AnimationTracker.js +1 -89
- package/lib/module/helpers/ErrorHandler.js +1 -269
- package/lib/module/helpers/GameControlButton.js +1 -219
- package/lib/module/helpers/GameOverModal.js +1 -144
- package/lib/module/helpers/GameOverModal.js.map +1 -1
- package/lib/module/helpers/GameSettingsModal.js +1 -287
- package/lib/module/helpers/ParticleBlast.js +1 -134
- package/lib/module/helpers/ScoreBoardContainer.js +1 -34
- package/lib/module/helpers/index.js +1 -12
- package/lib/module/index.js +1 -22
- package/lib/module/services/GamesConstants.js +1 -178
- package/lib/module/services/GamesService.js +1 -112
- package/lib/module/services/GamesService.js.map +1 -1
- package/lib/module/services/HapticsService.js +1 -77
- package/lib/module/services/SoundsService.js +1 -302
- package/lib/module/services/UtilsService.js +1 -32
- package/lib/typescript/src/games/balloon-blaster/BalloonBlaster.d.ts.map +1 -1
- package/lib/typescript/src/games/balloon-blaster/BalloonBlasterConstants.d.ts +1 -1
- package/lib/typescript/src/games/balloon-blaster/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/games/candy-crush/CandyCrushConstants.d.ts +7 -7
- package/lib/typescript/src/games/colors-sort/ColorsSort.d.ts.map +1 -1
- package/lib/typescript/src/games/colors-sort/ColorsSortStore.d.ts.map +1 -1
- package/lib/typescript/src/games/dino-jump/DinoJump.d.ts.map +1 -1
- package/lib/typescript/src/games/dino-jump/components/DinoSprite.d.ts.map +1 -1
- package/lib/typescript/src/games/flappy-bird/FlappyBird.d.ts.map +1 -1
- package/lib/typescript/src/games/flappy-bird/FlappyBirdConstants.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-merger/FruitMerger.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-merger/FruitMergerConstants.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-ninja/FruitNinja.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-ninja/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/games/game-2048/Game2048.d.ts.map +1 -1
- package/lib/typescript/src/games/maze-runner/MazeRunner.d.ts.map +1 -1
- package/lib/typescript/src/games/popit-fidget/PopitFidget.d.ts.map +1 -1
- package/lib/typescript/src/games/sliding-numbers/SlidingNumbers.d.ts.map +1 -1
- package/lib/typescript/src/games/space-fighter/SpaceFighter.d.ts.map +1 -1
- package/lib/typescript/src/games/whack-a-mole/WhackAMole.d.ts.map +1 -1
- package/lib/typescript/src/games/whack-a-mole/WhackAMoleConstants.d.ts +1 -1
- package/lib/typescript/src/games/whack-a-mole/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/helpers/GameOverModal.d.ts +3 -0
- package/lib/typescript/src/helpers/GameOverModal.d.ts.map +1 -1
- package/lib/typescript/src/services/GamesConstants.d.ts +7 -7
- package/package.json +2 -2
|
@@ -1,586 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
export class MazeRunnerService {
|
|
4
|
-
gameTimer = null;
|
|
5
|
-
constructor(gridSize = 15, gameAreaWidth = 350, gameAreaHeight = 500, gameDuration = 120) {
|
|
6
|
-
this.gameConfig = {
|
|
7
|
-
GRID_SIZE: gridSize,
|
|
8
|
-
CELL_SIZE: Math.min(gameAreaWidth, gameAreaHeight) / gridSize,
|
|
9
|
-
BALL_SIZE: 18,
|
|
10
|
-
PIPE_WIDTH: 8,
|
|
11
|
-
GAME_AREA_WIDTH: gameAreaWidth,
|
|
12
|
-
GAME_AREA_HEIGHT: gameAreaHeight,
|
|
13
|
-
COLLISION_TOLERANCE: 5,
|
|
14
|
-
SCORE_PER_COMPLETION: 100,
|
|
15
|
-
TIME_BONUS_MULTIPLIER: 10,
|
|
16
|
-
GAME_DURATION: gameDuration
|
|
17
|
-
};
|
|
18
|
-
this.gameState = {
|
|
19
|
-
isPlaying: false,
|
|
20
|
-
isPaused: false,
|
|
21
|
-
score: 0,
|
|
22
|
-
timeElapsed: 0,
|
|
23
|
-
// Start from 0 and count up
|
|
24
|
-
ballPosition: {
|
|
25
|
-
x: 0,
|
|
26
|
-
y: 0
|
|
27
|
-
},
|
|
28
|
-
targetPosition: {
|
|
29
|
-
x: 0,
|
|
30
|
-
y: 0
|
|
31
|
-
},
|
|
32
|
-
isCompleted: false,
|
|
33
|
-
gameOver: false
|
|
34
|
-
};
|
|
35
|
-
this.maze = [];
|
|
36
|
-
this.walls = [];
|
|
37
|
-
this.initializeMaze();
|
|
38
|
-
// Generate initial maze on service creation
|
|
39
|
-
this.generateMazeWithBacktracking();
|
|
40
|
-
this.generateWalls();
|
|
41
|
-
this.setStartAndEndPositions();
|
|
42
|
-
}
|
|
43
|
-
setGameStateChangeCallback(callback) {
|
|
44
|
-
this.onGameStateChange = callback;
|
|
45
|
-
}
|
|
46
|
-
notifyGameStateChange() {
|
|
47
|
-
if (this.onGameStateChange) {
|
|
48
|
-
this.onGameStateChange({
|
|
49
|
-
...this.gameState
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
initializeMaze() {
|
|
54
|
-
// Initialize maze grid
|
|
55
|
-
this.maze = [];
|
|
56
|
-
for (let y = 0; y < this.gameConfig.GRID_SIZE; y++) {
|
|
57
|
-
this.maze[y] = [];
|
|
58
|
-
for (let x = 0; x < this.gameConfig.GRID_SIZE; x++) {
|
|
59
|
-
if (this.maze[y]) {
|
|
60
|
-
this.maze[y][x] = {
|
|
61
|
-
x,
|
|
62
|
-
y,
|
|
63
|
-
walls: {
|
|
64
|
-
top: true,
|
|
65
|
-
right: true,
|
|
66
|
-
bottom: true,
|
|
67
|
-
left: true
|
|
68
|
-
},
|
|
69
|
-
visited: false
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
generateNewMaze() {
|
|
76
|
-
this.initializeMaze();
|
|
77
|
-
this.generateMazeWithBacktracking();
|
|
78
|
-
this.generateWalls();
|
|
79
|
-
this.setStartAndEndPositions();
|
|
80
|
-
|
|
81
|
-
// Reset game state for new maze
|
|
82
|
-
this.gameState.isCompleted = false;
|
|
83
|
-
this.gameState.isPlaying = false; // Don't auto-start, let user click start
|
|
84
|
-
this.gameState.isPaused = false;
|
|
85
|
-
this.gameState.gameOver = false;
|
|
86
|
-
this.gameState.score = 0; // Reset score for new maze
|
|
87
|
-
this.gameState.timeElapsed = 0; // Start from 0 and count up
|
|
88
|
-
|
|
89
|
-
this.notifyGameStateChange();
|
|
90
|
-
}
|
|
91
|
-
generateMazeWithBacktracking() {
|
|
92
|
-
// Create a proper maze using recursive backtracking algorithm
|
|
93
|
-
const stack = [];
|
|
94
|
-
const startCell = this.maze[0]?.[0];
|
|
95
|
-
if (startCell) {
|
|
96
|
-
startCell.visited = true;
|
|
97
|
-
stack.push(startCell);
|
|
98
|
-
}
|
|
99
|
-
while (stack.length > 0) {
|
|
100
|
-
const current = stack[stack.length - 1];
|
|
101
|
-
const neighbors = current ? this.getUnvisitedNeighbors(current) : [];
|
|
102
|
-
if (neighbors.length > 0) {
|
|
103
|
-
// Completely random neighbor selection for unpredictable maze generation
|
|
104
|
-
const next = neighbors[Math.floor(Math.random() * neighbors.length)];
|
|
105
|
-
if (current && next) this.removeWallBetween(current, next);
|
|
106
|
-
if (next) {
|
|
107
|
-
next.visited = true;
|
|
108
|
-
stack.push(next);
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
stack.pop();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Only minimal simplification to ensure connectivity
|
|
116
|
-
this.ensureConnectivity();
|
|
117
|
-
}
|
|
118
|
-
ensureConnectivity() {
|
|
119
|
-
// Only ensure basic connectivity without creating obvious paths
|
|
120
|
-
// Check if there's a path from start to end using BFS
|
|
121
|
-
if (!this.hasPathFromStartToEnd()) {
|
|
122
|
-
// If no path exists, create minimal connections
|
|
123
|
-
this.createMinimalPath();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
hasPathFromStartToEnd() {
|
|
127
|
-
// Use BFS to check if there's a path from start (0,0) to end (GRID_SIZE-1, GRID_SIZE-1)
|
|
128
|
-
const visited = Array(this.gameConfig.GRID_SIZE).fill(null).map(() => Array(this.gameConfig.GRID_SIZE).fill(false));
|
|
129
|
-
const queue = [{
|
|
130
|
-
x: 0,
|
|
131
|
-
y: 0
|
|
132
|
-
}];
|
|
133
|
-
if (visited[0]) visited[0][0] = true;
|
|
134
|
-
while (queue.length > 0) {
|
|
135
|
-
const current = queue.shift();
|
|
136
|
-
if (current.x === this.gameConfig.GRID_SIZE - 1 && current.y === this.gameConfig.GRID_SIZE - 1) {
|
|
137
|
-
return true; // Found path to end
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Check all four directions
|
|
141
|
-
const directions = [{
|
|
142
|
-
dx: 0,
|
|
143
|
-
dy: -1,
|
|
144
|
-
wall: 'top'
|
|
145
|
-
},
|
|
146
|
-
// Up
|
|
147
|
-
{
|
|
148
|
-
dx: 1,
|
|
149
|
-
dy: 0,
|
|
150
|
-
wall: 'right'
|
|
151
|
-
},
|
|
152
|
-
// Right
|
|
153
|
-
{
|
|
154
|
-
dx: 0,
|
|
155
|
-
dy: 1,
|
|
156
|
-
wall: 'bottom'
|
|
157
|
-
},
|
|
158
|
-
// Down
|
|
159
|
-
{
|
|
160
|
-
dx: -1,
|
|
161
|
-
dy: 0,
|
|
162
|
-
wall: 'left'
|
|
163
|
-
} // Left
|
|
164
|
-
];
|
|
165
|
-
for (const dir of directions) {
|
|
166
|
-
const newX = current.x + dir.dx;
|
|
167
|
-
const newY = current.y + dir.dy;
|
|
168
|
-
if (newX >= 0 && newX < this.gameConfig.GRID_SIZE && newY >= 0 && newY < this.gameConfig.GRID_SIZE && !visited[newY]?.[newX]) {
|
|
169
|
-
// Check if there's no wall blocking this direction
|
|
170
|
-
const currentCell = this.maze[current.y]?.[current.x];
|
|
171
|
-
if (currentCell && !currentCell.walls[dir.wall]) {
|
|
172
|
-
if (visited[newY]) visited[newY][newX] = true;
|
|
173
|
-
queue.push({
|
|
174
|
-
x: newX,
|
|
175
|
-
y: newY
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return false; // No path found
|
|
182
|
-
}
|
|
183
|
-
createMinimalPath() {
|
|
184
|
-
// Create a winding, unpredictable path from start to end
|
|
185
|
-
// This should only be called if no path exists after maze generation
|
|
186
|
-
let currentX = 0;
|
|
187
|
-
let currentY = 0;
|
|
188
|
-
const endX = this.gameConfig.GRID_SIZE - 1;
|
|
189
|
-
const endY = this.gameConfig.GRID_SIZE - 1;
|
|
190
|
-
const visited = new Set();
|
|
191
|
-
while (currentX !== endX || currentY !== endY) {
|
|
192
|
-
const key = `${currentX},${currentY}`;
|
|
193
|
-
if (visited.has(key)) {
|
|
194
|
-
// Prevent infinite loops - break out and create direct path
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
visited.add(key);
|
|
198
|
-
|
|
199
|
-
// Randomly choose direction, but prefer moving towards target occasionally
|
|
200
|
-
const possibleMoves = [];
|
|
201
|
-
if (currentX < endX) possibleMoves.push('right');
|
|
202
|
-
if (currentY < endY) possibleMoves.push('down');
|
|
203
|
-
if (currentX > 0) possibleMoves.push('left');
|
|
204
|
-
if (currentY > 0) possibleMoves.push('up');
|
|
205
|
-
if (possibleMoves.length === 0) break;
|
|
206
|
-
|
|
207
|
-
// 60% chance to move towards target, 40% chance random direction
|
|
208
|
-
let direction;
|
|
209
|
-
if (Math.random() < 0.6 && (currentX !== endX || currentY !== endY)) {
|
|
210
|
-
// Move towards target
|
|
211
|
-
if (currentX < endX && Math.random() < 0.5) {
|
|
212
|
-
direction = 'right';
|
|
213
|
-
} else if (currentY < endY) {
|
|
214
|
-
direction = 'down';
|
|
215
|
-
} else if (currentX < endX) {
|
|
216
|
-
direction = 'right';
|
|
217
|
-
} else {
|
|
218
|
-
direction = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
|
|
219
|
-
}
|
|
220
|
-
} else {
|
|
221
|
-
// Random direction
|
|
222
|
-
direction = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Remove wall in chosen direction
|
|
226
|
-
const cell = this.maze[currentY]?.[currentX];
|
|
227
|
-
if (direction === 'right' && currentX < this.gameConfig.GRID_SIZE - 1 && cell) {
|
|
228
|
-
cell.walls.right = false;
|
|
229
|
-
const rightCell = this.maze[currentY]?.[currentX + 1];
|
|
230
|
-
if (rightCell) rightCell.walls.left = false;
|
|
231
|
-
currentX++;
|
|
232
|
-
} else if (direction === 'down' && currentY < this.gameConfig.GRID_SIZE - 1 && cell) {
|
|
233
|
-
cell.walls.bottom = false;
|
|
234
|
-
const bottomCell = this.maze[currentY + 1]?.[currentX];
|
|
235
|
-
if (bottomCell) bottomCell.walls.top = false;
|
|
236
|
-
currentY++;
|
|
237
|
-
} else if (direction === 'left' && currentX > 0 && cell) {
|
|
238
|
-
cell.walls.left = false;
|
|
239
|
-
const leftCell = this.maze[currentY]?.[currentX - 1];
|
|
240
|
-
if (leftCell) leftCell.walls.right = false;
|
|
241
|
-
currentX--;
|
|
242
|
-
} else if (direction === 'up' && currentY > 0 && cell) {
|
|
243
|
-
cell.walls.top = false;
|
|
244
|
-
const topCell = this.maze[currentY - 1]?.[currentX];
|
|
245
|
-
if (topCell) topCell.walls.bottom = false;
|
|
246
|
-
currentY--;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
getUnvisitedNeighbors(cell) {
|
|
251
|
-
const neighbors = [];
|
|
252
|
-
const {
|
|
253
|
-
x,
|
|
254
|
-
y
|
|
255
|
-
} = cell;
|
|
256
|
-
|
|
257
|
-
// Top
|
|
258
|
-
if (y > 0 && this.maze[y - 1]?.[x] && !this.maze[y - 1]?.[x]?.visited) {
|
|
259
|
-
const topCell = this.maze[y - 1]?.[x];
|
|
260
|
-
if (topCell) {
|
|
261
|
-
neighbors.push(topCell);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// Right
|
|
265
|
-
if (x < this.gameConfig.GRID_SIZE - 1 && this.maze[y]?.[x + 1] && !this.maze[y]?.[x + 1]?.visited) {
|
|
266
|
-
const rightCell = this.maze[y]?.[x + 1];
|
|
267
|
-
if (rightCell) {
|
|
268
|
-
neighbors.push(rightCell);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// Bottom
|
|
272
|
-
if (y < this.gameConfig.GRID_SIZE - 1 && this.maze[y + 1]?.[x] && !this.maze[y + 1]?.[x]?.visited) {
|
|
273
|
-
const bottomCell = this.maze[y + 1]?.[x];
|
|
274
|
-
if (bottomCell) {
|
|
275
|
-
neighbors.push(bottomCell);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Left
|
|
279
|
-
if (x > 0 && this.maze[y]?.[x - 1] && !this.maze[y]?.[x - 1]?.visited) {
|
|
280
|
-
const leftCell = this.maze[y]?.[x - 1];
|
|
281
|
-
if (leftCell) {
|
|
282
|
-
neighbors.push(leftCell);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return neighbors;
|
|
286
|
-
}
|
|
287
|
-
removeWallBetween(current, next) {
|
|
288
|
-
const dx = current.x - next.x;
|
|
289
|
-
const dy = current.y - next.y;
|
|
290
|
-
if (dx === 1) {
|
|
291
|
-
// Next is to the left
|
|
292
|
-
current.walls.left = false;
|
|
293
|
-
next.walls.right = false;
|
|
294
|
-
} else if (dx === -1) {
|
|
295
|
-
// Next is to the right
|
|
296
|
-
current.walls.right = false;
|
|
297
|
-
next.walls.left = false;
|
|
298
|
-
} else if (dy === 1) {
|
|
299
|
-
// Next is above
|
|
300
|
-
current.walls.top = false;
|
|
301
|
-
next.walls.bottom = false;
|
|
302
|
-
} else if (dy === -1) {
|
|
303
|
-
// Next is below
|
|
304
|
-
current.walls.bottom = false;
|
|
305
|
-
next.walls.top = false;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
generateWalls() {
|
|
309
|
-
this.walls = [];
|
|
310
|
-
const wallThickness = 4;
|
|
311
|
-
const cellSize = this.gameConfig.CELL_SIZE;
|
|
312
|
-
const mazeWidth = this.gameConfig.GRID_SIZE * cellSize;
|
|
313
|
-
const mazeHeight = this.gameConfig.GRID_SIZE * cellSize;
|
|
314
|
-
|
|
315
|
-
// Create outer boundary walls (solid perimeter around the maze area)
|
|
316
|
-
// Top boundary
|
|
317
|
-
this.walls.push({
|
|
318
|
-
x: 0,
|
|
319
|
-
y: 0,
|
|
320
|
-
width: mazeWidth,
|
|
321
|
-
height: wallThickness,
|
|
322
|
-
type: 'horizontal'
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
// Bottom boundary
|
|
326
|
-
this.walls.push({
|
|
327
|
-
x: 0,
|
|
328
|
-
y: mazeHeight - wallThickness,
|
|
329
|
-
width: mazeWidth,
|
|
330
|
-
height: wallThickness,
|
|
331
|
-
type: 'horizontal'
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Left boundary
|
|
335
|
-
this.walls.push({
|
|
336
|
-
x: 0,
|
|
337
|
-
y: 0,
|
|
338
|
-
width: wallThickness,
|
|
339
|
-
height: mazeHeight,
|
|
340
|
-
type: 'vertical'
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// Right boundary
|
|
344
|
-
this.walls.push({
|
|
345
|
-
x: mazeWidth - wallThickness,
|
|
346
|
-
y: 0,
|
|
347
|
-
width: wallThickness,
|
|
348
|
-
height: mazeHeight,
|
|
349
|
-
type: 'vertical'
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Generate internal walls based on maze cells
|
|
353
|
-
// Only add walls where they are needed to avoid duplicates
|
|
354
|
-
for (let y = 0; y < this.gameConfig.GRID_SIZE; y++) {
|
|
355
|
-
for (let x = 0; x < this.gameConfig.GRID_SIZE; x++) {
|
|
356
|
-
const cell = this.maze[y]?.[x];
|
|
357
|
-
if (!cell) continue;
|
|
358
|
-
const cellX = x * cellSize;
|
|
359
|
-
const cellY = y * cellSize;
|
|
360
|
-
|
|
361
|
-
// Add horizontal walls (only add each wall once)
|
|
362
|
-
// Add top wall if this cell has a top wall and we're not at the top edge
|
|
363
|
-
if (cell.walls.top && y > 0) {
|
|
364
|
-
this.walls.push({
|
|
365
|
-
x: cellX,
|
|
366
|
-
y: cellY,
|
|
367
|
-
width: cellSize,
|
|
368
|
-
height: wallThickness,
|
|
369
|
-
type: 'horizontal'
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Add vertical walls (only add each wall once)
|
|
374
|
-
// Add left wall if this cell has a left wall and we're not at the left edge
|
|
375
|
-
if (cell.walls.left && x > 0) {
|
|
376
|
-
this.walls.push({
|
|
377
|
-
x: cellX,
|
|
378
|
-
y: cellY,
|
|
379
|
-
width: wallThickness,
|
|
380
|
-
height: cellSize,
|
|
381
|
-
type: 'vertical'
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
setStartAndEndPositions() {
|
|
388
|
-
const cellSize = this.gameConfig.CELL_SIZE;
|
|
389
|
-
|
|
390
|
-
// Start position (top-left)
|
|
391
|
-
this.gameState.ballPosition = {
|
|
392
|
-
x: cellSize / 2,
|
|
393
|
-
y: cellSize / 2
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// End position (bottom-right)
|
|
397
|
-
this.gameState.targetPosition = {
|
|
398
|
-
x: (this.gameConfig.GRID_SIZE - 1) * cellSize + cellSize / 2,
|
|
399
|
-
y: (this.gameConfig.GRID_SIZE - 1) * cellSize + cellSize / 2
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
updateSettings(gridSize, gameDuration, gameAreaWidth, gameAreaHeight) {
|
|
403
|
-
// Update game configuration with new settings
|
|
404
|
-
const newGameAreaWidth = gameAreaWidth || this.gameConfig.GAME_AREA_WIDTH;
|
|
405
|
-
const newGameAreaHeight = gameAreaHeight || this.gameConfig.GAME_AREA_HEIGHT;
|
|
406
|
-
this.gameConfig.GRID_SIZE = gridSize;
|
|
407
|
-
this.gameConfig.GAME_AREA_WIDTH = newGameAreaWidth;
|
|
408
|
-
this.gameConfig.GAME_AREA_HEIGHT = newGameAreaHeight;
|
|
409
|
-
this.gameConfig.CELL_SIZE = Math.min(newGameAreaWidth, newGameAreaHeight) / gridSize;
|
|
410
|
-
this.gameConfig.GAME_DURATION = gameDuration; // Update game duration
|
|
411
|
-
|
|
412
|
-
// Generate new maze with updated settings
|
|
413
|
-
this.generateNewMaze();
|
|
414
|
-
|
|
415
|
-
// Reset game state
|
|
416
|
-
this.gameState.isPlaying = false;
|
|
417
|
-
this.gameState.isPaused = false;
|
|
418
|
-
this.gameState.timeElapsed = 0; // Start from 0 and count up
|
|
419
|
-
this.gameState.isCompleted = false;
|
|
420
|
-
this.gameState.gameOver = false;
|
|
421
|
-
this.stopTimer();
|
|
422
|
-
this.notifyGameStateChange();
|
|
423
|
-
}
|
|
424
|
-
resetGame() {
|
|
425
|
-
// Generate a completely new maze for fresh gameplay
|
|
426
|
-
this.generateNewMaze();
|
|
427
|
-
|
|
428
|
-
// Reset game state without starting - just prepare for a new game
|
|
429
|
-
this.gameState.isPlaying = false; // Don't auto-start
|
|
430
|
-
this.gameState.isPaused = false;
|
|
431
|
-
this.gameState.score = 0; // Reset score for new game
|
|
432
|
-
this.gameState.timeElapsed = 0; // Start from 0 and count up
|
|
433
|
-
this.gameState.isCompleted = false;
|
|
434
|
-
this.gameState.gameOver = false;
|
|
435
|
-
this.stopTimer(); // Make sure timer is stopped
|
|
436
|
-
this.notifyGameStateChange();
|
|
437
|
-
}
|
|
438
|
-
startGame() {
|
|
439
|
-
// Start the game with current maze (don't regenerate here since resetGame already did)
|
|
440
|
-
this.gameState.ballPosition = {
|
|
441
|
-
x: this.gameConfig.CELL_SIZE / 2,
|
|
442
|
-
y: this.gameConfig.CELL_SIZE / 2
|
|
443
|
-
};
|
|
444
|
-
this.gameState.targetPosition = {
|
|
445
|
-
x: (this.gameConfig.GRID_SIZE - 1) * this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2,
|
|
446
|
-
y: (this.gameConfig.GRID_SIZE - 1) * this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2
|
|
447
|
-
};
|
|
448
|
-
this.gameState.isPlaying = true;
|
|
449
|
-
this.gameState.isPaused = false;
|
|
450
|
-
this.gameState.score = 0; // Reset score when starting new game
|
|
451
|
-
this.gameState.timeElapsed = 0; // Start from 0 and count up
|
|
452
|
-
this.gameState.isCompleted = false;
|
|
453
|
-
this.gameState.gameOver = false;
|
|
454
|
-
this.startTimer();
|
|
455
|
-
this.notifyGameStateChange();
|
|
456
|
-
}
|
|
457
|
-
pauseGame() {
|
|
458
|
-
this.gameState.isPaused = true;
|
|
459
|
-
this.stopTimer();
|
|
460
|
-
this.notifyGameStateChange();
|
|
461
|
-
}
|
|
462
|
-
resumeGame() {
|
|
463
|
-
this.gameState.isPaused = false;
|
|
464
|
-
this.startTimer();
|
|
465
|
-
this.notifyGameStateChange();
|
|
466
|
-
}
|
|
467
|
-
stopGame() {
|
|
468
|
-
this.gameState.isPlaying = false;
|
|
469
|
-
this.gameState.isPaused = false;
|
|
470
|
-
this.stopTimer();
|
|
471
|
-
this.notifyGameStateChange();
|
|
472
|
-
}
|
|
473
|
-
startTimer() {
|
|
474
|
-
this.stopTimer();
|
|
475
|
-
this.gameTimer = setInterval(() => {
|
|
476
|
-
if (!this.gameState.isPaused && this.gameState.isPlaying) {
|
|
477
|
-
this.gameState.timeElapsed += 1; // Count up from 0
|
|
478
|
-
|
|
479
|
-
// No time limit - player can take as long as they need
|
|
480
|
-
// Timer just tracks how long they've been playing
|
|
481
|
-
|
|
482
|
-
this.notifyGameStateChange();
|
|
483
|
-
}
|
|
484
|
-
}, 1000);
|
|
485
|
-
}
|
|
486
|
-
stopTimer() {
|
|
487
|
-
if (this.gameTimer) {
|
|
488
|
-
clearInterval(this.gameTimer);
|
|
489
|
-
this.gameTimer = null;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
updateBallPosition(newPosition) {
|
|
493
|
-
if (!this.gameState.isPlaying || this.gameState.isPaused) {
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Check if the new position is valid (within maze path)
|
|
498
|
-
if (this.isValidPosition(newPosition)) {
|
|
499
|
-
this.gameState.ballPosition = newPosition;
|
|
500
|
-
|
|
501
|
-
// Check if reached target (only if not already completed)
|
|
502
|
-
if (!this.gameState.isCompleted && this.isNearTarget(newPosition)) {
|
|
503
|
-
this.completeLevel();
|
|
504
|
-
}
|
|
505
|
-
this.notifyGameStateChange();
|
|
506
|
-
return true;
|
|
507
|
-
}
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
isValidPosition(position) {
|
|
511
|
-
const {
|
|
512
|
-
x,
|
|
513
|
-
y
|
|
514
|
-
} = position;
|
|
515
|
-
const ballRadius = this.gameConfig.BALL_SIZE / 2;
|
|
516
|
-
|
|
517
|
-
// Check if ball would collide with any wall
|
|
518
|
-
for (const wall of this.walls) {
|
|
519
|
-
if (this.ballCollidesWithWall(x, y, ballRadius, wall)) {
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Check game area boundaries (with some padding)
|
|
525
|
-
const padding = ballRadius + 2;
|
|
526
|
-
if (x - ballRadius < padding || x + ballRadius > this.gameConfig.GAME_AREA_WIDTH - padding || y - ballRadius < padding || y + ballRadius > this.gameConfig.GAME_AREA_HEIGHT - padding) {
|
|
527
|
-
return false;
|
|
528
|
-
}
|
|
529
|
-
return true;
|
|
530
|
-
}
|
|
531
|
-
ballCollidesWithWall(ballX, ballY, ballRadius, wall) {
|
|
532
|
-
// Calculate the closest point on the wall rectangle to the ball center
|
|
533
|
-
const closestX = Math.max(wall.x, Math.min(ballX, wall.x + wall.width));
|
|
534
|
-
const closestY = Math.max(wall.y, Math.min(ballY, wall.y + wall.height));
|
|
535
|
-
|
|
536
|
-
// Calculate the distance from the ball center to this closest point
|
|
537
|
-
const distanceX = ballX - closestX;
|
|
538
|
-
const distanceY = ballY - closestY;
|
|
539
|
-
const distanceSquared = distanceX * distanceX + distanceY * distanceY;
|
|
540
|
-
|
|
541
|
-
// Check if the distance is less than the ball radius
|
|
542
|
-
return distanceSquared < ballRadius * ballRadius;
|
|
543
|
-
}
|
|
544
|
-
isNearTarget(position) {
|
|
545
|
-
const distance = Math.sqrt(Math.pow(position.x - this.gameState.targetPosition.x, 2) + Math.pow(position.y - this.gameState.targetPosition.y, 2));
|
|
546
|
-
// Make target detection more generous - use 1.5x ball size for easier completion
|
|
547
|
-
return distance < this.gameConfig.BALL_SIZE * 1.5;
|
|
548
|
-
}
|
|
549
|
-
completeLevel() {
|
|
550
|
-
// No time bonus - faster completion is the goal
|
|
551
|
-
this.gameState.score += this.gameConfig.SCORE_PER_COMPLETION;
|
|
552
|
-
this.gameState.isCompleted = true;
|
|
553
|
-
this.gameState.isPlaying = false; // Stop the game when maze is completed
|
|
554
|
-
this.stopTimer();
|
|
555
|
-
this.notifyGameStateChange();
|
|
556
|
-
}
|
|
557
|
-
getGameState() {
|
|
558
|
-
return {
|
|
559
|
-
...this.gameState
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
getGameConfig() {
|
|
563
|
-
return {
|
|
564
|
-
...this.gameConfig
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
getWalls() {
|
|
568
|
-
return [...this.walls];
|
|
569
|
-
}
|
|
570
|
-
getMaze() {
|
|
571
|
-
return this.maze.map(row => row.map(cell => ({
|
|
572
|
-
...cell
|
|
573
|
-
})));
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Format time for display (MM:SS format)
|
|
577
|
-
formatTime(seconds) {
|
|
578
|
-
const minutes = Math.floor(seconds / 60);
|
|
579
|
-
const remainingSeconds = seconds % 60;
|
|
580
|
-
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
581
|
-
}
|
|
582
|
-
cleanup() {
|
|
583
|
-
this.stopTimer();
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
//# sourceMappingURL=MazeRunnerService.js.map
|
|
1
|
+
"use strict";export class MazeRunnerService{gameTimer = null;constructor(gridSize = 15,gameAreaWidth = 350,gameAreaHeight = 500,gameDuration = 120){this.gameConfig ={GRID_SIZE:gridSize,CELL_SIZE:Math.min(gameAreaWidth,gameAreaHeight)/ gridSize,BALL_SIZE:18,PIPE_WIDTH:8,GAME_AREA_WIDTH:gameAreaWidth,GAME_AREA_HEIGHT:gameAreaHeight,COLLISION_TOLERANCE:5,SCORE_PER_COMPLETION:100,TIME_BONUS_MULTIPLIER:10,GAME_DURATION:gameDuration};this.gameState ={isPlaying:false,isPaused:false,score:0,timeElapsed:0,ballPosition:{x:0,y:0},targetPosition:{x:0,y:0},isCompleted:false,gameOver:false};this.maze = [];this.walls = [];this.initializeMaze();this.generateMazeWithBacktracking();this.generateWalls();this.setStartAndEndPositions();}setGameStateChangeCallback(callback){this.onGameStateChange = callback;}notifyGameStateChange(){if(this.onGameStateChange){this.onGameStateChange({...this.gameState});}}initializeMaze(){this.maze = [];for(let y = 0;y < this.gameConfig.GRID_SIZE;y++){this.maze[y] = [];for(let x = 0;x < this.gameConfig.GRID_SIZE;x++){if(this.maze[y]){this.maze[y][x] ={x,y,walls:{top:true,right:true,bottom:true,left:true},visited:false};}}}}generateNewMaze(){this.initializeMaze();this.generateMazeWithBacktracking();this.generateWalls();this.setStartAndEndPositions();this.gameState.isCompleted = false;this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.gameOver = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.notifyGameStateChange();}generateMazeWithBacktracking(){const stack = [];const startCell = this.maze[0]?.[0];if(startCell){startCell.visited = true;stack.push(startCell);}while(stack.length > 0){const current = stack[stack.length - 1];const neighbors = current ? this.getUnvisitedNeighbors(current):[];if(neighbors.length > 0){const next = neighbors[Math.floor(Math.random()* neighbors.length)];if(current && next)this.removeWallBetween(current,next);if(next){next.visited = true;stack.push(next);}}else{stack.pop();}}this.ensureConnectivity();}ensureConnectivity(){if(!this.hasPathFromStartToEnd()){this.createMinimalPath();}}hasPathFromStartToEnd(){const visited = Array(this.gameConfig.GRID_SIZE).fill(null).map(()=> Array(this.gameConfig.GRID_SIZE).fill(false));const queue = [{x:0,y:0}];if(visited[0])visited[0][0] = true;while(queue.length > 0){const current = queue.shift();if(current.x === this.gameConfig.GRID_SIZE - 1 && current.y === this.gameConfig.GRID_SIZE - 1){return true;}const directions = [{dx:0,dy:-1,wall:'top'},{dx:1,dy:0,wall:'right'},{dx:0,dy:1,wall:'bottom'},{dx:-1,dy:0,wall:'left'}];for(const dir of directions){const newX = current.x + dir.dx;const newY = current.y + dir.dy;if(newX >= 0 && newX < this.gameConfig.GRID_SIZE && newY >= 0 && newY < this.gameConfig.GRID_SIZE && !visited[newY]?.[newX]){const currentCell = this.maze[current.y]?.[current.x];if(currentCell && !currentCell.walls[dir.wall]){if(visited[newY])visited[newY][newX] = true;queue.push({x:newX,y:newY});}}}}return false;}createMinimalPath(){let currentX = 0;let currentY = 0;const endX = this.gameConfig.GRID_SIZE - 1;const endY = this.gameConfig.GRID_SIZE - 1;const visited = new Set();while(currentX !== endX || currentY !== endY){const key = `${currentX},${currentY}`;if(visited.has(key)){break;}visited.add(key);const possibleMoves = [];if(currentX < endX)possibleMoves.push('right');if(currentY < endY)possibleMoves.push('down');if(currentX > 0)possibleMoves.push('left');if(currentY > 0)possibleMoves.push('up');if(possibleMoves.length === 0)break;let direction;if(Math.random()< 0.6 &&(currentX !== endX || currentY !== endY)){if(currentX < endX && Math.random()< 0.5){direction = 'right';}else if(currentY < endY){direction = 'down';}else if(currentX < endX){direction = 'right';}else{direction = possibleMoves[Math.floor(Math.random()* possibleMoves.length)];}}else{direction = possibleMoves[Math.floor(Math.random()* possibleMoves.length)];}const cell = this.maze[currentY]?.[currentX];if(direction === 'right' && currentX < this.gameConfig.GRID_SIZE - 1 && cell){cell.walls.right = false;const rightCell = this.maze[currentY]?.[currentX + 1];if(rightCell)rightCell.walls.left = false;currentX++;}else if(direction === 'down' && currentY < this.gameConfig.GRID_SIZE - 1 && cell){cell.walls.bottom = false;const bottomCell = this.maze[currentY + 1]?.[currentX];if(bottomCell)bottomCell.walls.top = false;currentY++;}else if(direction === 'left' && currentX > 0 && cell){cell.walls.left = false;const leftCell = this.maze[currentY]?.[currentX - 1];if(leftCell)leftCell.walls.right = false;currentX--;}else if(direction === 'up' && currentY > 0 && cell){cell.walls.top = false;const topCell = this.maze[currentY - 1]?.[currentX];if(topCell)topCell.walls.bottom = false;currentY--;}}}getUnvisitedNeighbors(cell){const neighbors = [];const{x,y}= cell;if(y > 0 && this.maze[y - 1]?.[x] && !this.maze[y - 1]?.[x]?.visited){const topCell = this.maze[y - 1]?.[x];if(topCell){neighbors.push(topCell);}}if(x < this.gameConfig.GRID_SIZE - 1 && this.maze[y]?.[x + 1] && !this.maze[y]?.[x + 1]?.visited){const rightCell = this.maze[y]?.[x + 1];if(rightCell){neighbors.push(rightCell);}}if(y < this.gameConfig.GRID_SIZE - 1 && this.maze[y + 1]?.[x] && !this.maze[y + 1]?.[x]?.visited){const bottomCell = this.maze[y + 1]?.[x];if(bottomCell){neighbors.push(bottomCell);}}if(x > 0 && this.maze[y]?.[x - 1] && !this.maze[y]?.[x - 1]?.visited){const leftCell = this.maze[y]?.[x - 1];if(leftCell){neighbors.push(leftCell);}}return neighbors;}removeWallBetween(current,next){const dx = current.x - next.x;const dy = current.y - next.y;if(dx === 1){current.walls.left = false;next.walls.right = false;}else if(dx === -1){current.walls.right = false;next.walls.left = false;}else if(dy === 1){current.walls.top = false;next.walls.bottom = false;}else if(dy === -1){current.walls.bottom = false;next.walls.top = false;}}generateWalls(){this.walls = [];const wallThickness = 4;const cellSize = this.gameConfig.CELL_SIZE;const mazeWidth = this.gameConfig.GRID_SIZE * cellSize;const mazeHeight = this.gameConfig.GRID_SIZE * cellSize;this.walls.push({x:0,y:0,width:mazeWidth,height:wallThickness,type:'horizontal'});this.walls.push({x:0,y:mazeHeight - wallThickness,width:mazeWidth,height:wallThickness,type:'horizontal'});this.walls.push({x:0,y:0,width:wallThickness,height:mazeHeight,type:'vertical'});this.walls.push({x:mazeWidth - wallThickness,y:0,width:wallThickness,height:mazeHeight,type:'vertical'});for(let y = 0;y < this.gameConfig.GRID_SIZE;y++){for(let x = 0;x < this.gameConfig.GRID_SIZE;x++){const cell = this.maze[y]?.[x];if(!cell)continue;const cellX = x * cellSize;const cellY = y * cellSize;if(cell.walls.top && y > 0){this.walls.push({x:cellX,y:cellY,width:cellSize,height:wallThickness,type:'horizontal'});}if(cell.walls.left && x > 0){this.walls.push({x:cellX,y:cellY,width:wallThickness,height:cellSize,type:'vertical'});}}}}setStartAndEndPositions(){const cellSize = this.gameConfig.CELL_SIZE;this.gameState.ballPosition ={x:cellSize / 2,y:cellSize / 2};this.gameState.targetPosition ={x:(this.gameConfig.GRID_SIZE - 1)* cellSize + cellSize / 2,y:(this.gameConfig.GRID_SIZE - 1)* cellSize + cellSize / 2};}updateSettings(gridSize,gameDuration,gameAreaWidth,gameAreaHeight){const newGameAreaWidth = gameAreaWidth || this.gameConfig.GAME_AREA_WIDTH;const newGameAreaHeight = gameAreaHeight || this.gameConfig.GAME_AREA_HEIGHT;this.gameConfig.GRID_SIZE = gridSize;this.gameConfig.GAME_AREA_WIDTH = newGameAreaWidth;this.gameConfig.GAME_AREA_HEIGHT = newGameAreaHeight;this.gameConfig.CELL_SIZE = Math.min(newGameAreaWidth,newGameAreaHeight)/ gridSize;this.gameConfig.GAME_DURATION = gameDuration;this.generateNewMaze();this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.stopTimer();this.notifyGameStateChange();}resetGame(){this.generateNewMaze();this.gameState.isPlaying = false;this.gameState.isPaused = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.stopTimer();this.notifyGameStateChange();}startGame(){this.gameState.ballPosition ={x:this.gameConfig.CELL_SIZE / 2,y:this.gameConfig.CELL_SIZE / 2};this.gameState.targetPosition ={x:(this.gameConfig.GRID_SIZE - 1)* this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2,y:(this.gameConfig.GRID_SIZE - 1)* this.gameConfig.CELL_SIZE + this.gameConfig.CELL_SIZE / 2};this.gameState.isPlaying = true;this.gameState.isPaused = false;this.gameState.score = 0;this.gameState.timeElapsed = 0;this.gameState.isCompleted = false;this.gameState.gameOver = false;this.startTimer();this.notifyGameStateChange();}pauseGame(){this.gameState.isPaused = true;this.stopTimer();this.notifyGameStateChange();}resumeGame(){this.gameState.isPaused = false;this.startTimer();this.notifyGameStateChange();}stopGame(){this.gameState.isPlaying = false;this.gameState.isPaused = false;this.stopTimer();this.notifyGameStateChange();}startTimer(){this.stopTimer();this.gameTimer = setInterval(()=>{if(!this.gameState.isPaused && this.gameState.isPlaying){this.gameState.timeElapsed += 1;this.notifyGameStateChange();}},1000);}stopTimer(){if(this.gameTimer){clearInterval(this.gameTimer);this.gameTimer = null;}}updateBallPosition(newPosition){if(!this.gameState.isPlaying || this.gameState.isPaused){return false;}if(this.isValidPosition(newPosition)){this.gameState.ballPosition = newPosition;if(!this.gameState.isCompleted && this.isNearTarget(newPosition)){this.completeLevel();}this.notifyGameStateChange();return true;}return false;}isValidPosition(position){const{x,y}= position;const ballRadius = this.gameConfig.BALL_SIZE / 2;for(const wall of this.walls){if(this.ballCollidesWithWall(x,y,ballRadius,wall)){return false;}}const padding = ballRadius + 2;if(x - ballRadius < padding || x + ballRadius > this.gameConfig.GAME_AREA_WIDTH - padding || y - ballRadius < padding || y + ballRadius > this.gameConfig.GAME_AREA_HEIGHT - padding){return false;}return true;}ballCollidesWithWall(ballX,ballY,ballRadius,wall){const closestX = Math.max(wall.x,Math.min(ballX,wall.x + wall.width));const closestY = Math.max(wall.y,Math.min(ballY,wall.y + wall.height));const distanceX = ballX - closestX;const distanceY = ballY - closestY;const distanceSquared = distanceX * distanceX + distanceY * distanceY;return distanceSquared < ballRadius * ballRadius;}isNearTarget(position){const distance = Math.sqrt(Math.pow(position.x - this.gameState.targetPosition.x,2)+ Math.pow(position.y - this.gameState.targetPosition.y,2));return distance < this.gameConfig.BALL_SIZE * 1.5;}completeLevel(){this.gameState.score += this.gameConfig.SCORE_PER_COMPLETION;this.gameState.isCompleted = true;this.gameState.isPlaying = false;this.stopTimer();this.notifyGameStateChange();}getGameState(){return{...this.gameState};}getGameConfig(){return{...this.gameConfig};}getWalls(){return [...this.walls];}getMaze(){return this.maze.map(row => row.map(cell =>({...cell})));}formatTime(seconds){const minutes = Math.floor(seconds / 60);const remainingSeconds = seconds % 60;return `${minutes}:${remainingSeconds.toString().padStart(2,'0')}`;}cleanup(){this.stopTimer();}}
|