Skip to content

Commit 00b705d

Browse files
Add files via upload
0 parents  commit 00b705d

22 files changed

+487
-0
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

complete project/code/game.py

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
from settings import *
2+
from random import choice
3+
from sys import exit
4+
from os.path import join
5+
6+
from timer import Timer
7+
8+
class Game:
9+
def __init__(self, get_next_shape, update_score):
10+
11+
# general
12+
self.surface = pygame.Surface((GAME_WIDTH, GAME_HEIGHT))
13+
self.display_surface = pygame.display.get_surface()
14+
self.rect = self.surface.get_rect(topleft = (PADDING, PADDING))
15+
self.sprites = pygame.sprite.Group()
16+
17+
# game connection
18+
self.get_next_shape = get_next_shape
19+
self.update_score = update_score
20+
21+
# lines
22+
self.line_surface = self.surface.copy()
23+
self.line_surface.fill((0,255,0))
24+
self.line_surface.set_colorkey((0,255,0))
25+
self.line_surface.set_alpha(120)
26+
27+
# tetromino
28+
self.field_data = [[0 for x in range(COLUMNS)] for y in range(ROWS)]
29+
self.tetromino = Tetromino(
30+
choice(list(TETROMINOS.keys())),
31+
self.sprites,
32+
self.create_new_tetromino,
33+
self.field_data)
34+
35+
# timer
36+
self.down_speed = UPDATE_START_SPEED
37+
self.down_speed_faster = self.down_speed * 0.3
38+
self.down_pressed = False
39+
self.timers = {
40+
'vertical move': Timer(self.down_speed, True, self.move_down),
41+
'horizontal move': Timer(MOVE_WAIT_TIME),
42+
'rotate': Timer(ROTATE_WAIT_TIME)
43+
}
44+
self.timers['vertical move'].activate()
45+
46+
# score
47+
self.current_level = 1
48+
self.current_score = 0
49+
self.current_lines = 0
50+
51+
# sound
52+
self.landing_sound = pygame.mixer.Sound(join('..','sound', 'landing.wav'))
53+
self.landing_sound.set_volume(0.1)
54+
55+
def calculate_score(self, num_lines):
56+
self.current_lines += num_lines
57+
self.current_score += SCORE_DATA[num_lines] * self.current_level
58+
59+
if self.current_lines / 10 > self.current_level:
60+
self.current_level += 1
61+
self.down_speed *= 0.75
62+
self.down_speed_faster = self.down_speed * 0.3
63+
self.timers['vertical move'].duration = self.down_speed
64+
65+
self.update_score(self.current_lines, self.current_score, self.current_level)
66+
67+
def check_game_over(self):
68+
for block in self.tetromino.blocks:
69+
if block.pos.y < 0:
70+
exit()
71+
72+
def create_new_tetromino(self):
73+
self.landing_sound.play()
74+
self.check_game_over()
75+
self.check_finished_rows()
76+
self.tetromino = Tetromino(
77+
self.get_next_shape(),
78+
self.sprites,
79+
self.create_new_tetromino,
80+
self.field_data)
81+
82+
def timer_update(self):
83+
for timer in self.timers.values():
84+
timer.update()
85+
86+
def move_down(self):
87+
self.tetromino.move_down()
88+
89+
def draw_grid(self):
90+
91+
for col in range(1, COLUMNS):
92+
x = col * CELL_SIZE
93+
pygame.draw.line(self.line_surface, LINE_COLOR, (x,0), (x,self.surface.get_height()), 1)
94+
95+
for row in range(1, ROWS):
96+
y = row * CELL_SIZE
97+
pygame.draw.line(self.line_surface, LINE_COLOR, (0,y), (self.surface.get_width(),y))
98+
99+
self.surface.blit(self.line_surface, (0,0))
100+
101+
def input(self):
102+
keys = pygame.key.get_pressed()
103+
104+
# checking horizontal movement
105+
if not self.timers['horizontal move'].active:
106+
if keys[pygame.K_LEFT]:
107+
self.tetromino.move_horizontal(-1)
108+
self.timers['horizontal move'].activate()
109+
if keys[pygame.K_RIGHT]:
110+
self.tetromino.move_horizontal(1)
111+
self.timers['horizontal move'].activate()
112+
113+
# check for rotation
114+
if not self.timers['rotate'].active:
115+
if keys[pygame.K_UP]:
116+
self.tetromino.rotate()
117+
self.timers['rotate'].activate()
118+
119+
# down speedup
120+
if not self.down_pressed and keys[pygame.K_DOWN]:
121+
self.down_pressed = True
122+
self.timers['vertical move'].duration = self.down_speed_faster
123+
124+
if self.down_pressed and not keys[pygame.K_DOWN]:
125+
self.down_pressed = False
126+
self.timers['vertical move'].duration = self.down_speed
127+
128+
def check_finished_rows(self):
129+
130+
# get the full row indexes
131+
delete_rows = []
132+
for i, row in enumerate(self.field_data):
133+
if all(row):
134+
delete_rows.append(i)
135+
136+
if delete_rows:
137+
for delete_row in delete_rows:
138+
139+
# delete full rows
140+
for block in self.field_data[delete_row]:
141+
block.kill()
142+
143+
# move down blocks
144+
for row in self.field_data:
145+
for block in row:
146+
if block and block.pos.y < delete_row:
147+
block.pos.y += 1
148+
149+
# rebuild the field data
150+
self.field_data = [[0 for x in range(COLUMNS)] for y in range(ROWS)]
151+
for block in self.sprites:
152+
self.field_data[int(block.pos.y)][int(block.pos.x)] = block
153+
154+
# update score
155+
self.calculate_score(len(delete_rows))
156+
157+
def run(self):
158+
159+
# update
160+
self.input()
161+
self.timer_update()
162+
self.sprites.update()
163+
164+
# drawing
165+
self.surface.fill(GRAY)
166+
self.sprites.draw(self.surface)
167+
168+
self.draw_grid()
169+
self.display_surface.blit(self.surface, (PADDING,PADDING))
170+
pygame.draw.rect(self.display_surface, LINE_COLOR, self.rect, 2, 2)
171+
172+
class Tetromino:
173+
def __init__(self, shape, group, create_new_tetromino, field_data):
174+
175+
# setup
176+
self.shape = shape
177+
self.block_positions = TETROMINOS[shape]['shape']
178+
self.color = TETROMINOS[shape]['color']
179+
self.create_new_tetromino = create_new_tetromino
180+
self.field_data = field_data
181+
182+
# create blocks
183+
self.blocks = [Block(group, pos, self.color) for pos in self.block_positions]
184+
185+
# collisions
186+
def next_move_horizontal_collide(self, blocks, amount):
187+
collision_list = [block.horizontal_collide(int(block.pos.x + amount), self.field_data) for block in self.blocks]
188+
return True if any(collision_list) else False
189+
190+
def next_move_vertical_collide(self, blocks, amount):
191+
collision_list = [block.vertical_collide(int(block.pos.y + amount), self.field_data) for block in self.blocks]
192+
return True if any(collision_list) else False
193+
194+
# movement
195+
def move_horizontal(self, amount):
196+
if not self.next_move_horizontal_collide(self.blocks, amount):
197+
for block in self.blocks:
198+
block.pos.x += amount
199+
200+
def move_down(self):
201+
if not self.next_move_vertical_collide(self.blocks, 1):
202+
for block in self.blocks:
203+
block.pos.y += 1
204+
else:
205+
for block in self.blocks:
206+
self.field_data[int(block.pos.y)][int(block.pos.x)] = block
207+
self.create_new_tetromino()
208+
209+
# rotate
210+
def rotate(self):
211+
if self.shape != 'O':
212+
213+
# 1. pivot point
214+
pivot_pos = self.blocks[0].pos
215+
216+
# 2. new block positions
217+
new_block_positions = [block.rotate(pivot_pos) for block in self.blocks]
218+
219+
# 3. collision check
220+
for pos in new_block_positions:
221+
# horizontal
222+
if pos.x < 0 or pos.x >= COLUMNS:
223+
return
224+
225+
# field check -> collision with other pieces
226+
if self.field_data[int(pos.y)][int(pos.x)]:
227+
return
228+
229+
# vertical / floor check
230+
if pos.y > ROWS:
231+
return
232+
233+
# 4. implement new positions
234+
for i, block in enumerate(self.blocks):
235+
block.pos = new_block_positions[i]
236+
237+
class Block(pygame.sprite.Sprite):
238+
def __init__(self, group, pos, color):
239+
240+
# general
241+
super().__init__(group)
242+
self.image = pygame.Surface((CELL_SIZE,CELL_SIZE))
243+
self.image.fill(color)
244+
245+
# position
246+
self.pos = pygame.Vector2(pos) + BLOCK_OFFSET
247+
self.rect = self.image.get_rect(topleft = self.pos * CELL_SIZE)
248+
249+
def rotate(self, pivot_pos):
250+
251+
return pivot_pos + (self.pos - pivot_pos).rotate(90)
252+
253+
def horizontal_collide(self, x, field_data):
254+
if not 0 <= x < COLUMNS:
255+
return True
256+
257+
if field_data[int(self.pos.y)][x]:
258+
return True
259+
260+
def vertical_collide(self, y, field_data):
261+
if y >= ROWS:
262+
return True
263+
264+
if y >= 0 and field_data[y][int(self.pos.x)]:
265+
return True
266+
267+
def update(self):
268+
269+
self.rect.topleft = self.pos * CELL_SIZE

