@@ -12,14 +12,20 @@ const canvasContainer = document.querySelector(".canvas-container");
1212const easterEggVideo = document . getElementById ( "easterEggVideo" ) ;
1313const pausedMessageElement = document . getElementById ( "pausedMessage" ) ;
1414
15- // --- Audio Elements ---
16- const eatSound = new Audio ( 'media/eat.mp3' ) ;
17- const gameOverSound = new Audio ( 'media/game-over.mp3' ) ;
18-
1915// --- Game Constants ---
2016const GRID_SIZE = 20 ; // Number of cells in the grid
2117let CANVAS_WIDTH , CANVAS_HEIGHT ;
2218let CELL_SIZE ;
19+ const POWER_UP_POINTS = 3 ;
20+ const EATER_EGG_TRIGGER = 50 ;
21+
22+ // --- Audio Elements ---
23+ const eatSound = new Audio ( 'media/eat.mp3' ) ;
24+ const gameOverSound = new Audio ( 'media/game-over.mp3' ) ;
25+
26+ // --- Image Elements ---
27+ const powerUpImage = new Image ( ) ;
28+ powerUpImage . src = 'media/rat.png' ;
2329
2430// --- Define color gradient start/end colors ---
2531// Head has darker color and gradually becomes lighter towards the tail
@@ -29,10 +35,13 @@ const SNAKE_TAIL_COLOR_RGB = [134, 239, 172]; // Lighter Green (tailwind green-4
2935const FOOD_COLOR = "#ef4444" ; // Red (tailwind red-500)
3036const BORDER_COLOR = "#555555" ; // Slightly lighter border for contrast on white
3137const INITIAL_SPEED_MS = 150 ;
38+ const MAX_SPEED_MS = 80 ;
39+
3240
3341// --- Game State ---
3442let snake = [ ] ;
3543let food = { x : 0 , y : 0 } ;
44+ let powerUp = null ;
3645let dx = 0 ;
3746let dy = 0 ;
3847let score = 0 ;
@@ -128,6 +137,21 @@ function drawFood() {
128137 ctx . strokeRect ( food . x * CELL_SIZE , food . y * CELL_SIZE , CELL_SIZE , CELL_SIZE ) ;
129138}
130139
140+ // Function to draw the power-up
141+ function drawPowerUp ( ) {
142+
143+ // image must fit into the cell
144+ if ( powerUp ) {
145+ ctx . drawImage (
146+ powerUpImage ,
147+ powerUp . x * CELL_SIZE ,
148+ powerUp . y * CELL_SIZE ,
149+ CELL_SIZE ,
150+ CELL_SIZE
151+ ) ;
152+ }
153+ }
154+
131155function moveSnake ( ) {
132156 if ( ! gameActive || isPaused ) return ;
133157
@@ -152,21 +176,34 @@ function moveSnake() {
152176 eatSound . play ( ) ;
153177
154178 // Check for easter egg
155- if ( score === 100 ) { // Trigger at 100
179+ if ( score === EATER_EGG_TRIGGER ) {
156180 triggerEasterEgg ( ) ;
157181 }
158182
159183 // create food at a new position
160184 generateFood ( ) ;
161185
186+ // Generate power-up every 10 points
187+ if ( score % 10 === 0 ) {
188+ generatePowerUp ( ) ;
189+ }
190+
162191 // we increase the speed as the game progresses to make it more challenging
163192 // this can be achieved by reducing the interval time between each game loop
164193 // so that the snake get redrawn more frequently
165- if ( score % 5 === 0 && currentSpeed > 50 ) {
194+ if ( score % 5 === 0 && currentSpeed > MAX_SPEED_MS ) {
166195 currentSpeed -= 10 ;
196+
197+ console . log ( currentSpeed )
167198 clearInterval ( gameLoopInterval ) ;
168199 gameLoopInterval = setInterval ( gameLoop , currentSpeed ) ;
169200 }
201+ } else if ( powerUp && head . x === powerUp . x && head . y === powerUp . y ) {
202+ // Handle power-up collection
203+ eatSound . play ( ) ;
204+ score += POWER_UP_POINTS ;
205+ scoreElement . textContent = `Score: ${ score } ` ;
206+ powerUp = null ; // Remove the power-up
170207 } else {
171208 // if the head did not hit the food, we remove the last part of the snake
172209 // making it look like the snake is moving forward
@@ -185,6 +222,20 @@ function generateFood() {
185222 food = { x : newFoodX , y : newFoodY } ;
186223}
187224
225+ // Function to generate a power-up
226+ function generatePowerUp ( ) {
227+ let newPowerUpX , newPowerUpY ;
228+ do {
229+ newPowerUpX = Math . floor ( Math . random ( ) * GRID_SIZE ) ;
230+ newPowerUpY = Math . floor ( Math . random ( ) * GRID_SIZE ) ;
231+ } while (
232+ snake . some ( ( part ) => part . x === newPowerUpX && part . y === newPowerUpY ) ||
233+ ( food . x === newPowerUpX && food . y === newPowerUpY )
234+ ) ;
235+
236+ powerUp = { x : newPowerUpX , y : newPowerUpY } ;
237+ }
238+
188239function handleKeyDown ( event ) {
189240 // allow player to restart the game by pressing enter
190241 if (
@@ -281,6 +332,7 @@ function gameLoop() {
281332 }
282333 clearCanvas ( ) ;
283334 drawFood ( ) ;
335+ drawPowerUp ( ) ;
284336 moveSnake ( ) ;
285337 drawSnake ( ) ;
286338}
@@ -319,13 +371,19 @@ function startGame() {
319371}
320372
321373function endGame ( ) {
374+ if ( ! gameOverElement . classList . contains ( "hidden" ) ) {
375+ return ; // Prevent multiple game over messages
376+ }
377+
322378 console . log ( "Game Over!" ) ;
323379 gameActive = false ;
324380 clearInterval ( gameLoopInterval ) ;
325381 gameLoopInterval = null ;
326382 finalScoreElement . textContent = score ;
327383 gameOverElement . classList . remove ( "hidden" ) ;
328384 gameOverSound . play ( ) ; // Play game over sound effect
385+ powerUp = null ;
386+ currentSpeed = INITIAL_SPEED_MS ; // Reset speed for next game
329387}
330388
331389function showStartMessage ( ) {
@@ -375,7 +433,6 @@ function showStartMessage() {
375433function triggerEasterEgg ( ) {
376434 // Pause the game
377435 gameActive = false ;
378- clearInterval ( gameLoopInterval ) ;
379436
380437 // Show and play the video
381438 easterEggVideo . classList . remove ( "hidden" ) ;
@@ -386,7 +443,6 @@ function triggerEasterEgg() {
386443 easterEggVideo . classList . add ( "hidden" ) ;
387444 // Resume the game
388445 gameActive = true ;
389- gameLoopInterval = setInterval ( gameLoop , currentSpeed ) ;
390446 } ;
391447}
392448
@@ -433,6 +489,7 @@ window.addEventListener("resize", () => {
433489 } else if ( gameActive ) {
434490 clearCanvas ( ) ; // Clear with white
435491 drawFood ( ) ;
492+ drawPowerUp ( ) ; // Draw the power-up
436493 drawSnake ( ) ; // Redraw gradient snake
437494 } else if ( ! gameOverElement . classList . contains ( "hidden" ) ) {
438495 clearCanvas ( ) ; // Clear with white
0 commit comments