Pārlūkot izejas kodu

refactored game_state

Alexey Dorokhov 3 gadi atpakaļ
vecāks
revīzija
783b0406cb
5 mainītis faili ar 239 papildinājumiem un 196 dzēšanām
  1. 29 15
      competitive_tetris.py
  2. 0 179
      game.py
  3. 206 0
      game_state.py
  4. 2 1
      wire_protocol.fbs
  5. 2 1
      wire_protocol/CommandResponseError.py

+ 29 - 15
competitive_tetris.py

@@ -9,7 +9,7 @@ import zmq
 import flatbuffers
 
 import tetrominos
-import game
+import game_state
 import wire_protocol.Command
 import wire_protocol.CommandRequest
 import wire_protocol.CommandResponse
@@ -66,7 +66,7 @@ def draw_next_shape(shape, surface):
     surface.blit(label, (sx + 10, sy - 30))
 
 
-def draw_game_state_to_window(surface, game_state):
+def draw_game_state_to_window(surface, state):
     surface.fill((0, 0, 0))
 
     pygame.font.init()
@@ -77,13 +77,13 @@ def draw_game_state_to_window(surface, game_state):
 
     # current score
     font = pygame.font.SysFont('comicsans', 30)
-    label = font.render(f'Score: {game_state.score}', 1, (255,255,255))
+    label = font.render(f'Score: {state.data.score}', 1, (255,255,255))
     sx = TOP_LEFT_X + PLAY_WIDTH + 50
     sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
     surface.blit(label, (sx + 20, sy + 160))
 
 
-    field = game.get_game_state_field(game_state)
+    field = state.get_field()
     for row in range(field.shape[0]):
         for col in range(field.shape[1]):
             color = (0,0,0)
@@ -117,8 +117,7 @@ def main(win):
 
     def _next_piece_func():
         return random.choice(range(len(tetrominos.SHAPES)))
-    game_state = game.initial_game_state(_next_piece_func(),
-                                         _next_piece_func())
+    state = game_state.create_initial_game_state(_next_piece_func)
 
     while run:
         clock.tick()
@@ -138,15 +137,31 @@ def main(win):
             cells = None
 
             if req.Command() == wire_protocol.Command.Command.ShiftLeft:
-                game_state = game.game_state_shift(game_state, -1)
+                next_state = state.shift(-1)
+                if next_state is None:
+                    error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
+                else:
+                    state = next_state
             elif req.Command() == wire_protocol.Command.Command.ShiftRight:
-                game_state = game.game_state_shift(game_state, 1)
+                next_state = state.shift(1)
+                if next_state is None:
+                    error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
+                else:
+                    state = next_state
             elif req.Command() == wire_protocol.Command.Command.Rotate:
-                game_state = game.game_state_rotate(game_state)
+                next_state = state.rotate()
+                if next_state is None:
+                    error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
+                else:
+                    state = next_state
             elif req.Command() == wire_protocol.Command.Command.Land:
-                game_state = game.game_state_land(game_state)
+                next_state = state.land()
+                if next_state is None:
+                    error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
+                else:
+                    state = next_state
             elif req.Command() == wire_protocol.Command.Command.GetGameState:
-                cells = game.get_game_state_field(game_state)
+                cells = state.get_field()
             else:
                 error_code = wire_protocol.CommandResponseError.CommandResponseError.UNKNOWN_COMMAND
 
@@ -174,12 +189,11 @@ def main(win):
 
         is_game_running = True
         if frame_time >= 500:
-            game_state, is_game_running = game.advance_game_state(
-                    game_state, _next_piece_func)
+            state, is_game_running = state.advance()
             frame_time = 0
 
-        draw_game_state_to_window(win, game_state)
-        draw_next_shape(game_state.next_piece, win)
+        draw_game_state_to_window(win, state)
+        draw_next_shape(state.data.next_piece, win)
         pygame.display.update()
 
         if not is_game_running:

+ 0 - 179
game.py

