11import pygame
22import random
3+
34pygame .init ()
45
6+ # ================== CONFIG ==================
57GRID_SIZE = 40
6- GRID_WIDTH = 17
7- GRID_HEIGHT = 17
8+ GRID_WIDTH = 17
9+ GRID_HEIGHT = 17
810SCREEN_WIDTH = GRID_SIZE * GRID_WIDTH
911SCREEN_HEIGHT = GRID_SIZE * GRID_HEIGHT
10- BG_COLOR = (30 , 30 , 30 )
12+
13+ BG_COLOR = (30 , 30 , 30 )
1114PLAYER_COLOR = (0 , 255 , 0 )
1215COIN_COLOR = (255 , 215 , 0 )
1316POISON_COLOR = (255 , 0 , 0 )
17+ SHIELD_COLOR = (0 , 191 , 255 )
18+
1419FPS = 15
1520
1621screen = pygame .display .set_mode ((SCREEN_WIDTH , SCREEN_HEIGHT ))
17- pygame .display .set_caption ("coin&poison " )
22+ pygame .display .set_caption ("Coin & Poison Adventure " )
1823
1924font = pygame .font .Font (None , 36 )
2025
26+ # ================== CLASSES ==================
27+
2128class Player (pygame .sprite .Sprite ):
29+ """Player controlled by arrow keys"""
2230 def __init__ (self ):
2331 super ().__init__ ()
2432 self .image = pygame .Surface ((GRID_SIZE , GRID_SIZE ))
2533 self .image .fill (PLAYER_COLOR )
2634 self .rect = self .image .get_rect ()
2735 self .rect .topleft = (0 , 0 )
2836 self .score = 0
37+ self .lives = 3
38+ self .shield = False
2939
3040 def move (self , dx , dy ):
3141 new_rect = self .rect .move (dx * GRID_SIZE , dy * GRID_SIZE )
32- if 0 <= new_rect .left < SCREEN_WIDTH and 0 <= new_rect .top < SCREEN_HEIGHT : # within limits
42+ if 0 <= new_rect .left < SCREEN_WIDTH and 0 <= new_rect .top < SCREEN_HEIGHT :
3343 self .rect = new_rect
3444 else :
35- self .score -= 1 # hit the boundary
45+ self .score -= 1 # penalize hitting boundary
3646
3747class Coin (pygame .sprite .Sprite ):
48+ """Collectible coin"""
3849 def __init__ (self , x , y ):
3950 super ().__init__ ()
4051 self .image = pygame .Surface ((GRID_SIZE , GRID_SIZE ))
@@ -43,15 +54,27 @@ def __init__(self, x, y):
4354 self .rect .topleft = (x * GRID_SIZE , y * GRID_SIZE )
4455
4556class Poison (pygame .sprite .Sprite ):
57+ """Poison block that reduces life"""
4658 def __init__ (self , x , y ):
4759 super ().__init__ ()
4860 self .image = pygame .Surface ((GRID_SIZE , GRID_SIZE ))
4961 self .image .fill (POISON_COLOR )
5062 self .rect = self .image .get_rect ()
5163 self .rect .topleft = (x * GRID_SIZE , y * GRID_SIZE )
5264
53- # to spawn coin and poison blocks without overlapping with each other and player
65+ class Shield (pygame .sprite .Sprite ):
66+ """Power-up shield to protect from poison"""
67+ def __init__ (self , x , y ):
68+ super ().__init__ ()
69+ self .image = pygame .Surface ((GRID_SIZE , GRID_SIZE ))
70+ self .image .fill (SHIELD_COLOR )
71+ self .rect = self .image .get_rect ()
72+ self .rect .topleft = (x * GRID_SIZE , y * GRID_SIZE )
73+
74+ # ================== HELPER FUNCTIONS ==================
75+
5476def spawn_objects (num_objects , avoid_positions ):
77+ """Spawn objects without overlapping"""
5578 objects = []
5679 while len (objects ) < num_objects :
5780 x = random .randint (0 , GRID_WIDTH - 1 )
@@ -61,35 +84,67 @@ def spawn_objects(num_objects, avoid_positions):
6184 avoid_positions .add ((x , y ))
6285 return objects
6386
87+ def reset_level ():
88+ """Reset coins, poisons, and optionally add shield"""
89+ global coins , poisons , shield_powerups , all_sprites , avoid_positions , FPS , level
90+ level += 1
91+ FPS += 1 # increase speed for next level
92+ num_coins = 10 + level * 2
93+ num_poisons = 5 + level
94+
95+ # Clear old objects
96+ for sprite in coins .sprites () + poisons .sprites () + shield_powerups .sprites ():
97+ all_sprites .remove (sprite )
98+
99+ # Reset positions
100+ avoid_positions = {(player .rect .x // GRID_SIZE , player .rect .y // GRID_SIZE )}
101+
102+ # Spawn coins
103+ coin_positions = spawn_objects (num_coins , avoid_positions )
104+ coins = pygame .sprite .Group ()
105+ for pos in coin_positions :
106+ coin = Coin (* pos )
107+ coins .add (coin )
108+ all_sprites .add (coin )
109+
110+ # Spawn poisons
111+ poison_positions = spawn_objects (num_poisons , avoid_positions )
112+ poisons = pygame .sprite .Group ()
113+ for pos in poison_positions :
114+ poison = Poison (* pos )
115+ poisons .add (poison )
116+ all_sprites .add (poison )
117+
118+ # Spawn shield occasionally
119+ shield_positions = spawn_objects (1 , avoid_positions )
120+ shield_powerups = pygame .sprite .Group ()
121+ for pos in shield_positions :
122+ shield = Shield (* pos )
123+ shield_powerups .add (shield )
124+ all_sprites .add (shield )
125+
126+ # ================== GAME INIT ==================
127+
64128player = Player ()
65129all_sprites = pygame .sprite .Group ()
66130all_sprites .add (player )
67131
68- # total no of coins and poison blocks
69- num_coins = 20
70- num_poisons = 10
71-
132+ coins = pygame .sprite .Group ()
133+ poisons = pygame .sprite .Group ()
134+ shield_powerups = pygame .sprite .Group ()
72135avoid_positions = {(player .rect .x // GRID_SIZE , player .rect .y // GRID_SIZE )}
73136
74- poison_positions = spawn_objects (num_poisons , avoid_positions )
75- poisons = pygame .sprite .Group ()
76- for pos in poison_positions :
77- poison = Poison (* pos )
78- poisons .add (poison )
79- all_sprites .add (poison )
80-
81- coin_positions = spawn_objects (num_coins , avoid_positions )
82- coins = pygame .sprite .Group ()
83- for pos in coin_positions :
84- coin = Coin (* pos )
85- coins .add (coin )
86- all_sprites .add (coin )
137+ level = 1
138+ reset_level ()
87139
88140clock = pygame .time .Clock ()
89-
90141running = True
142+
143+ # ================== GAME LOOP ==================
144+
91145while running :
92- clock .tick (FPS )
146+ clock .tick (FPS )
147+
93148 for event in pygame .event .get ():
94149 if event .type == pygame .QUIT :
95150 running = False
@@ -104,24 +159,39 @@ def spawn_objects(num_objects, avoid_positions):
104159 if keys [pygame .K_DOWN ]:
105160 player .move (0 , 1 )
106161
162+ # Collect coins
107163 collected_coins = pygame .sprite .spritecollide (player , coins , True )
108164 player .score += len (collected_coins )
109165
110- if pygame .sprite .spritecollideany (player , poisons ):
111- print (f"collided with poison block! score: { player .score } " )
112- running = False
166+ # Collect shield
167+ if pygame .sprite .spritecollide (player , shield_powerups , True ):
168+ player .shield = True
169+ print ("Shield activated! Next poison hit will be ignored." )
113170
114- # if all coins collected, won
171+ # Poison collision
172+ if pygame .sprite .spritecollideany (player , poisons ):
173+ if player .shield :
174+ player .shield = False
175+ print ("Shield protected you from poison!" )
176+ else :
177+ player .lives -= 1
178+ if player .lives == 0 :
179+ print (f"Game Over! Final Score: { player .score } " )
180+ running = False
181+ else :
182+ print (f"Hit poison! Lives left: { player .lives } " )
183+
184+ # Level completed
115185 if len (coins ) == 0 :
116- print (f"collected all coins! score: { player . score } " )
117- running = False
186+ print (f"Level { level } cleared! Moving to next level... " )
187+ reset_level ()
118188
189+ # Draw everything
119190 screen .fill (BG_COLOR )
120191 all_sprites .draw (screen )
121-
122- score_text = font .render (f"score: { player .score } " , True , (255 , 255 , 255 ))
123- screen .blit (score_text , (10 , 10 ))
192+ score_text = font .render (f"Score: { player .score } | Coins left: { len (coins )} | Lives: { player .lives } | Level: { level } " , True , (255 , 255 , 255 ))
193+ screen .blit (score_text , (10 , 10 ))
124194
125195 pygame .display .flip ()
126196
127- pygame .quit ()
197+ pygame .quit ()
0 commit comments