from random import randrange from blocks import blocks from bucket import Bucket from render import draw_bucket from browser import document, bind, window, timer class Tetris: def __init__(self): canvas = document["tetris"] self.ctx = canvas.getContext("2d") self.bucket = Bucket() # new game self.lines = 0 self.score = 0 self.timer = 0 # new block self.new_block() def new_block(self): self.block = randrange(len(blocks)) # TODO seems not very random, lots of same blocks self.rotation = 0 self.row = 0 self.column = 3 if not self.bucket.test_tetromino(self.row, self.column, self.block, self.rotation): window.alert('Game over!') timer.clear_interval(self.timer) return self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) timer.clear_interval(self.timer) self.timer = timer.set_interval(self.move_down, 650 - self.lines) def move_down(self): self.bucket.clear_tetromino(self.row, self.column, self.block, self.rotation) if self.bucket.test_tetromino(self.row + 1, self.column, self.block, self.rotation): self.score += 1 self.row += 1 self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) else: # land block self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) lines_cleared = self.bucket.clear_lines() self.score += [0, 150, 300, 700, 1500][lines_cleared] self.lines += lines_cleared # new block self.new_block() def move_left(self): self.bucket.clear_tetromino(self.row, self.column, self.block, self.rotation) if self.bucket.test_tetromino(self.row, self.column - 1, self.block, self.rotation): self.column -= 1 self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) def move_right(self): self.bucket.clear_tetromino(self.row, self.column, self.block, self.rotation) if self.bucket.test_tetromino(self.row, self.column + 1, self.block, self.rotation): self.column += 1 self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) def rotate(self): self.bucket.clear_tetromino(self.row, self.column, self.block, self.rotation) if self.bucket.test_tetromino( self.row, self.column, self.block, (self.rotation + 1) % 4 ): self.rotation = (self.rotation + 1) % 4 self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) def drop(self): self.bucket.clear_tetromino(self.row, self.column, self.block, self.rotation) while self.bucket.test_tetromino(self.row + 1, self.column, self.block, self.rotation): self.row += 1 self.score += 5 self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation) def draw_score(self): self.ctx.fillStyle = "white" self.ctx.fillText(f"Score: {self.score}", 220, 20) self.ctx.fillText(f"Lines: {self.lines}", 220, 40) def loop(self, *args): self.ctx.clearRect(0, 0, self.ctx.canvas.width, self.ctx.canvas.height) self.draw_score() draw_bucket(self.ctx, self.bucket.bucket) # TODO optimize timer.set_timeout(self.loop, self.speed) window.requestAnimationFrame(self.loop) @bind(document, "keydown") def keyDownHandler(e): if e.metaKey or e.altKey or e.shiftKey or e.ctrlKey: return match e.code: case "ArrowLeft" | "KeyJ": e.preventDefault() tetris.move_left() case "ArrowRight" | "KeyL": e.preventDefault() tetris.move_right() case "ArrowDown" | "KeyM": e.preventDefault() tetris.move_down() case "ArrowUp" | "KeyK": e.preventDefault() tetris.rotate() case "Space": e.preventDefault() tetris.drop() tetris = Tetris() tetris.loop()