complete project/code/main.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from settings import *
2+
from sys import exit
3+
from os.path import join
4+
5+
# components
6+
from game import Game
7+
from score import Score
8+
from preview import Preview
9+
10+
from random import choice
11+
12+
class Main:
13+
def __init__(self):
14+
15+
# general
16+
pygame.init()
17+
self.display_surface = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT))
18+
self.clock = pygame.time.Clock()
19+
pygame.display.set_caption('Tetris')
20+
21+
# shapes
22+
self.next_shapes = [choice(list(TETROMINOS.keys())) for shape in range(3)]
23+
24+
# components
25+
self.game = Game(self.get_next_shape, self.update_score)
26+
self.score = Score()
27+
self.preview = Preview()
28+
29+
# audio
30+
self.music = pygame.mixer.Sound(join('..','sound','music.wav'))
31+
self.music.set_volume(0.05)
32+
self.music.play(-1)
33+
34+
def update_score(self, lines, score, level):
35+
self.score.lines = lines
36+
self.score.score = score
37+
self.score.level = level
38+
39+
def get_next_shape(self):
40+
next_shape = self.next_shapes.pop(0)
41+
self.next_shapes.append(choice(list(TETROMINOS.keys())))
42+
return next_shape
43+
44+
def run(self):
45+
while True:
46+
for event in pygame.event.get():
47+
if event.type == pygame.QUIT:
48+
pygame.quit()
49+
exit()
50+
51+
# display
52+
self.display_surface.fill(GRAY)
53+
54+
# components
55+
self.game.run()
56+
self.score.run()
57+
self.preview.run(self.next_shapes)
58+
59+
# updating the game
60+
pygame.display.update()
61+
self.clock.tick()
62+
63+
if __name__ == '__main__':
64+
main = Main()
65+
main.run()

