aboutsummaryrefslogtreecommitdiff
path: root/tetris_shape.py
diff options
context:
space:
mode:
Diffstat (limited to 'tetris_shape.py')
-rw-r--r--tetris_shape.py171
1 files changed, 171 insertions, 0 deletions
diff --git a/tetris_shape.py b/tetris_shape.py
new file mode 100644
index 0000000..e81a0af
--- /dev/null
+++ b/tetris_shape.py
@@ -0,0 +1,171 @@
+LEFT = "left"
+RIGHT = "right"
+DOWN = "down"
+direction_d = { "left": (-1, 0), "right": (1, 0), "down": (0, 1) }
+
+class Block(object):
+ def __init__( self, (x, y), color):
+ self.color = color
+ self.x = x
+ self.y = y
+
+ def coord( self ):
+ return (self.x, self.y)
+
+class shape(object):
+ """
+ Shape is the Base class for the game pieces e.g. square, T, S, Z, L,
+ reverse L and I. Shapes are constructed of blocks.
+ """
+ @classmethod
+ def check_and_create(cls, board, coords, color ):
+ """
+ Check if the blocks that make the shape can be placed in empty coords
+ before creating and returning the shape instance. Otherwise, return
+ None.
+ """
+ for coord in coords:
+ if not board.check_block( coord ):
+ return None
+
+ return cls( board, coords, color)
+
+ def __init__(self, board, coords, color ):
+ """
+ Initialise the shape base.
+ """
+ self.board = board
+ self.blocks = []
+
+ for coord in coords:
+ self.blocks.append( Block(coord,color) )
+
+ def move( self, direction ):
+ """
+ Move the blocks in the direction indicated by adding (dx, dy) to the
+ current block coordinates
+ """
+ d_x, d_y = direction_d[direction]
+
+ for block in self.blocks:
+ x = block.x + d_x
+ y = block.y + d_y
+ if not self.board.check_block( (x, y) ):
+ return False
+
+ for block in self.blocks:
+ block.x += d_x
+ block.y += d_y
+
+ return True
+
+ def rotate(self, clockwise = True):
+ """
+ Rotate the blocks around the 'middle' block, 90-degrees. The
+ middle block is always the index 0 block in the list of blocks
+ that make up a shape.
+ """
+ # TO DO: Refactor for DRY
+ middle = self.blocks[0]
+ rel = []
+ for block in self.blocks:
+ rel.append( (block.x-middle.x, block.y-middle.y ) )
+
+ # to rotate 90-degrees (x,y) = (-y, x)
+ # First check that the there are no collisions or out of bounds moves.
+ for idx in xrange(len(self.blocks)):
+ rel_x, rel_y = rel[idx]
+ if clockwise:
+ x = middle.x+rel_y
+ y = middle.y-rel_x
+ else:
+ x = middle.x-rel_y
+ y = middle.y+rel_x
+
+ if not self.board.check_block( (x, y) ):
+ return False
+
+ for idx in xrange(len(self.blocks)):
+ rel_x, rel_y = rel[idx]
+ if clockwise:
+ x = middle.x+rel_y
+ y = middle.y-rel_x
+ else:
+ x = middle.x-rel_y
+ y = middle.y+rel_x
+
+ self.blocks[idx].x = x
+ self.blocks[idx].y = y
+
+ return True
+
+class shape_limited_rotate( shape ):
+ """
+ This is a base class for the shapes like the S, Z and I that don't fully
+ rotate (which would result in the shape moving *up* one block on a 180).
+ Instead they toggle between 90 degrees clockwise and then back 90 degrees
+ anti-clockwise.
+ """
+ def __init__( self, board, coords, color ):
+ self.clockwise = True
+ super(shape_limited_rotate, self).__init__(board, coords, color)
+
+ def rotate(self, clockwise=True):
+ """
+ Clockwise, is used to indicate if the shape should rotate clockwise
+ or back again anti-clockwise. It is toggled.
+ """
+ super(shape_limited_rotate, self).rotate(clockwise=self.clockwise)
+ if self.clockwise:
+ self.clockwise=False
+ else:
+ self.clockwise=True
+
+class square_shape( shape ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords = [(4,0),(5,0),(4,1),(5,1)]
+ return super(square_shape, cls).check_and_create(board, coords, "red")
+
+ def rotate(self, clockwise=True):
+ """
+ Override the rotate method for the square shape to do exactly nothing!
+ """
+ pass
+
+class t_shape( shape ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords = [(4,0),(3,0),(5,0),(4,1)]
+ return super(t_shape, cls).check_and_create(board, coords, "yellow" )
+
+class l_shape( shape ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords = [(4,0),(3,0),(5,0),(3,1)]
+ return super(l_shape, cls).check_and_create(board, coords, "orange")
+
+class reverse_l_shape( shape ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords = [(5,0),(4,0),(6,0),(6,1)]
+ return super(reverse_l_shape, cls).check_and_create(
+ board, coords, "green")
+
+class z_shape( shape_limited_rotate ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords =[(5,0),(4,0),(5,1),(6,1)]
+ return super(z_shape, cls).check_and_create(board, coords, "purple")
+
+class s_shape( shape_limited_rotate ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords =[(5,1),(4,1),(5,0),(6,0)]
+ return super(s_shape, cls).check_and_create(board, coords, "magenta")
+
+class i_shape( shape_limited_rotate ):
+ @classmethod
+ def check_and_create( cls, board ):
+ coords =[(4,0),(3,0),(5,0),(6,0)]
+ return super(i_shape, cls).check_and_create(board, coords, "blue")