瀏覽代碼

Added basic game logic in game.py

Alexey Dorokhov 3 年之前
父節點
當前提交
269edfd7bb
共有 3 個文件被更改,包括 354 次插入123 次删除
  1. 139 0
      game.py
  2. 0 123
      shapes.py
  3. 215 0
      tetrominos.py

+ 139 - 0
game.py

@@ -0,0 +1,139 @@
+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.int32, order="C")
+    right_zeros = np.zeros(shape=(array.shape[0],
+                                  FIELD_WIDTH-col_offset-array.shape[1]),
+                           dtype=np.int32, order="C")
+    array = np.concatenate((left_zeros, array, right_zeros), axis=1)
+
+    top_zeros = np.zeros(shape=(row_offset,FIELD_WIDTH),
+                         dtype=np.int32, order="C")
+    bottom_zeros = np.zeros(shape=(FIELD_HEIGHT - row_offset - array.shape[0],
+                                   FIELD_WIDTH),
+                            dtype=np.int32, 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'])
+
+
+def initial_game_state(current_piece, next_piece):
+    return GameState(grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
+                                   dtype=np.int32, order="C"),
+                     current_piece=current_piece,
+                     next_piece=next_piece,
+                     rotation=0, row=0, col=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 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 advance_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)
+    if is_game_state_valid(next_game_state):
+        return next_game_state
+    return None
+
+
+def advance_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)
+    if is_game_state_valid(next_game_state):
+        return next_game_state
+    return None
+
+
+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)
+    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)
+    return next_game_state
+
+
+def advance_game_state(game_state, command):
+    command_game_state = None
+    if command == COMMAND_LEFT:
+        command_game_state = advance_game_state_shift(game_state, -1)
+    elif command == COMMAND_RIGHT:
+        command_game_state = advance_game_state_shift(game_state, 1)
+    elif command == COMMAND_ROTATE:
+        command_game_state = advance_game_state_rotate(game_state)
+
+    if command_game_state is None:
+        command_game_state = game_state
+
+    next_game_state = advance_game_state_down(command_game_state)
+    if next_game_state is None:
+        next_game_state = advance_game_state_piece(command_game_state, 0)
+    return next_game_state

+ 0 - 123
shapes.py

@@ -1,123 +0,0 @@
-I = [['..........',
-      '...0000...',
-      '..........',
-      '..........'],
-     ['.....0....',
-      '.....0....',
-      '.....0....',
-      '.....0....'],
-     ['..........',
-      '..........',
-      '...0000...',
-      '..........'],
-     ['....0.....',
-      '....0.....',
-      '....0.....',
-      '....0.....']]
-
-O = [['..........',
-      '....00....',
-      '....00....',
-      '..........'],
-     ['..........',
-      '....00....',
-      '....00....',
-      '..........'],
-     ['..........',
-      '....00....',
-      '....00....',
-      '..........'],
-     ['..........',
-      '....00....',
-      '....00....',
-      '..........']]
-
-T = [['....0.....',
-      '...000....',
-      '..........',
-      '..........'],
-     ['....0.....',
-      '....00....',
-      '....0.....',
-      '..........'],
-     ['..........',
-      '...000....',
-      '....0.....',
-      '..........'],
-     ['....0.....',
-      '...00.....',
-      '....0.....',
-      '..........']]
-
-S = [['....00....',
-      '...00.....',
-      '..........',
-      '..........'],
-     ['....0.....',
-      '....00....',
-      '.....0....',
-      '..........'],
-     ['..........',
-      '....00....',
-      '...00.....',
-      '..........'],
-     ['...0......',
-      '...00.....',
-      '....0.....',
-      '..........']]
-
-Z = [['...00.....',
-      '....00....',
-      '..........',
-      '..........'],
-     ['.....0....',
-      '....00....',
-      '....0.....',
-      '..........'],
-     ['..........',
-      '...00.....',
-      '....00....',
-      '..........'],
-     ['....0.....',
-      '...00.....',
-      '...0......',
-      '..........']]
-
-J = [['...0......',
-      '...000....',
-      '..........',
-      '..........'],
-     ['....00....',
-      '....0.....',
-      '....0.....',
-      '..........'],
-     ['..........',
-      '...000....',
-      '.....0....',
-      '..........'],
-     ['....0.....',
-      '....0.....',
-      '...00.....',
-      '..........']]
-
-L = [['.....0....',
-      '...000....',
-      '..........',
-      '..........'],
-     ['....0.....',
-      '....0.....',
-      '....00....',
-      '..........'],
-     ['..........',
-      '...000....',
-      '...0......',
-      '..........'],
-     ['...00.....',
-      '....0.....',
-      '....0.....',
-      '..........']]
-
-SHAPES = [S, Z, I, O, J, L, T]
-SHAPE_COLORS = [(0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 255, 0),
-                (255, 165, 0), (0, 0, 255), (128, 0, 128)]
-

+ 215 - 0
tetrominos.py