complete project/code/preview.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from settings import *
2+
from pygame.image import load
3+
from os import path
4+
5+
class Preview:
6+
def __init__(self):
7+
8+
# general
9+
self.display_surface = pygame.display.get_surface()
10+
self.surface = pygame.Surface((SIDEBAR_WIDTH, GAME_HEIGHT * PREVIEW_HEIGHT_FRACTION))
11+
self.rect = self.surface.get_rect(topright = (WINDOW_WIDTH - PADDING,PADDING))
12+
13+
# shapes
14+
self.shape_surfaces = {shape: load(path.join('..','graphics',f'{shape}.png')).convert_alpha() for shape in TETROMINOS.keys()}
15+
16+
# image position data
17+
self.increment_height = self.surface.get_height() / 3
18+
19+
def display_pieces(self, shapes):
20+
for i, shape in enumerate(shapes):
21+
shape_surface = self.shape_surfaces[shape]
22+
x = self.surface.get_width() / 2
23+
y = self.increment_height / 2 + i * self.increment_height
24+
rect = shape_surface.get_rect(center = (x,y))
25+
self.surface.blit(shape_surface,rect)
26+
27+
def run(self, next_shapes):
28+
self.surface.fill(GRAY)
29+
self.display_pieces(next_shapes)
30+
self.display_surface.blit(self.surface, self.rect)
31+
pygame.draw.rect(self.display_surface, LINE_COLOR, self.rect, 2, 2)

0 commit comments

Comments
 (0)