プログラミング上達講座の2回目です。
テトリスを題材にしてプログラムを考えてみましょう。
解説動画はこちら
テトリスのデータ構造を考える
テトリス
は4つのブロックで構成されたテトリミノ
を
フィールドに並べていくパズルゲームです。
このようなパズルゲームは
プログラミングの題材には打って付け!!
早速考えてみましょう。
1.テトリスゲームの素材を描画してみよう
1.1フィールドのデータ化
盤面(10 x 20マス)のフィールドと
上方以外を囲むブロック下記のような
フィールドをデータ化してみよう。
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■□□□□□□□□□□■
■■■■■■■■■■■■
ヒント:
白黒のマスを1つずつ表示させるのは面倒臭いので
繰り返しを用いてみよう。
全体では12マスかける21行となる。
1.1回答
単純に上記になるようにデータを作成して出力するだけ。
データをひとまとめにしておくと再利用しやすくなります。
縦横の揃ったデータはリスト型を用いると
表現しやすく管理しやすくなります。
リストの掛け算は要素の繰り返し足し算は要素の連結となります。
data = [['■'] + ['□']*10 + ['■']]*20 + [['■']*12]
for y in data:
print(''.join(y))
この場合は文字としてデータを出力しますが
パズルゲームの場合文字列でデータを格納すると
あとあと面倒い事もあるので数値で考えるケースが多いです。
data = [[1] + [0]*10 + [1]]*20 + [[1]*12]
for y in data:
for x in y:
print('■' if x==1 else '□',end='')
print()
■なら1
□なら0としてデータを格納して出力時に変換します。
これで結果は一緒になります。
1.2テトリミノのデータ化
テトリミノ(7つ)
□■□□ □■□□ □■□□ □□■□ □■□□ □□■□ □□□□
□■□□ □■■□ □■□□ □□■□ □■■□ □■■□ □■■□
□■□□ □■□□ □■■□ □■■□ □□■□ □■□□ □■■□
□■□□ □□□□ □□□□ □□□□ □□□□ □□□□ □□□□
上記のような7種類のテトリミノをデータ化してみよう
1.2回答
こちらもゲームで用いる際には数値や文字を値とするリスト型のような
縦横の個数が決まったデータ型を用いると表現しやすくなります。
7つのテトリミノは独立して考え回転などを考慮すると
縦横4x4マスのリスト型で定義するのが再利用しやすい形でしょう。
line = [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]]
tblo = [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
lr = [[0,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,0,0]]
ll = [[0,0,1,0],[0,0,1,0],[0,1,1,0],[0,0,0,0]]
hr = [[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]]
hl = [[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
sq = [[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]]
t_minos = [line,tblo,lr,ll,hr,hl,sq]
for mino in t_minos:
for y in mino:
for x in y:
print('■' if x==1 else '□',end='')
print()
print()
□■□□
□■□□
□■□□
□■□□
□■□□
□■■□
□■□□
□□□□
□■□□
□■□□
□■■□
□□□□
□□■□
□□■□
□■■□
□□□□
□■□□
□■■□
□□■□
□□□□
□□■□
□■■□
□■□□
□□□□
□□□□
□■■□
□■■□
□□□□
2.テトリミノを回転させる関数を作ってみよう
先ほど作ったテトリミノのデータを使って
90度ずつ回転を行う関数rotate
を作成しよう
def rotate(data , 角度):
処理
return data
□■□□
□■■□
□■□□
□□□□
これを90度回転させると
□□□□
□■■■
□□■□
□□□□
2.回答
まずテトリミノのデータを用意します。
data = [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
毎回描画コードを書くのは面倒なので関数化します。
def print_block(block):
for y in block:
for x in y:
print('■' if x==1 else '□',end='')
print()
print_block(data)
□■□□
□■■□
□■□□
□□□□
2.回答 [::-1] とzip関数を用いる方法
リストに対して[::-1]
を用いると元のリストの浅いコピーを逆順で作成します。
zip
関数は各引数の先頭から1つの要素を繰り返し抽出しTuple
を作成します。
合わせると要素が90度回転したことになります。
data = [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
print_block(data[::-1])
□□□□
□■□□
□■■□
□■□□
print_block(zip(*data[::-1]))
□□□□
□■■■
□□■□
□□□□
関数にまとめると
def rotate(data,num):
tmp = data.copy()
if num==0:
return tmp
rotated = list(map(list, zip(*tmp[::-1])))
for i in range(num//90-1):
rotated = list(map(list, zip(*rotated[::-1])))
return rotated
data = [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
block = rotate(data,270)
print_block(block)
□□□□
□■□□
■■■□
□□□□
2.回答 numpyライブラリを用いる方法
numpyライブラリにはこうした行列のようなデータを
操作する機能を備えたものがあります。
numpy.rot90(データ , 回転数)
数値の配列を反時計回りに90度回転させる
回転数は0~を指定 , 1で90度回転
回転数を-の値にすると時計回り
import numpy as np
def rotate2(data,num):
return np.rot90(data.copy(),-num//90)
data = [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]]
block = rotate2(data,90)
print_block(block)
□□□□
□■■■
□□■□
□□□□
こちらは2行で出来てしまいますね。
まとめ
データ構造の考え方とデータの操作方法は
パズルゲームがおすすめです。
今後もやっていきますので色々解いてみて下さい。
それでは。
作者の情報
乙pyのHP:
http://www.otupy.net/
Youtube:
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw
Twitter:
https://twitter.com/otupython