@@ -1,179 +0,0 @@
-import collections
-import numpy as np
-
-import tetrominos
-
-FIELD_WIDTH = 10
-FIELD_HEIGHT = 20
-
-COMMAND_NONE = 0
-COMMAND_LEFT = 1
-COMMAND_RIGHT = 2
-COMMAND_ROTATE = 3
-
-def tetromino_to_field_at(tetromino, row, col):
-    col_offset = col + tetromino.col_offset
-    row_offset = row + tetromino.row_offset
-    if col_offset < 0 or row_offset < 0:
-        return None
-    if col_offset + tetromino.data.shape[1] > FIELD_WIDTH:
-        return None
-    if row_offset + tetromino.data.shape[0] > FIELD_HEIGHT:
-        return None
-
-    array = tetromino.data
-    left_zeros = np.zeros(shape=(array.shape[0], col_offset),
-                          dtype=np.byte, order="C")
-    right_zeros = np.zeros(shape=(array.shape[0],
-                                  FIELD_WIDTH-col_offset-array.shape[1]),
-                           dtype=np.byte, order="C")
-    array = np.concatenate((left_zeros, array, right_zeros), axis=1)
-
-    top_zeros = np.zeros(shape=(row_offset,FIELD_WIDTH),
-                         dtype=np.byte, order="C")
-    bottom_zeros = np.zeros(shape=(FIELD_HEIGHT - row_offset - array.shape[0],
-                                   FIELD_WIDTH),
-                            dtype=np.byte, order="C")
-    array = np.concatenate((top_zeros, array, bottom_zeros), axis=0)
-    return array
-
-
-GameState = collections.namedtuple('GameState',
-                                   ['grid', 'current_piece', 'next_piece',
-                                    'rotation', 'row', 'col', 'score'])
-
-
-def initial_game_state(current_piece, next_piece):
-    return GameState(grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
-                                   dtype=np.byte, order="C"),
-                     current_piece=current_piece,
-                     next_piece=next_piece,
-                     rotation=0, row=0, col=0, score=0)
-
-
-def is_game_state_valid(game_state):
-    tetromino_array = tetromino_to_field_at(
-            tetrominos.SHAPES[game_state.current_piece][game_state.rotation],
-            game_state.row, game_state.col)
-    if tetromino_array is None:
-        return False
-
-    total_field = np.add(game_state.grid, tetromino_array)
-    if np.any(np.vectorize(lambda x: x > 1)(total_field)):
-        return False
-    return True
-
-
-def get_game_state_field(game_state):
-    tetromino_array = tetromino_to_field_at(
-            tetrominos.SHAPES[game_state.current_piece][game_state.rotation],
-            game_state.row, game_state.col)
-    total_field = np.add(game_state.grid, tetromino_array)
-    total_field = np.add(total_field, tetromino_array)
-    return total_field
-
-
-def game_state_shift(game_state, col_offset):
-    next_game_state = GameState(grid=game_state.grid,
-                                current_piece=game_state.current_piece,
-                                next_piece=game_state.next_piece,
-                                rotation=game_state.rotation,
-                                row=game_state.row,
-                                col=game_state.col + col_offset,
-                                score=game_state.score)
-    if is_game_state_valid(next_game_state):
-        return next_game_state
-    return game_state
-
-
-def game_state_land(game_state):
-    while True:
-        next_game_state = GameState(grid=game_state.grid,
-                                    current_piece=game_state.current_piece,
-                                    next_piece=game_state.next_piece,
-                                    rotation=game_state.rotation,
-                                    row=game_state.row + 1,
-                                    col=game_state.col,
-                                    score=game_state.score)
-        if is_game_state_valid(next_game_state):
-            game_state = next_game_state
-        else:
-            break
-    return game_state
-
-
-def game_state_rotate(game_state):
-    next_game_state = GameState(grid=game_state.grid,
-                                current_piece=game_state.current_piece,
-                                next_piece=game_state.next_piece,
-                                rotation=(game_state.rotation + 1) % 4,
-                                row=game_state.row,
-                                col=game_state.col,
-                                score=game_state.score)
-    if is_game_state_valid(next_game_state):
-        return next_game_state
-    return game_state
-
-
-def advance_game_state_down(game_state):
-    next_game_state = GameState(grid=game_state.grid,
-                                current_piece=game_state.current_piece,
-                                next_piece=game_state.next_piece,
-                                rotation=game_state.rotation,
-                                row=game_state.row + 1,
-                                col=game_state.col,
-                                score=game_state.score)
-    if is_game_state_valid(next_game_state):
-        return next_game_state
-    return None
-
-
-def advance_game_state_piece(game_state, next_piece):
-    tetromino_array = tetromino_to_field_at(
-            tetrominos.SHAPES[game_state.current_piece][game_state.rotation],
-            game_state.row, game_state.col)
-    next_grid = np.add(game_state.grid, tetromino_array)
-    next_game_state = GameState(grid=next_grid,
-                                current_piece=game_state.next_piece,
-                                next_piece=next_piece,
-                                rotation=0, row=0, col=0,
-                                score=game_state.score)
-    return next_game_state
-
-
-def advance_game_state_purge_lines(game_state):
-    next_grid_row = FIELD_HEIGHT-1
-    next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
-                                dtype=np.byte, order="C")
-    next_score = game_state.score
-    for row in range(FIELD_HEIGHT-1, -1,-1):
-        if np.all(game_state.grid[row,:]):
-            next_score += 1
-        else:
-            next_grid[next_grid_row,:] = game_state.grid[row,:]
-            next_grid_row -= 1
-
-    next_game_state = GameState(grid=next_grid,
-                                current_piece=game_state.current_piece,
-                                next_piece=game_state.next_piece,
-                                rotation=game_state.rotation,
-                                row=game_state.row,
-                                col=game_state.col,
-                                score=next_score)
-    return next_game_state
-
-
-
-
-def advance_game_state(game_state, next_piece_func):
-    next_game_state = advance_game_state_down(game_state)
-    is_game_running = True
-    if next_game_state is None:
-        next_game_state = advance_game_state_piece(game_state, next_piece_func())
-        next_game_state = advance_game_state_purge_lines(next_game_state)
-        if not is_game_state_valid(next_game_state):
-            is_game_running = False
-
-    if not is_game_running:
-        return game_state, False
-    return next_game_state, True

