game_state.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. drop_count = 0
  98. while True:
  99. try_game_state.data = GameStateData(
  100. grid=next_game_state.data.grid,
  101. current_piece=next_game_state.data.current_piece,
  102. next_piece=next_game_state.data.next_piece,
  103. rotation=next_game_state.data.rotation,
  104. row=next_game_state.data.row + 1,
  105. col=next_game_state.data.col,
  106. score=next_game_state.data.score)
  107. if try_game_state.is_valid():
  108. next_game_state.data = try_game_state.data
  109. drop_count += 1
  110. else:
  111. break
  112. next_game_state.data = GameStateData(
  113. grid=next_game_state.data.grid,
  114. current_piece=next_game_state.data.current_piece,
  115. next_piece=next_game_state.data.next_piece,
  116. rotation=next_game_state.data.rotation,
  117. row=next_game_state.data.row,
  118. col=next_game_state.data.col,
  119. score=next_game_state.data.score + drop_count*2)
  120. return next_game_state
  121. def _advance_down(self):
  122. next_game_state = GameState()
  123. next_game_state.next_piece_func = self.next_piece_func
  124. next_game_state.data = GameStateData(
  125. grid=self.data.grid,
  126. current_piece=self.data.current_piece,
  127. next_piece=self.data.next_piece,
  128. rotation=self.data.rotation,
  129. row=self.data.row + 1,
  130. col=self.data.col,
  131. score=self.data.score)
  132. if next_game_state.is_valid():
  133. return next_game_state
  134. return None
  135. def _advance_piece(self):
  136. tetromino_array = tetromino_to_field_at(
  137. tetrominos.SHAPES[self.data.current_piece][self.data.rotation],
  138. self.data.row, self.data.col)
  139. next_grid = np.add(self.data.grid, tetromino_array)
  140. next_game_state = GameState()
  141. next_game_state.next_piece_func = self.next_piece_func
  142. next_game_state.data = GameStateData(
  143. grid=next_grid,
  144. current_piece=self.data.next_piece,
  145. next_piece=self.next_piece_func(),
  146. rotation=0, row=0, col=0,
  147. score=self.data.score)
  148. return next_game_state
  149. def _advance_purge_lines(self):
  150. next_grid_row = FIELD_HEIGHT-1
  151. next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  152. dtype=np.byte, order="C")
  153. lines_purged = 0
  154. for row in range(FIELD_HEIGHT-1, -1,-1):
  155. if np.all(self.data.grid[row,:]):
  156. lines_purged += 1
  157. else:
  158. next_grid[next_grid_row,:] = self.data.grid[row,:]
  159. next_grid_row -= 1
  160. lines_scores = [0, 40, 100, 300, 1200]
  161. next_score = self.data.score + lines_scores[lines_purged]
  162. next_game_state = GameState()
  163. next_game_state.next_piece_func = self.next_piece_func
  164. next_game_state.data = GameStateData(
  165. grid=next_grid,
  166. current_piece=self.data.current_piece,
  167. next_piece=self.data.next_piece,
  168. rotation=self.data.rotation,
  169. row=self.data.row,
  170. col=self.data.col,
  171. score=next_score)
  172. return next_game_state
  173. def advance(self):
  174. next_game_state = self._advance_down()
  175. is_game_running = True
  176. if next_game_state is None:
  177. next_game_state = self._advance_piece()
  178. next_game_state = next_game_state._advance_purge_lines()
  179. if not next_game_state.is_valid():
  180. is_game_running = False
  181. if not is_game_running:
  182. return self, False
  183. return next_game_state, True
  184. def create_initial_game_state(next_piece_func):
  185. game_state = GameState()
  186. game_state.next_piece_func = next_piece_func
  187. game_state.data = GameStateData(
  188. grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH),
  189. dtype=np.byte, order="C"),
  190. current_piece=next_piece_func(),
  191. next_piece=next_piece_func(),
  192. rotation=0, row=0, col=0, score=0)
  193. return game_state