| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- 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 shift_left(self):
- return self._shift(-1)
- def shift_right(self):
- return self._shift(1)
- 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()
- drop_count = 0
- 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
- drop_count += 1
- else:
- break
- next_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,
- col=next_game_state.data.col,
- score=next_game_state.data.score + drop_count*2)
- return next_game_state
- def 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 + 1)
- if next_game_state.is_valid():
- return next_game_state
- return None
- 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")
- lines_purged = 0
- for row in range(FIELD_HEIGHT-1, -1,-1):
- if np.all(self.data.grid[row,:]):
- lines_purged += 1
- else:
- next_grid[next_grid_row,:] = self.data.grid[row,:]
- next_grid_row -= 1
- lines_scores = [0, 40, 100, 300, 1200]
- next_score = self.data.score + lines_scores[lines_purged]
- 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
|