game_state.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 shift_left(self):
  72. return self._shift(-1)
  73. def shift_right(self):
  74. return self._shift(1)
  75. def rotate(self):
  76. next_game_state = GameState()
  77. next_game_state.next_piece_func = self.next_piece_func
  78. next_game_state.data = GameStateData(
  79. grid=self.data.grid,
  80. current_piece=self.data.current_piece,
  81. next_piece=self.data.next_piece,
  82. rotation=(self.data.rotation + 1) % 4,
  83. row=self.data.row,
  84. col=self.data.col,
  85. score=self.data.score)
  86. if next_game_state.is_valid():
  87. return next_game_state
  88. return None
  89. def land(self):
  90. next_game_state = GameState()
  91. next_game_state.next_piece_func = self.next_piece_func
  92. next_game_state.data = GameStateData(
  93. grid=self.data.grid,
  94. current_piece=self.data.current_piece,
  95. next_piece=self.data.next_piece,
  96. rotation=self.data.rotation,
  97. row=self.data.row,
  98. col=self.data.col,
  99. score=self.data.score)
  100. try_game_state = GameState()
  101. drop_count = 0
  102. while True:
  103. try_game_state.data = GameStateData(
  104. grid=next_game_state.data.grid,
  105. current_piece=next_game_state.data.current_piece,
  106. next_piece=next_game_state.data.next_piece,
  107. rotation=next_game_state.data.rotation,
  108. row=next_game_state.data.row + 1,
  109. col=next_game_state.data.col,
  110. score=next_game_state.data.score)
  111. if try_game_state.is_valid():
  112. next_game_state.data = try_game_state.data
  113. drop_count += 1
  114. else:
  115. break
  116. next_game_state.data = GameStateData(
  117. grid=next_game_state.data.grid,
  118. current_piece=next_game_state.data.current_piece,
  119. next_piece=next_game_state.data.next_piece,
  120. rotation=next_game_state.data.rotation,
  121. row=next_game_state.data.row,
  122. col=next_game_state.data.col,
  123. score=next_game_state.data.score + drop_count*2)
  124. return next_game_state
  125. def down(self):
  126. next_game_state = GameState()
  127. next_game_state.next_piece_func = self.next_piece_func
  128. next_game_state.data = GameStateData(
  129. grid=self.data.grid,
  130. current_piece=self.data.current_piece,
  131. next_piece=self.data.next_piece,
  132. rotation=self.data.rotation,
  133. row=self.data.row + 1,
  134. col=self.data.col,
  135. score=self.data.score + 1)
  136. if next_game_state.is_valid():
  137. return next_game_state
  138. return None
  139. def _advance_down(self):
  140. next_game_state = GameState()
  141. next_game_state.next_piece_func = self.next_piece_func
  142. next_game_state.data = GameStateData(
  143. grid=self.data.grid,
  144. current_piece=self.data.current_piece,
  145. next_piece=self.data.next_piece,
  146. rotation=self.data.rotation,
  147. row=self.data.row + 1,
  148. col=self.data.col,
  149. score=self.data.score)
  150. if next_game_state.is_valid():
  151. return next_game_state
  152. return None
  153. def _advance_piece(self):
  154. tetromino_array = tetromino_to_field_at(
  155. tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
  156. self.data.row, self.data.col)
  157. next_grid = np.add(self.data.grid, tetromino_array)
  158. next_game_state = GameState()
  159. next_game_state.next_piece_func = self.next_piece_func
  160. next_game_state.data = GameStateData(
  161. grid=next_grid,
  162. current_piece=self.data.next_piece,
  163. next_piece=self.next_piece_func(),
  164. rotation=0, row=0, col=0,
  165. score=self.data.score)
  166. return next_game_state
  167. def _advance_purge_lines(self):
  168. next_grid_row = FIELD_HEIGHT-1
  169. next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  170. dtype=np.byte, order="C")
  171. lines_purged = 0
  172. for row in range(FIELD_HEIGHT-1, -1,-1):
  173. if np.all(self.data.grid[row,:]):
  174. lines_purged += 1
  175. else:
  176. next_grid[next_grid_row,:] = self.data.grid[row,:]
  177. next_grid_row -= 1
  178. lines_scores = [0, 40, 100, 300, 1200]
  179. next_score = self.data.score + lines_scores[lines_purged]
  180. next_game_state = GameState()
  181. next_game_state.next_piece_func = self.next_piece_func
  182. next_game_state.data = GameStateData(
  183. grid=next_grid,
  184. current_piece=self.data.current_piece,
  185. next_piece=self.data.next_piece,
  186. rotation=self.data.rotation,
  187. row=self.data.row,
  188. col=self.data.col,
  189. score=next_score)
  190. return next_game_state
  191. def advance(self):
  192. next_game_state = self._advance_down()
  193. is_game_running = True
  194. if next_game_state is None:
  195. next_game_state = self._advance_piece()
  196. next_game_state = next_game_state._advance_purge_lines()
  197. if not next_game_state.is_valid():
  198. is_game_running = False
  199. if not is_game_running:
  200. return self, False
  201. return next_game_state, True
  202. def create_initial_game_state(next_piece_func):
  203. game_state = GameState()
  204. game_state.next_piece_func = next_piece_func
  205. game_state.data = GameStateData(
  206. grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  207. dtype=np.byte, order="C"),
  208. current_piece=next_piece_func(),
  209. next_piece=next_piece_func(),
  210. rotation=0, row=0, col=0, score=0)
  211. return game_state