game_state.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import collections
  2. import numpy as np
  3. import tetrominos
  4. FIELD_WIDTH = 10
  5. FIELD_HEIGHT = 20
  6. COMMAND_NONE = 0
  7. COMMAND_LEFT = 1
  8. COMMAND_RIGHT = 2
  9. COMMAND_ROTATE = 3
  10. def tetromino_to_field_at(tetromino, row, col):
  11. col_offset = col + tetromino.col_offset
  12. row_offset = row + tetromino.row_offset
  13. if col_offset < 0 or row_offset < 0:
  14. return None
  15. if col_offset + tetromino.data.shape[1] > FIELD_WIDTH:
  16. return None
  17. if row_offset + tetromino.data.shape[0] > FIELD_HEIGHT:
  18. return None
  19. array = tetromino.data
  20. left_zeros = np.zeros(shape=(array.shape[0], col_offset),
  21. dtype=np.byte, order="C")
  22. right_zeros = np.zeros(shape=(array.shape[0],
  23. FIELD_WIDTH-col_offset-array.shape[1]),
  24. dtype=np.byte, order="C")
  25. array = np.concatenate((left_zeros, array, right_zeros), axis=1)
  26. top_zeros = np.zeros(shape=(row_offset,FIELD_WIDTH),
  27. dtype=np.byte, order="C")
  28. bottom_zeros = np.zeros(shape=(FIELD_HEIGHT - row_offset - array.shape[0],
  29. FIELD_WIDTH),
  30. dtype=np.byte, order="C")
  31. array = np.concatenate((top_zeros, array, bottom_zeros), axis=0)
  32. return array
  33. GameStateData = collections.namedtuple('GameStateData',
  34. ['grid', 'current_piece', 'next_piece',
  35. 'rotation', 'row', 'col', 'score'])
  36. class GameState:
  37. def __init__(self):
  38. self.data = None
  39. self.next_piece_func = None
  40. def is_valid(self):
  41. tetromino_array = tetromino_to_field_at(
  42. tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
  43. self.data.row, self.data.col)
  44. if tetromino_array is None:
  45. return False
  46. total_field = np.add(self.data.grid, tetromino_array)
  47. if np.any(np.vectorize(lambda x: x > 1)(total_field)):
  48. return False
  49. return True
  50. def get_field(self):
  51. tetromino_array = tetromino_to_field_at(
  52. tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
  53. self.data.row, self.data.col)
  54. total_field = np.add(self.data.grid, tetromino_array)
  55. total_field = np.add(total_field, tetromino_array)
  56. return total_field
  57. def shift(self, col_offset):
  58. next_game_state = GameState()
  59. next_game_state.next_piece_func = self.next_piece_func
  60. next_game_state.data = GameStateData(
  61. grid=self.data.grid,
  62. current_piece=self.data.current_piece,
  63. next_piece=self.data.next_piece,
  64. rotation=self.data.rotation,
  65. row=self.data.row,
  66. col=self.data.col + col_offset,
  67. score=self.data.score)
  68. if next_game_state.is_valid():
  69. return next_game_state
  70. return None
  71. def rotate(self):
  72. next_game_state = GameState()
  73. next_game_state.next_piece_func = self.next_piece_func
  74. next_game_state.data = GameStateData(
  75. grid=self.data.grid,
  76. current_piece=self.data.current_piece,
  77. next_piece=self.data.next_piece,
  78. rotation=(self.data.rotation + 1) % 4,
  79. row=self.data.row,
  80. col=self.data.col,
  81. score=self.data.score)
  82. if next_game_state.is_valid():
  83. return next_game_state
  84. return None
  85. def land(self):
  86. next_game_state = GameState()
  87. next_game_state.next_piece_func = self.next_piece_func
  88. next_game_state.data = GameStateData(
  89. grid=self.data.grid,
  90. current_piece=self.data.current_piece,
  91. next_piece=self.data.next_piece,
  92. rotation=self.data.rotation,
  93. row=self.data.row,
  94. col=self.data.col,
  95. score=self.data.score)
  96. try_game_state = GameState()
  97. while True:
  98. try_game_state.data = GameStateData(
  99. grid=next_game_state.data.grid,
  100. current_piece=next_game_state.data.current_piece,
  101. next_piece=next_game_state.data.next_piece,
  102. rotation=next_game_state.data.rotation,
  103. row=next_game_state.data.row + 1,
  104. col=next_game_state.data.col,
  105. score=next_game_state.data.score)
  106. if try_game_state.is_valid():
  107. next_game_state.data = try_game_state.data
  108. else:
  109. break
  110. return next_game_state
  111. def _advance_down(self):
  112. next_game_state = GameState()
  113. next_game_state.next_piece_func = self.next_piece_func
  114. next_game_state.data = GameStateData(
  115. grid=self.data.grid,
  116. current_piece=self.data.current_piece,
  117. next_piece=self.data.next_piece,
  118. rotation=self.data.rotation,
  119. row=self.data.row + 1,
  120. col=self.data.col,
  121. score=self.data.score)
  122. if next_game_state.is_valid():
  123. return next_game_state
  124. return None
  125. def _advance_piece(self):
  126. tetromino_array = tetromino_to_field_at(
  127. tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
  128. self.data.row, self.data.col)
  129. next_grid = np.add(self.data.grid, tetromino_array)
  130. next_game_state = GameState()
  131. next_game_state.next_piece_func = self.next_piece_func
  132. next_game_state.data = GameStateData(
  133. grid=next_grid,
  134. current_piece=self.data.next_piece,
  135. next_piece=self.next_piece_func(),
  136. rotation=0, row=0, col=0,
  137. score=self.data.score)
  138. return next_game_state
  139. def _advance_purge_lines(self):
  140. next_grid_row = FIELD_HEIGHT-1
  141. next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  142. dtype=np.byte, order="C")
  143. next_score = self.data.score
  144. for row in range(FIELD_HEIGHT-1, -1,-1):
  145. if np.all(self.data.grid[row,:]):
  146. next_score += 1
  147. else:
  148. next_grid[next_grid_row,:] = self.data.grid[row,:]
  149. next_grid_row -= 1
  150. next_game_state = GameState()
  151. next_game_state.next_piece_func = self.next_piece_func
  152. next_game_state.data = GameStateData(
  153. grid=next_grid,
  154. current_piece=self.data.current_piece,
  155. next_piece=self.data.next_piece,
  156. rotation=self.data.rotation,
  157. row=self.data.row,
  158. col=self.data.col,
  159. score=next_score)
  160. return next_game_state
  161. def advance(self):
  162. next_game_state = self._advance_down()
  163. is_game_running = True
  164. if next_game_state is None:
  165. next_game_state = self._advance_piece()
  166. next_game_state = next_game_state._advance_purge_lines()
  167. if not next_game_state.is_valid():
  168. is_game_running = False
  169. if not is_game_running:
  170. return self, False
  171. return next_game_state, True
  172. def create_initial_game_state(next_piece_func):
  173. game_state = GameState()
  174. game_state.next_piece_func = next_piece_func
  175. game_state.data = GameStateData(
  176. grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  177. dtype=np.byte, order="C"),
  178. current_piece=next_piece_func(),
  179. next_piece=next_piece_func(),
  180. rotation=0, row=0, col=0, score=0)
  181. return game_state