+ 206 - 0
game_state.py

@@ -0,0 +1,206 @@
+import collections
+import numpy as np
+
+import tetrominos
+
+FIELD_WIDTH = 10
+FIELD_HEIGHT = 20
+
+COMMAND_NONE = 0
+COMMAND_LEFT = 1
+COMMAND_RIGHT = 2
+COMMAND_ROTATE = 3
+
+def tetromino_to_field_at(tetromino, row, col):
+    col_offset = col + tetromino.col_offset
+    row_offset = row + tetromino.row_offset
+    if col_offset < 0 or row_offset < 0:
+        return None
+    if col_offset + tetromino.data.shape[1] > FIELD_WIDTH:
+        return None
+    if row_offset + tetromino.data.shape[0] > FIELD_HEIGHT:
+        return None
+
+    array = tetromino.data
+    left_zeros = np.zeros(shape=(array.shape[0], col_offset),
+                          dtype=np.byte, order="C")
+    right_zeros = np.zeros(shape=(array.shape[0],
+                                  FIELD_WIDTH-col_offset-array.shape[1]),
+                           dtype=np.byte, order="C")
+    array = np.concatenate((left_zeros, array, right_zeros), axis=1)
+
+    top_zeros = np.zeros(shape=(row_offset,FIELD_WIDTH),
+                         dtype=np.byte, order="C")
+    bottom_zeros = np.zeros(shape=(FIELD_HEIGHT - row_offset - array.shape[0],
+                                   FIELD_WIDTH),
+                            dtype=np.byte, order="C")
+    array = np.concatenate((top_zeros, array, bottom_zeros), axis=0)
+    return array
+
+
+GameStateData = collections.namedtuple('GameStateData',
+                                       ['grid', 'current_piece', 'next_piece',
+                                        'rotation', 'row', 'col', 'score'])
+
+
+class GameState:
+    def __init__(self):
+        self.data = None
+        self.next_piece_func = None
+
+    def is_valid(self):
+        tetromino_array = tetromino_to_field_at(
+                tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
+                self.data.row, self.data.col)
+        if tetromino_array is None:
+            return False
+        total_field = np.add(self.data.grid, tetromino_array)
+        if np.any(np.vectorize(lambda x: x > 1)(total_field)):
+            return False
+        return True
+
+    def get_field(self):
+        tetromino_array = tetromino_to_field_at(
+                tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
+                self.data.row, self.data.col)
+        total_field = np.add(self.data.grid, tetromino_array)
+        total_field = np.add(total_field, tetromino_array)
+        return total_field
+
+    def shift(self, col_offset):
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=self.data.grid,
+                current_piece=self.data.current_piece,
+                next_piece=self.data.next_piece,
+                rotation=self.data.rotation,
+                row=self.data.row,
+                col=self.data.col + col_offset,
+                score=self.data.score)
+        if next_game_state.is_valid():
+            return next_game_state
+        return None
+
+    def rotate(self):
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=self.data.grid,
+                current_piece=self.data.current_piece,
+                next_piece=self.data.next_piece,
+                rotation=(self.data.rotation + 1) % 4,
+                row=self.data.row,
+                col=self.data.col,
+                score=self.data.score)
+        if next_game_state.is_valid():
+            return next_game_state
+        return None
+
+    def land(self):
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=self.data.grid,
+                current_piece=self.data.current_piece,
+                next_piece=self.data.next_piece,
+                rotation=self.data.rotation,
+                row=self.data.row,
+                col=self.data.col,
+                score=self.data.score)
+
+        try_game_state = GameState()
+        while True:
+            try_game_state.data = GameStateData(
+                grid=next_game_state.data.grid,
+                current_piece=next_game_state.data.current_piece,
+                next_piece=next_game_state.data.next_piece,
+                rotation=next_game_state.data.rotation,
+                row=next_game_state.data.row + 1,
+                col=next_game_state.data.col,
+                score=next_game_state.data.score)
+            if try_game_state.is_valid():
+                next_game_state.data = try_game_state.data
+            else:
+                break
+        return next_game_state
+
+    def _advance_down(self):
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=self.data.grid,
+                current_piece=self.data.current_piece,
+                next_piece=self.data.next_piece,
+                rotation=self.data.rotation,
+                row=self.data.row + 1,
+                col=self.data.col,
+                score=self.data.score)
+        if next_game_state.is_valid():
+            return next_game_state
+        return None
+
+    def _advance_piece(self):
+        tetromino_array = tetromino_to_field_at(
+                tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
+                self.data.row, self.data.col)
+        next_grid = np.add(self.data.grid, tetromino_array)
+
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=next_grid,
+                current_piece=self.data.next_piece,
+                next_piece=self.next_piece_func(),
+                rotation=0, row=0, col=0,
+                score=self.data.score)
+        return next_game_state
+
+    def _advance_purge_lines(self):
+        next_grid_row = FIELD_HEIGHT-1
+        next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
+                                    dtype=np.byte, order="C")
+        next_score = self.data.score
+        for row in range(FIELD_HEIGHT-1, -1,-1):
+            if np.all(self.data.grid[row,:]):
+                next_score += 1
+            else:
+                next_grid[next_grid_row,:] = self.data.grid[row,:]
+                next_grid_row -= 1
+
+        next_game_state = GameState()
+        next_game_state.next_piece_func = self.next_piece_func
+        next_game_state.data = GameStateData(
+                grid=next_grid,
+                current_piece=self.data.current_piece,
+                next_piece=self.data.next_piece,
+                rotation=self.data.rotation,
+                row=self.data.row,
+                col=self.data.col,
+                score=next_score)
+        return next_game_state
+
+    def advance(self):
+        next_game_state = self._advance_down()
+        is_game_running = True
+        if next_game_state is None:
+            next_game_state = self._advance_piece()
+            next_game_state = next_game_state._advance_purge_lines()
+            if not next_game_state.is_valid():
+                is_game_running = False
+        
+        if not is_game_running:
+            return self, False
+        return next_game_state, True
+
+
+def create_initial_game_state(next_piece_func):
+    game_state = GameState()
+    game_state.next_piece_func = next_piece_func
+    game_state.data = GameStateData(
+            grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
+                          dtype=np.byte, order="C"),
+            current_piece=next_piece_func(),
+            next_piece=next_piece_func(),
+            rotation=0, row=0, col=0, score=0)
+    return game_state

+ 2 - 1
wire_protocol.fbs

@@ -10,7 +10,8 @@ enum Command : short {
 
 enum CommandResponseError : short {
   OK = 0,
-  UNKNOWN_COMMAND = 1,
+  INVALID_COMMAND = 1,
+  UNKNOWN_COMMAND = 2,
 }
 
 table CommandRequest {

+ 2 - 1
wire_protocol/CommandResponseError.py

@@ -4,4 +4,5 @@
 
 class CommandResponseError(object):
     OK = 0
-    UNKNOWN_COMMAND = 1
+    INVALID_COMMAND = 1
+    UNKNOWN_COMMAND = 2