Pytris
Introduction
Tetris written using Python in the browser.
Swiftly researching options for Python in the browser, resulted in using Brython.
PyScript was another option, but I favored Brython for the lower download size.
Controls
Use cursor keys ▲ ▶ ▼ ◀ SPACE or I J K L M SPACE to control the game.
Todo
- Could show tetromino preview
- Should wall-kick
- Should do correct t-spin
- Should do recursive gravity (clear lines)
Main source
from random import randrangefrom blocks import blocksfrom bucket import Bucketfrom render import draw_bucketfrom browser import document, bind, window, timerclass Tetris:def __init__(self):canvas = document["tetris"]self.ctx = canvas.getContext("2d")self.bucket = Bucket()# new gameself.lines = 0self.score = 0self.timer = 0# new blockself.new_block()def new_block(self):self.block = randrange(len(blocks)) # TODO seems not very random, lots of same blocksself.rotation = 0self.row = 0self.column = 3if not self.bucket.test_tetromino(self.row, self.column, self.block, self.rotation):window.alert('Game over!')timer.clear_interval(self.timer)returnself.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 += 1self.row += 1self.bucket.draw_tetromino(self.row, self.column, self.block, self.rotation)else:# land blockself.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 blockself.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 -= 1self.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 += 1self.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) % 4self.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 += 1self.score += 5self.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:returnmatch 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()