├── .gitpod.yml ├── README.md ├── main.py └── requirements.txt /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: gitpod/workspace-full-vnc 2 | ports: 3 | - port: 5900 4 | onOpen: ignore 5 | - port: 6080 6 | onOpen: open-preview 7 | tasks: 8 | - init: pip3 install -r requirements.txt 9 | command: python3 main.py 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tetris Game 2 | The game of Tetris. Made with pygame. 3 | 4 | You can view the video tutorials on how to create this game here: https://www.youtube.com/watch?v=uoR4ilCWwKA&t=3s 5 | 6 | # 💻 Launch Your Software Development Career Today! 7 | 8 | 🎓 **No degree? No problem!** My program equips you with everything you need to break into tech and land an entry-level software development role. 9 | 10 | 🚀 **Why Join?** 11 | - 💼 **$70k+ starting salary potential** 12 | - 🕐 **Self-paced:** Complete on your own time 13 | - 🤑 **Affordable:** Low risk compared to expensive bootcamps or degrees 14 | - 🎯 **45,000+ job openings** in the market 15 | 16 | 👉 **[Start your journey today!](https://techwithtim.net/dev)** 17 | No experience needed—just your determination. Future-proof your career and unlock six-figure potential like many of our students have! 18 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random 3 | 4 | """ 5 | 10 x 20 square grid 6 | shapes: S, Z, I, O, J, L, T 7 | represented in order by 0 - 6 8 | """ 9 | 10 | pygame.font.init() 11 | 12 | # GLOBALS VARS 13 | s_width = 800 14 | s_height = 700 15 | play_width = 300 # meaning 300 // 10 = 30 width per block 16 | play_height = 600 # meaning 600 // 20 = 20 height per blo ck 17 | block_size = 30 18 | 19 | top_left_x = (s_width - play_width) // 2 20 | top_left_y = s_height - play_height 21 | 22 | 23 | # SHAPE FORMATS 24 | 25 | S = [['.....', 26 | '.....', 27 | '..00.', 28 | '.00..', 29 | '.....'], 30 | ['.....', 31 | '..0..', 32 | '..00.', 33 | '...0.', 34 | '.....']] 35 | 36 | Z = [['.....', 37 | '.....', 38 | '.00..', 39 | '..00.', 40 | '.....'], 41 | ['.....', 42 | '..0..', 43 | '.00..', 44 | '.0...', 45 | '.....']] 46 | 47 | I = [['..0..', 48 | '..0..', 49 | '..0..', 50 | '..0..', 51 | '.....'], 52 | ['.....', 53 | '0000.', 54 | '.....', 55 | '.....', 56 | '.....']] 57 | 58 | O = [['.....', 59 | '.....', 60 | '.00..', 61 | '.00..', 62 | '.....']] 63 | 64 | J = [['.....', 65 | '.0...', 66 | '.000.', 67 | '.....', 68 | '.....'], 69 | ['.....', 70 | '..00.', 71 | '..0..', 72 | '..0..', 73 | '.....'], 74 | ['.....', 75 | '.....', 76 | '.000.', 77 | '...0.', 78 | '.....'], 79 | ['.....', 80 | '..0..', 81 | '..0..', 82 | '.00..', 83 | '.....']] 84 | 85 | L = [['.....', 86 | '...0.', 87 | '.000.', 88 | '.....', 89 | '.....'], 90 | ['.....', 91 | '..0..', 92 | '..0..', 93 | '..00.', 94 | '.....'], 95 | ['.....', 96 | '.....', 97 | '.000.', 98 | '.0...', 99 | '.....'], 100 | ['.....', 101 | '.00..', 102 | '..0..', 103 | '..0..', 104 | '.....']] 105 | 106 | T = [['.....', 107 | '..0..', 108 | '.000.', 109 | '.....', 110 | '.....'], 111 | ['.....', 112 | '..0..', 113 | '..00.', 114 | '..0..', 115 | '.....'], 116 | ['.....', 117 | '.....', 118 | '.000.', 119 | '..0..', 120 | '.....'], 121 | ['.....', 122 | '..0..', 123 | '.00..', 124 | '..0..', 125 | '.....']] 126 | 127 | shapes = [S, Z, I, O, J, L, T] 128 | shape_colors = [(0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 255, 0), (255, 165, 0), (0, 0, 255), (128, 0, 128)] 129 | # index 0 - 6 represent shape 130 | 131 | 132 | class Piece(object): 133 | rows = 20 # y 134 | columns = 10 # x 135 | 136 | def __init__(self, column, row, shape): 137 | self.x = column 138 | self.y = row 139 | self.shape = shape 140 | self.color = shape_colors[shapes.index(shape)] 141 | self.rotation = 0 # number from 0-3 142 | 143 | 144 | def create_grid(locked_positions={}): 145 | grid = [[(0,0,0) for x in range(10)] for x in range(20)] 146 | 147 | for i in range(len(grid)): 148 | for j in range(len(grid[i])): 149 | if (j,i) in locked_positions: 150 | c = locked_positions[(j,i)] 151 | grid[i][j] = c 152 | return grid 153 | 154 | 155 | def convert_shape_format(shape): 156 | positions = [] 157 | format = shape.shape[shape.rotation % len(shape.shape)] 158 | 159 | for i, line in enumerate(format): 160 | row = list(line) 161 | for j, column in enumerate(row): 162 | if column == '0': 163 | positions.append((shape.x + j, shape.y + i)) 164 | 165 | for i, pos in enumerate(positions): 166 | positions[i] = (pos[0] - 2, pos[1] - 4) 167 | 168 | return positions 169 | 170 | 171 | def valid_space(shape, grid): 172 | accepted_positions = [[(j, i) for j in range(10) if grid[i][j] == (0,0,0)] for i in range(20)] 173 | accepted_positions = [j for sub in accepted_positions for j in sub] 174 | formatted = convert_shape_format(shape) 175 | 176 | for pos in formatted: 177 | if pos not in accepted_positions: 178 | if pos[1] > -1: 179 | return False 180 | 181 | return True 182 | 183 | 184 | def check_lost(positions): 185 | for pos in positions: 186 | x, y = pos 187 | if y < 1: 188 | return True 189 | return False 190 | 191 | 192 | def get_shape(): 193 | global shapes, shape_colors 194 | 195 | return Piece(5, 0, random.choice(shapes)) 196 | 197 | 198 | def draw_text_middle(text, size, color, surface): 199 | font = pygame.font.SysFont('comicsans', size, bold=True) 200 | label = font.render(text, 1, color) 201 | 202 | surface.blit(label, (top_left_x + play_width/2 - (label.get_width() / 2), top_left_y + play_height/2 - label.get_height()/2)) 203 | 204 | 205 | def draw_grid(surface, row, col): 206 | sx = top_left_x 207 | sy = top_left_y 208 | for i in range(row): 209 | pygame.draw.line(surface, (128,128,128), (sx, sy+ i*30), (sx + play_width, sy + i * 30)) # horizontal lines 210 | for j in range(col): 211 | pygame.draw.line(surface, (128,128,128), (sx + j * 30, sy), (sx + j * 30, sy + play_height)) # vertical lines 212 | 213 | 214 | def clear_rows(grid, locked): 215 | # need to see if row is clear the shift every other row above down one 216 | 217 | inc = 0 218 | for i in range(len(grid)-1,-1,-1): 219 | row = grid[i] 220 | if (0, 0, 0) not in row: 221 | inc += 1 222 | # add positions to remove from locked 223 | ind = i 224 | for j in range(len(row)): 225 | try: 226 | del locked[(j, i)] 227 | except: 228 | continue 229 | if inc > 0: 230 | for key in sorted(list(locked), key=lambda x: x[1])[::-1]: 231 | x, y = key 232 | if y < ind: 233 | newKey = (x, y + inc) 234 | locked[newKey] = locked.pop(key) 235 | 236 | 237 | def draw_next_shape(shape, surface): 238 | font = pygame.font.SysFont('comicsans', 30) 239 | label = font.render('Next Shape', 1, (255,255,255)) 240 | 241 | sx = top_left_x + play_width + 50 242 | sy = top_left_y + play_height/2 - 100 243 | format = shape.shape[shape.rotation % len(shape.shape)] 244 | 245 | for i, line in enumerate(format): 246 | row = list(line) 247 | for j, column in enumerate(row): 248 | if column == '0': 249 | pygame.draw.rect(surface, shape.color, (sx + j*30, sy + i*30, 30, 30), 0) 250 | 251 | surface.blit(label, (sx + 10, sy- 30)) 252 | 253 | 254 | def draw_window(surface): 255 | surface.fill((0,0,0)) 256 | # Tetris Title 257 | font = pygame.font.SysFont('comicsans', 60) 258 | label = font.render('TETRIS', 1, (255,255,255)) 259 | 260 | surface.blit(label, (top_left_x + play_width / 2 - (label.get_width() / 2), 30)) 261 | 262 | for i in range(len(grid)): 263 | for j in range(len(grid[i])): 264 | pygame.draw.rect(surface, grid[i][j], (top_left_x + j* 30, top_left_y + i * 30, 30, 30), 0) 265 | 266 | # draw grid and border 267 | draw_grid(surface, 20, 10) 268 | pygame.draw.rect(surface, (255, 0, 0), (top_left_x, top_left_y, play_width, play_height), 5) 269 | # pygame.display.update() 270 | 271 | 272 | def main(): 273 | global grid 274 | 275 | locked_positions = {} # (x,y):(255,0,0) 276 | grid = create_grid(locked_positions) 277 | 278 | change_piece = False 279 | run = True 280 | current_piece = get_shape() 281 | next_piece = get_shape() 282 | clock = pygame.time.Clock() 283 | fall_time = 0 284 | level_time = 0 285 | fall_speed = 0.27 286 | score = 0 287 | 288 | while run: 289 | 290 | grid = create_grid(locked_positions) 291 | fall_time += clock.get_rawtime() 292 | level_time += clock.get_rawtime() 293 | clock.tick() 294 | 295 | if level_time/1000 > 4: 296 | level_time = 0 297 | if fall_speed > 0.15: 298 | fall_speed -= 0.005 299 | 300 | 301 | # PIECE FALLING CODE 302 | if fall_time/1000 >= fall_speed: 303 | fall_time = 0 304 | current_piece.y += 1 305 | if not (valid_space(current_piece, grid)) and current_piece.y > 0: 306 | current_piece.y -= 1 307 | change_piece = True 308 | 309 | for event in pygame.event.get(): 310 | if event.type == pygame.QUIT: 311 | run = False 312 | pygame.display.quit() 313 | quit() 314 | 315 | if event.type == pygame.KEYDOWN: 316 | if event.key == pygame.K_LEFT: 317 | current_piece.x -= 1 318 | if not valid_space(current_piece, grid): 319 | current_piece.x += 1 320 | 321 | elif event.key == pygame.K_RIGHT: 322 | current_piece.x += 1 323 | if not valid_space(current_piece, grid): 324 | current_piece.x -= 1 325 | elif event.key == pygame.K_UP: 326 | # rotate shape 327 | current_piece.rotation = current_piece.rotation + 1 % len(current_piece.shape) 328 | if not valid_space(current_piece, grid): 329 | current_piece.rotation = current_piece.rotation - 1 % len(current_piece.shape) 330 | 331 | if event.key == pygame.K_DOWN: 332 | # move shape down 333 | current_piece.y += 1 334 | if not valid_space(current_piece, grid): 335 | current_piece.y -= 1 336 | 337 | '''if event.key == pygame.K_SPACE: 338 | while valid_space(current_piece, grid): 339 | current_piece.y += 1 340 | current_piece.y -= 1 341 | print(convert_shape_format(current_piece))''' # todo fix 342 | 343 | shape_pos = convert_shape_format(current_piece) 344 | 345 | # add piece to the grid for drawing 346 | for i in range(len(shape_pos)): 347 | x, y = shape_pos[i] 348 | if y > -1: 349 | grid[y][x] = current_piece.color 350 | 351 | # IF PIECE HIT GROUND 352 | if change_piece: 353 | for pos in shape_pos: 354 | p = (pos[0], pos[1]) 355 | locked_positions[p] = current_piece.color 356 | current_piece = next_piece 357 | next_piece = get_shape() 358 | change_piece = False 359 | 360 | # call four times to check for multiple clear rows 361 | if clear_rows(grid, locked_positions): 362 | score += 10 363 | 364 | draw_window(win) 365 | draw_next_shape(next_piece, win) 366 | pygame.display.update() 367 | 368 | # Check if user lost 369 | if check_lost(locked_positions): 370 | run = False 371 | 372 | draw_text_middle("You Lost", 40, (255,255,255), win) 373 | pygame.display.update() 374 | pygame.time.delay(2000) 375 | 376 | 377 | def main_menu(): 378 | run = True 379 | while run: 380 | win.fill((0,0,0)) 381 | draw_text_middle('Press any key to begin.', 60, (255, 255, 255), win) 382 | pygame.display.update() 383 | for event in pygame.event.get(): 384 | if event.type == pygame.QUIT: 385 | run = False 386 | 387 | if event.type == pygame.KEYDOWN: 388 | main() 389 | pygame.quit() 390 | 391 | 392 | win = pygame.display.set_mode((s_width, s_height)) 393 | pygame.display.set_caption('Tetris') 394 | 395 | main_menu() # start game 396 | 397 | 398 | 399 | 400 | 401 | 402 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pygame 2 | --------------------------------------------------------------------------------