generated from mwc/project_game
Started retro tetris game
Started developing a retro version of Tetris, following the planning on the board of the classroom (see planning.jpg). Moved cursor work into cursor.
This commit is contained in:
10
block.py
Normal file
10
block.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class Block:
|
||||||
|
"""A Block represents a single square on the Tetris board.
|
||||||
|
Blocks are part of a Piece while they are 'alive'.
|
||||||
|
"""
|
||||||
|
character = "X"
|
||||||
|
color = "blue"
|
||||||
|
alive = True
|
||||||
|
|
||||||
|
def __init__(self, position):
|
||||||
|
self.position = position
|
||||||
7
game.py
Normal file
7
game.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from retro.game import Game
|
||||||
|
from manager import Manager
|
||||||
|
|
||||||
|
agents = [Manager()]
|
||||||
|
state = {'level': 1}
|
||||||
|
game = Game(agents, state, board_size=(20, 20), debug=True)
|
||||||
|
game.play()
|
||||||
24
manager.py
Normal file
24
manager.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from piece import Piece, PIECE_DEFINITIONS
|
||||||
|
from random import choice
|
||||||
|
from retro.errors import AgentNotFoundByName
|
||||||
|
|
||||||
|
class Manager:
|
||||||
|
"""The Manager takes care of stuff that isn't anyone else's responsibility:
|
||||||
|
- Create a Piece whenever none exists.
|
||||||
|
- Clear full rows of Blocks (and move other Blocks down).
|
||||||
|
- End the game when the Blocks pile up all the way.
|
||||||
|
"""
|
||||||
|
display = False
|
||||||
|
|
||||||
|
def play_turn(self, game):
|
||||||
|
try:
|
||||||
|
game.get_agent_by_name("piece")
|
||||||
|
except AgentNotFoundByName:
|
||||||
|
self.create_piece(game)
|
||||||
|
|
||||||
|
def create_piece(self, game):
|
||||||
|
width, height = game.board_size
|
||||||
|
piece = Piece((width//2, 2), game, choice(PIECE_DEFINITIONS))
|
||||||
|
game.add_agent(piece)
|
||||||
|
|
||||||
|
|
||||||
100
piece.py
Normal file
100
piece.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
from block import Block
|
||||||
|
|
||||||
|
PIECE_DEFINITIONS = [
|
||||||
|
[(-1, 0), (0, 0), (1, 0), (2, 0)],
|
||||||
|
[(0, 0), (1, 0), (0, 1), (1, 1)],
|
||||||
|
]
|
||||||
|
|
||||||
|
class Piece:
|
||||||
|
"""A Piece is a group of blocks which are 'alive':
|
||||||
|
They fall and the player can rotate or move them.
|
||||||
|
A Piece is created with a position, the game, and a list of block_offsets,
|
||||||
|
each of which represents the location of one of the Piece's
|
||||||
|
Blocks relative to the Piece position.
|
||||||
|
"""
|
||||||
|
name = "piece"
|
||||||
|
display = False
|
||||||
|
|
||||||
|
def __init__(self, position, game, block_offsets):
|
||||||
|
self.position = position
|
||||||
|
self.blocks = {}
|
||||||
|
for offset in block_offsets:
|
||||||
|
self.create_block(game, offset)
|
||||||
|
|
||||||
|
def handle_keystroke(self, keystroke, game):
|
||||||
|
x, y = self.position
|
||||||
|
if keystroke.name == "KEY_LEFT":
|
||||||
|
new_position = (x - 1, y)
|
||||||
|
if self.can_move_to(new_position, game):
|
||||||
|
self.move_to(new_position)
|
||||||
|
elif keystroke.name == "KEY_RIGHT":
|
||||||
|
new_position = (x + 1, y)
|
||||||
|
if self.can_move_to(new_position, game):
|
||||||
|
self.move_to(new_position)
|
||||||
|
|
||||||
|
def play_turn(self, game):
|
||||||
|
if self.should_fall(game):
|
||||||
|
self.fall(game)
|
||||||
|
|
||||||
|
def should_fall(self, game):
|
||||||
|
"""Determines whether the piece should fall.
|
||||||
|
Currently, the Piece falls every third turn.
|
||||||
|
In the future, the Piece should fall slowly at first, and
|
||||||
|
then should fall faster at higher levels.
|
||||||
|
"""
|
||||||
|
return game.turn_number % 3 == 0
|
||||||
|
|
||||||
|
def fall(self, game):
|
||||||
|
x, y = self.position
|
||||||
|
falling_position = (x, y + 1)
|
||||||
|
if self.can_move_to(falling_position, game):
|
||||||
|
self.move_to(falling_position)
|
||||||
|
else:
|
||||||
|
self.destroy(game)
|
||||||
|
|
||||||
|
def can_move_to(self, new_position, game):
|
||||||
|
"""Checks whether the Piece can move to a new position.
|
||||||
|
For every one of the Piece's Blocks, finds where that block
|
||||||
|
would be after the move, and checks whether there are any dead agents
|
||||||
|
already there (live agents would be Blocks which are part of this Piece,
|
||||||
|
not a problem since they'll be moving too).
|
||||||
|
"""
|
||||||
|
x, y = new_position
|
||||||
|
agents_by_position = game.get_agents_by_position()
|
||||||
|
for offset in self.blocks.keys():
|
||||||
|
ox, oy = offset
|
||||||
|
new_block_position = (x+ox, y+oy)
|
||||||
|
if not game.on_board(new_block_position):
|
||||||
|
return False
|
||||||
|
for agent in agents_by_position[new_block_position]:
|
||||||
|
if not agent.alive:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def move_to(self, position):
|
||||||
|
"""Move to position and updates positions of Blocks.
|
||||||
|
"""
|
||||||
|
x, y = position
|
||||||
|
self.position = position
|
||||||
|
for offset, block in self.blocks.items():
|
||||||
|
ox, oy = offset
|
||||||
|
block.position = (x + ox, y + oy)
|
||||||
|
|
||||||
|
def create_block(self, game, offset):
|
||||||
|
x, y = self.position
|
||||||
|
ox, oy = offset
|
||||||
|
block = Block((x + ox, y + oy))
|
||||||
|
self.blocks[offset] = block
|
||||||
|
game.add_agent(block)
|
||||||
|
|
||||||
|
def destroy(self, game):
|
||||||
|
"""Causes the Piece to destroy itself.
|
||||||
|
All the Blocks are set to dead.
|
||||||
|
"""
|
||||||
|
for block in self.blocks.values():
|
||||||
|
block.alive = False
|
||||||
|
game.remove_agent(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
planning.jpg
Normal file
BIN
planning.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 MiB |
8
poetry.lock
generated
8
poetry.lock
generated
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansicon"
|
name = "ansicon"
|
||||||
@@ -48,14 +48,14 @@ ansicon = {version = "*", markers = "platform_system == \"Windows\""}
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "retro-games"
|
name = "retro-games"
|
||||||
version = "1.1.0"
|
version = "1.1.3"
|
||||||
description = "A simple framework for Terminal-based games"
|
description = "A simple framework for Terminal-based games"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.10"
|
python-versions = "<4.0,>=3.10"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "retro_games-1.1.0-py3-none-any.whl", hash = "sha256:c621117e4dd528b1e4870d897d00c4365566ab3ba965177e3996ed3c889dd9f8"},
|
{file = "retro_games-1.1.3-py3-none-any.whl", hash = "sha256:4bdd27241b5cb3ee72e69a042d301ff58df2a2ade7e3c29400a538fa54e30148"},
|
||||||
{file = "retro_games-1.1.0.tar.gz", hash = "sha256:2167b574f42fe1e739b7c9ec75e98a9b76df42e2166376b85559291b3dc58f82"},
|
{file = "retro_games-1.1.3.tar.gz", hash = "sha256:4f91ff725e551820aa4e30c12c0264e2da41967ed34252122b7136bc2a8ed311"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|||||||
Reference in New Issue
Block a user