PythonでTETRISアルゴリズムを考える 2
前回の続き。
# テトリミノの移動や回転をする
def move(self, to: str=' ') -> bool:
pt = self.pt
# left
if to == 'h':
q_li = [pt[0] , pt[1]-1, self.rot]
# right
elif to == 'l':
q_li = [pt[0] , pt[1]+1, self.rot]
# rotate right
elif to == 'f':
q_li = [pt[0] , pt[1] ,(self.rot+1)%4]
# rotate left
elif to == 'a':
q_li = [pt[0] , pt[1] ,(self.rot-1)%4]
# down
else:
q_li = [pt[0]+1, pt[1] , self.rot]
for ix in self.t4mino_li[self.cur][q_li[-1]]:
if ix[0]+q_li[0] >= self.board_size[0] \
or ix[1]+q_li[1] < 0 \
or ix[1]+q_li[1] >= self.board_size[1] \
or self.board[ix[0]+q_li[0], ix[1]+q_li[1]] != 0:
return False
pt[0], pt[1], self.rot = q_li
return True
移動や回転ができる場合はTrue
、そうでなければFalse
を返すようにしています。
このように書き換えてからプログラムを再度実行してみましょう!
0| 5 |
1| 555 |
2| |
...
19| |
--+----------+
h
...
h
0| 5 |
1|555 |
2| |
...
19| |
--+----------+
h
0| 5 |
1|555 |
2| |
...
19| |
--+----------+
ちゃんとのめり込みをしないようになりました!
6. テトリミノを保存する
積み上がったテトリミノを保存するために、save_board()
を宣言します。ついでに、テトリミノが横で揃ったときに消す操作も追加します。
class Tetris(object):...
# ボードに保存する
def save_board(self):
pt = self.pt
for ix in self.t4mino_li[self.cur][self.rot]:
self.board[ix[0]+pt[0], ix[1]+pt[1]] = self.cur
# flush
for i in range(self.board_size[0]):
if (self.board[i] != 0).all():
for i_ in reversed(range(i)):
self.board[i_+1] = self.board[i_]
これでボードに保存するための関数が完成しました。
あとはこれをgame()
に書き込んでゆきます。
class Tetris(object):...
# ゲームのメイン部分
def game(self):
while True:
self.display()
_pt = self.pt.copy()
key = input()
self.move(key)
self.move() # テトリミノを1個下げる
# テトリミノが動いていない場合、新たにテトリミノを生成する
if _pt == self.pt:
self.save_board()
self.gen_t4mino()
print('END')
7. 終了条件を書く
ついにラストです!
テトリスは、0行目の中央4マスに動かせないテトリミノがある場合、ゲームオーバーとなります。
そのために関数yet()
を宣言してゆきます。ついでに、game()
も書き換えます。
class Tetris(object):...
# ゲームのメイン部分
def game(self):
while self.yet():
self.display()
_pt = self.pt.copy()
key = input()
self.move(key)
self.move() # テトリミノを1個下げる
if _pt == self.pt:
self.save_board()
self.gen_t4mino()
print('END')
# ゲームの終了条件
def yet(self) -> bool:
mid = self.board_size[1] // 2
if (self.board[0, mid-2:mid+2] == 0).all():
return True
return False
これでテトリスをPythonで実現できました!
さいごに
テトリスは最初に言った通り様々な言語で実現されています。
でもほとんど最初から自力で作るのは至難でした。ここまでで8時間近くかかってしまいました……。
他にもいろいろ作ってみたいものが見つかったら、また書いてゆきます。
ソースコード
「分解されてて読みにくい!」という人のために、ソースコードを公開します。ほとんどアルゴリズム自体なので、著作権フリーみたいな感じです。
pyqt
とかpygame
とかに組み込んでみて、動作を確認してみるのもありだと思います。
import numpy as np
class Tetris(object):
def __init__(self):
self.board_size = [20, 10]
self.init_t4mino()
self.init_board()
self.gen_t4mino()
# ボードを初期化する
def init_board(self):
self.board = np.zeros(self.board_size, dtype=np.int)
# テトリミノを初期化する
def init_t4mino(self):
# including rotation table
self.t4mino_li = [
# i
[[[1, i] for i in range(4)],
[[i, 1] for i in range(4)]] * 2,
# o
[[[0, 0], [0, 1], [1, 0], [1, 1]]] * 4,
# t
[[[0, 1]] + [[1, i] for i in range(3)],
[[1, 2]] + [[i, 1] for i in range(3)],
[[2, 1]] + [[1, i] for i in range(3)],
[[1, 0]] + [[i, 1] for i in range(3)]],
# j
[[[0, 0]] + [[1, i] for i in range(3)],
[[0, 2]] + [[i, 1] for i in range(3)],
[[2, 2]] + [[1, i] for i in range(3)],
[[2, 0]] + [[i, 1] for i in range(3)]],
# l
[[[0, 2]] + [[1, i] for i in range(3)],
[[2, 2]] + [[i, 1] for i in range(3)],
[[2, 0]] + [[1, i] for i in range(3)],
[[0, 0]] + [[i, 1] for i in range(3)]],
# s
[[[0, 1], [0, 2], [1, 0], [1, 1]],
[[0, 0], [1, 0], [1, 1], [2, 1]]] * 2,
# t
[[[0, 0], [0, 1], [1, 1], [1, 2]],
[[0, 1], [1, 0], [1, 1], [2, 0]]] * 2
]
# ゲームのメイン部分
def game(self):
while self.yet():
self.display()
_pt = self.pt.copy()
key = input()
self.move(key)
self.move() # テトリミノを1個下げる
if _pt == self.pt:
self.save_board()
self.gen_t4mino()
print('END')
# ゲームの終了条件
def yet(self) -> bool:
mid = self.board_size[1] // 2
if (self.board[0, mid-2:mid+2] == 0).all():
return True
return False
# ボードを保存する
def save_board(self):
pt = self.pt
for ix in self.t4mino_li[self.cur][self.rot]:
self.board[ix[0]+pt[0], ix[1]+pt[1]] = self.cur + 1
# flush
for i in range(self.board_size[0]):
if (self.board[i] != 0).all():
for i_ in reversed(range(i)):
self.board[i_+1] = self.board[i_]
# ボードに表示する要素
def element(self, i: int, j: int) -> str:
ix_li = self.t4mino_li[self.cur][self.rot]
pt = self.pt
for ix in ix_li:
if ix[0]+pt[0] == i and ix[1]+pt[1] == j:
return self.cur + 1
tmp = self.board[i, j]
return ' ' if tmp == 0 else tmp
# ボードの表示
def display(self):
ix_li = self.t4mino_li[self.cur][self.rot]
pt = self.pt
for i in range(self.board_size[0]):
print('{:>2}|'.format(i), end='')
for j in range(self.board_size[1]):
print(self.element(i, j), end='')
print('|')
print('--+' + '-' * self.board_size[1] + '+')
# テトリミノの移動や回転をする
def move(self, to: str=' ') -> bool:
pt = self.pt
# left
if to == 'h':
q_li = [pt[0] , pt[1]-1, self.rot]
# right
elif to == 'l':
q_li = [pt[0] , pt[1]+1, self.rot]
# rotate right
elif to == 'f':
q_li = [pt[0] , pt[1] ,(self.rot+1)%4]
# rotate left
elif to == 'a':
q_li = [pt[0] , pt[1] ,(self.rot-1)%4]
# down
else:
q_li = [pt[0]+1, pt[1] , self.rot]
for ix in self.t4mino_li[self.cur][q_li[-1]]:
if ix[0]+q_li[0] >= self.board_size[0] \
or ix[1]+q_li[1] < 0 \
or ix[1]+q_li[1] >= self.board_size[1] \
or self.board[ix[0]+q_li[0], ix[1]+q_li[1]] != 0:
return False
pt[0], pt[1], self.rot = q_li
return True
# テトリミノを生成する
def gen_t4mino(self):
self.cur = np.random.randint(7)
# i, otjlst
self.pt = [0, self.board_size[1] // 2 - (2 if self.cur == 0 else 1)]
self.pt = [0, self.board_size[1] // 2 - 1]
self.rot = 0
if __name__ == '__main__':
t = Tetris()
t.game()
- 前の記事
PythonでTETRISアルゴリズムを考える 1 2019.12.03
- 次の記事
ほぼゼロからニューロンモデルのプログラムを作る 2019.12.04