はじめに
Pythonの勉強で、標準モジュールのみでテトリス(っぽいもの)を作成しました。
改版の余地はあるのですが、めんどくさいのでやらないかもしれません。
今回はコードだけ張ります。
作り方の解説は別な記事で紹介しようと思います。
仕様
- 左移動:a
- 右移動:d
- 下移動:s
- 回転:スペース
- 「Start」ボタンで開始
- 「Reset」ボタンでリセット
コード
import copy
import random
import tkinter
class block_type:
def __init__(self, cell):
self.tags = ["tag1", "tag2", "tag3", "tag4"]
self.cell = cell
def gen(self):
a = random.randrange(0, 7)
self.cnt = 0
if a == 0:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [4, -2, "tag3"], [4, -3, "tag4"]]
self._round = [
{"tag1": [1, -1], "tag2": [0, 0], "tag3": [-1, 1], "tag4": [-2, 2]},
{"tag1": [-1, 1], "tag2": [0, 0], "tag3": [1, -1], "tag4": [2, -2]},
]
elif a == 1:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [3, -1, "tag3"], [5, -1, "tag4"]]
self._round = [
{"tag1": [1, -1], "tag2": [0, 0], "tag3": [1, 1], "tag4": [-1, -1]},
{"tag1": [-1, -1], "tag2": [0, 0], "tag3": [1, -1], "tag4": [-1, 1]},
{"tag1": [-1, 1], "tag2": [0, 0], "tag3": [-1, -1], "tag4": [1, 1]},
{"tag1": [1, 1], "tag2": [0, 0], "tag3": [-1, 1], "tag4": [1, -1]},
]
elif a == 2:
self.block = [[4, 0, "tag1"], [3, 0, "tag2"], [4, -1, "tag3"], [3, -1, "tag4"]]
self._round = [
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [0, 0], "tag4": [0, 0]},
]
elif a == 3:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [5, -1, "tag3"], [5, -2, "tag4"]]
self._round = [
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [-2, 0], "tag4": [0, 2]},
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [2, 0], "tag4": [0, -2]},
]
elif a == 4:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [3, -1, "tag3"], [3, -2, "tag4"]]
self._round = [
{"tag1": [0, -2], "tag2": [0, 0], "tag3": [0, 0], "tag4": [2, 0]},
{"tag1": [0, 2], "tag2": [0, 0], "tag3": [0, 0], "tag4": [-2, 0]},
]
elif a == 5:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [4, -2, "tag3"], [3, -2, "tag4"]]
self._round = [
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [-2, 2], "tag4": [0, 2]},
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [2, -2], "tag4": [2, 0]},
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [2, 1], "tag4": [0, -1]},
{"tag1": [0, 0], "tag2": [0, 0], "tag3": [-2, -1], "tag4": [-2, -1]},
]
else:
self.block = [[4, 0, "tag1"], [4, -1, "tag2"], [4, -2, "tag3"], [5, -2, "tag4"]]
self._round = [
{"tag1": [-1, -2], "tag2": [0, 0], "tag3": [0, 0], "tag4": [-3, 0]},
{"tag1": [0, 1], "tag2": [0, 0], "tag3": [0, 0], "tag4": [2, -1]},
{"tag1": [2, 0], "tag2": [0, 0], "tag3": [0, 0], "tag4": [2, 2]},
{"tag1": [1, -1], "tag2": [0, 0], "tag3": [0, 0], "tag4": [-1, -1]},
]
for i in range(len(self._round)):
for tag in self.tags:
for n in range(2):
self._round[i][tag][n] = self._round[i][tag][n]*self.cell
return self.block
def gen_round(self):
self.round = self._round[self.cnt % len(self._round)]
return self.round
def gen_ok(self):
self.cnt += 1
class tetris_class:
def __init__(self):
self.app = tkinter.Tk()
self.app.title("Tetris")
self.start_button = tkinter.Button(
self.app,
text = "Start",
fg = "black",
command = self.click_button,
)
self.start_button.pack()
self.height_num, self.width_num = 20, 10
self.cell = 20
self._offset_w, self._offset_h, = 100, 100
self._width = self._offset_w*2 + self.cell*self.width_num
self._height = self._offset_h*2 + self.cell*self.height_num
self.toggle = False
self.block_exist_pos = []
self.blocks = block_type(self.cell)
self.canvas = tkinter.Canvas(
self.app,
width=self._width,
height=self._height,
bg="black"
)
self.canvas.pack()
self.initialize()
def initialize(self):
self.block_exist_pos = []
for i in self.canvas.find_all():
self.canvas.delete(i)
# Draw grid with 10x20
for w in range(self.width_num+1):
self.canvas.create_line(
self._offset_w + self.cell*w, self._offset_h,
self._offset_w + self.cell*w, self._offset_h + self.cell*self.height_num,
tag=w, fill="gray",width=1)
for h in range(self.height_num+1):
self.canvas.create_line(
self._offset_w, self._offset_h + self.cell*h,
self._offset_w + self.cell*self.width_num, self._offset_h + self.cell*h,
tag=h, fill="gray",width=1)
def click_button(self):
if self.toggle:
self.toggle = False
self.start_button["text"] = "Start"
else:
self.toggle = True
self.initialize()
self.start_button["text"] = "Reset"
self.gen_block()
self.app.after(500, self.move_block)
def press_a(self, _):
if self.toggle:
a = {i: (-self.cell, 0) for i in self.blocks.tags}
if self.move_en_frame(a, "side"):
for tag in self.blocks.tags:
self.canvas.move(tag, -self.cell, 0)
def press_d(self, _):
if self.toggle:
a = {i: (self.cell, 0) for i in self.blocks.tags}
if self.move_en_frame(a, "side"):
for tag in self.blocks.tags:
self.canvas.move(tag, self.cell, 0)
def press_s(self, _):
if self.toggle:
a = {i: (0, self.cell) for i in self.blocks.tags}
if self.move_en_frame(a):
for tag in self.blocks.tags:
self.canvas.move(tag, 0, self.cell)
def press_space(self, _):
if self.toggle:
if self.move_en_frame(self.blocks.gen_round(), "round"):
self.blocks.gen_ok()
for tag in self.blocks.tags:
x = self.blocks.round[tag][0]
y = self.blocks.round[tag][1]
self.canvas.move(tag, x, y)
def move_block(self):
if self.toggle:
a = {i: (0, self.cell) for i in self.blocks.tags}
if self.move_en_frame(a):
for tag in self.blocks.tags:
self.canvas.move(tag, 0, self.cell)
self.app.after(500, self.move_block)
def gen_block(self):
if self.toggle:
for x, y, tag in self.blocks.gen():
self.canvas.create_rectangle(
self._offset_w + self.cell*x, self._offset_h + self.cell*y,
self._offset_w + self.cell*(x+1), self._offset_h + self.cell*(y+1),
tag=tag, fill="gray", width=1)
def delete_line(self):
# ブロックが詰まっている行のIDとy座標の辞書を作成
cnt = {self._offset_h + self.cell*i: {} for i in range(20)}
for n, m, id in self.block_exist_pos:
try:
cnt[m].update({id: n})
except KeyError: # ワークアラウンド
self.toggle = False
return
# ブロックが10列ある行を削除
delete_line_pos = []
for m, v in cnt.items():
if len(v.keys()) != 10:
continue
delete_line_pos.append(m)
for id, n in v.items():
self.block_exist_pos.remove([n, m, id])
self.canvas.delete(id)
# 削除された行より上にあるブロックを下に移動
buf = []
for n, m, id in self.block_exist_pos:
a = 0
for i in delete_line_pos:
if i > m:
a = a + self.cell
self.canvas.move(id, 0, a)
buf.append([n, m + a, id])
self.block_exist_pos = copy.deepcopy(buf)
def prepare_next_block(self):
# 次のブロックを生成する準備
for tag in self.blocks.tags:
pos = self.canvas.bbox(tag)
self.canvas.delete(tag)
x_s = pos[0] + 1 # ワークアラウンド
y_s = pos[1] + 1 # ワークアラウンド
id = self.canvas.create_rectangle(
x_s, y_s, x_s + self.cell, y_s + self.cell, fill="red", width=1)
self.block_exist_pos.append([x_s, y_s, id])
# 一行そろったら消す
self.delete_line()
# 次のBlockを生成
self.gen_block()
def move_en_frame(self, a, meta=""):
# Blockの位置を変えて良いか
for tag in self.blocks.tags:
pos = self.canvas.bbox(tag)
x_s = pos[0] + 1
y_s = pos[1] + 1
# BlockとBlockが重なったら次のBlock
for n, m, _ in self.block_exist_pos:
if y_s + a[tag][1] == m and x_s + a[tag][0] == n:
if meta == "":
self.prepare_next_block()
return False
# Blockが一番下についたら次のBlock
if y_s + a[tag][1] > self._height - self._offset_h - 1:
if meta != "round":
self.prepare_next_block()
return False
# Blockが両端に来たらFalse
if x_s + a[tag][0] < self._offset_w or x_s + a[tag][0] >= self._width - self._offset_w:
return False
return True
def run(self):
self.app.bind("<a>", self.press_a)
self.app.bind("<d>", self.press_d)
self.app.bind("<s>", self.press_s)
self.app.bind("<space>", self.press_space)
self.app.mainloop()
if __name__ == "__main__":
a = tetris_class()
a.run()
まとめ
Pythonの標準モジュールのみでテトリスを作成しました。
コメント