@@ -0,0 +1,215 @@
+"""
+All tetrominos and their rotations
+"""
+
+import collections
+import numpy as np
+
+def convert_to_array(tetromino):
+    rows = len(tetromino)
+    cols = len(tetromino[0])
+    array_tetromino = np.zeros(shape=(rows,cols),
+                               dtype=np.int32, order="C")
+    for row_index, row in enumerate(tetromino):
+        for col_index, col in enumerate(row):
+            array_tetromino[row_index,col_index] = 0 if col == '.' else 1
+    return array_tetromino
+
+
+def crop_col_right_once(tetromino):
+    if not np.any(tetromino[:,-1]):
+        return tetromino[:,0:-1]
+    return None
+
+
+def crop_col_right(tetromino):
+    while True:
+        new_tetromino = crop_col_right_once(tetromino)
+        if new_tetromino is None:
+            return tetromino
+        tetromino = new_tetromino
+
+
+def crop_row_bottom_once(tetromino):
+    if not np.any(tetromino[-1,:]):
+        return tetromino[0:-1,:]
+    return None
+
+
+def crop_row_bottom(tetromino):
+    while True:
+        new_tetromino = crop_row_bottom_once(tetromino)
+        if new_tetromino is None:
+            return tetromino
+        tetromino = new_tetromino
+
+
+def crop_col_left_once(tetromino):
+    if not np.any(tetromino[:,0]):
+        return tetromino[:,1:]
+    return None
+
+
+def crop_col_left(tetromino):
+    col_offset = 0
+    while True:
+        new_tetromino = crop_col_left_once(tetromino)
+        if new_tetromino is None:
+            return (tetromino, col_offset)
+        tetromino = new_tetromino
+        col_offset += 1
+
+
+def crop_row_top_once(tetromino):
+    if not np.any(tetromino[0,:]):
+        return tetromino[1:,:]
+    return None
+
+
+def crop_row_top(tetromino):
+    row_offset = 0
+    while True:
+        new_tetromino = crop_row_top_once(tetromino)
+        if new_tetromino is None:
+            return (tetromino, row_offset)
+        tetromino = new_tetromino
+        row_offset += 1
+
+
+Tetromino = collections.namedtuple('Tetromino',
+                                   ['data', 'col_offset', 'row_offset'])
+
+def crop(tetromino):
+    new_tetromino = crop_col_right(crop_row_bottom(tetromino))
+    new_tetromino, c_off = crop_col_left(new_tetromino)
+    new_tetromino, r_off = crop_row_top(new_tetromino)
+    return Tetromino(data=new_tetromino, col_offset=c_off, row_offset=r_off)
+
+
+def convert_to_tuples(tetrominos):
+    return list(map(lambda x: crop(convert_to_array(x)), tetrominos))
+
+
+
+I = convert_to_tuples([['..........',
+                        '...0000...',
+                        '..........',
+                        '..........'],
+                       ['.....0....',
+                        '.....0....',
+                        '.....0....',
+                        '.....0....'],
+                       ['..........',
+                        '..........',
+                        '...0000...',
+                        '..........'],
+                       ['....0.....',
+                        '....0.....',
+                        '....0.....',
+                        '....0.....']])
+
+O = convert_to_tuples([['..........',
+                        '....00....',
+                        '....00....',
+                        '..........'],
+                       ['..........',
+                        '....00....',
+                        '....00....',
+                        '..........'],
+                       ['..........',
+                        '....00....',
+                        '....00....',
+                        '..........'],
+                       ['..........',
+                        '....00....',
+                        '....00....',
+                        '..........']])
+
+T = convert_to_tuples([['....0.....',
+                        '...000....',
+                        '..........',
+                        '..........'],
+                       ['....0.....',
+                        '....00....',
+                        '....0.....',
+                        '..........'],
+                       ['..........',
+                        '...000....',
+                        '....0.....',
+                        '..........'],
+                       ['....0.....',
+                        '...00.....',
+                        '....0.....',
+                        '..........']])
+
+S = convert_to_tuples([['....00....',
+                        '...00.....',
+                        '..........',
+                        '..........'],
+                       ['....0.....',
+                        '....00....',
+                        '.....0....',
+                        '..........'],
+                       ['..........',
+                        '....00....',
+                        '...00.....',
+                        '..........'],
+                       ['...0......',
+                        '...00.....',
+                        '....0.....',
+                        '..........']])
+
+Z = convert_to_tuples([['...00.....',
+                        '....00....',
+                        '..........',
+                        '..........'],
+                       ['.....0....',
+                        '....00....',
+                        '....0.....',
+                        '..........'],
+                       ['..........',
+                        '...00.....',
+                        '....00....',
+                        '..........'],
+                       ['....0.....',
+                        '...00.....',
+                        '...0......',
+                        '..........']])
+
+J = convert_to_tuples([['...0......',
+                        '...000....',
+                        '..........',
+                        '..........'],
+                       ['....00....',
+                        '....0.....',
+                        '....0.....',
+                        '..........'],
+                       ['..........',
+                        '...000....',
+                        '.....0....',
+                        '..........'],
+                       ['....0.....',
+                        '....0.....',
+                        '...00.....',
+                        '..........']])
+
+L = convert_to_tuples([['.....0....',
+                        '...000....',
+                        '..........',
+                        '..........'],
+                       ['....0.....',
+                        '....0.....',
+                        '....00....',
+                        '..........'],
+                       ['..........',
+                        '...000....',
+                        '...0......',
+                        '..........'],
+                       ['...00.....',
+                        '....0.....',
+                        '....0.....',
+                        '..........']])
+
+SHAPES = [S, Z, I, O, J, L, T]
+SHAPE_COLORS = [(0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 255, 0),
+                (255, 165, 0), (0, 0, 255), (128, 0, 128)]