Python でつくるガイスター、連載第 4 回です。
はマウス操作で駒の初期配置を決められるようになりました。今回はゲーム本番での初期盤面を描画するところまでやっていきます。
モジュールに切り分ける
駒の初期配置を決定するモジュールと、ゲーム本番での動作を規定するモジュールを分けて書きたいので、
共通部分だけmain.py
に置いて、そこから各モジュールを呼び出す形にします。
setup.py1import sys23import pygame4from pygame.locals import *56from config import IVORY7import draw, mouse8910def _check_color(colors):11'''12駒がすべて配置済みで赤と青が半分ずつあるか13-> bool1415colors : list <- [str]16色のリスト17'''18return (len(colors) == 819and len([s for s in colors if s == 'R'])20== len([s for s in colors if s == 'B']))212223def main(screen, font, select_snd, decide_snd, forbid_snd):24# 配置を決定する側25# 0 - 先攻, 1 - 後攻26_state = 027# 決定された初期配置28# [{(int, int): Piece}]29_order = [{}, {}]3031while True:32satisfied = _check_color(list(_order[_state].values()))3334screen.fill(IVORY)35draw.setup(screen, font, _state, _order[_state], not satisfied)36pygame.display.update()3738# イベントハンドリング39for event in pygame.event.get():40# 閉じるボタン41if event.type == QUIT:42pygame.quit()43sys.exit()44# マウスクリック45if event.type == MOUSEBUTTONDOWN:46# 左47if event.button == 1:48_mouse_pos = event.pos49_square_pos = tuple(mouse.chcoord(_mouse_pos))5051for i in range(1, 5):52for j in range(2, 4):53if _square_pos == (i, j):54select_snd.play()55_order[_state][(i, j)] = 'R'5657if mouse.on_area(*_mouse_pos, 500, 530, 80, 50):58if satisfied:59decide_snd.play()60_state += 161else:62forbid_snd.play()63# 右64elif event.button == 3:65_mouse_pos = event.pos66_square_pos = tuple(mouse.chcoord(_mouse_pos))6768for i in range(1, 5):69for j in range(2, 4):70if _square_pos == (i, j):71select_snd.play()72_order[_state][(i, j)] = 'B'73# キー74if event.type == KEYDOWN:75# Esc キー76if event.key == K_ESCAPE:77pygame.quit()78sys.exit()
game.py1import sys23import pygame4from pygame.locals import *56from config import IVORY789def main(screen):10while True:11screen.fill(IVORY)12pygame.display.update()1314# イベントハンドリング15for event in pygame.event.get():16# 閉じるボタン17if event.type == QUIT:18pygame.quit()19sys.exit()20# キー21if event.type == KEYDOWN:22# Esc キー23if event.key == K_ESCAPE:24pygame.quit()25sys.exit()
main.py1import sys23import pygame4from pygame.locals import *56from config import DISP_SIZE7import setup, game8910if __name__ == '__main__':11pygame.init()12# 音声の設定13snd = pygame.mixer.Sound14select_snd = snd('./sounds/select.wav')15decide_snd = snd('./sounds/decide.wav')16forbid_snd = snd('./sounds/forbid.wav')1718pygame.display.set_caption('Geister')19font = pygame.font.SysFont('hg丸ゴシックmpro', 16)202122setup.main(screen, font, select_snd, decide_snd, forbid_snd)2324game.main(screen)
setup.py
でのwhile
ループが終了すればgame.py
でのループが開始します。
駒クラスを作成する
盤面のデータは、位置:駒の辞書型にすることにします。
駒はオブジェクトとして、どちら側の駒か、色は何かなどの属性を持たせます。
piece.py1import numpy as np23class Piece:4def __init__(self, color, side):5self.color = color # 赤('R') or 青('B')6self.side = side # 先攻(0) or 後攻(1)78def __repr__(self):9return self.color + str(self.side)1011def covering_squares(self, pos):12'''13可能な移動先のマスのリスト14-> list <- [(int, int)]1516pos : tuple <- (int, int)17現在の位置18'''19pos_ = np.asarray(pos) + [(0, 1), (0, -1), (-1, 0), (1, 0)]20dest = [(x, y) for x, y in pos_ if 0 <= x <= 5 and 0 <= y <= 5]21if self.color == 'B':22if self.side == 0:23if pos == (0, 0):24dest += [(0, -1)]25if pos == (5, 0):26dest += [(5, -1)]27elif self.side == 1:28if pos == (0, 5):29dest += [(0, 6)]30if pos == (5, 5):31dest += [(5, 6)]3233return dest
__repr__
メソッドはデバッグのときに役立ちます。
print
したときに出力される文字列を返しますが、これがないとオブジェクトidだけを返すので属性の情報がわかりづらくなります。
convering_squares
メソッドの条件分岐は、良いおばけが相手の陣地の角から脱出することができるようにするものです。
初期盤面を生成する
プレイヤーの配置から初期盤面を生成する関数を作ります。
setup.py1...2from piece import Piece3...45def _init_board(order1, order2):6'''7駒の配置から初期盤面を出力8-> dict <- {(int, int): Piece}910order1, order2 : dict <- {(int, int): str}11駒の初期配置. order1 が先攻12'''13return {**{(5-x, 3-y): Piece(s, 1) for (x, y), s in order2.items()},14**{(x, y+2): Piece(s, 0) for (x, y), s in order1.items()}}15
盤面は左上が(0, 0)
から右下が(5, 5)
までで座標を取っていて、
駒の位置は先手が(1, 4)
から(4, 5)
、後手が(1, 0)
から(4, 1)
までとなります。
マウスで登録したときには左上(1, 2)
、右下(4, 3)
になっているので、座標を変換しています。
先手は平行移動だけですが、後手は180度回転もしています。
これで_init_board(*_order)
とすれば、初期盤面を返すことができます。
これをsetup.main()
の返り値としてmain.py
に渡し、
さらにgame.py
に引数として渡すことで盤面を描画していきます。
setup.py1...2# イベントハンドリング3for event in pygame.event.get():4...5# マウスクリック6if event.type == MOUSEBUTTONDOWN:7# 左8if event.button == 1:9...1011if mouse.on_area(*_mouse_pos, 500, 530, 80, 50):12if satisfied:13decide_snd.play()14if _state == 1: return _init_board(*_order)15_state += 116...
main.py1...2if __name__ == '__main__':3...4orders = setup.main(screen, font, select_snd, decide_snd, forbid_snd)5game.main(screen, orders)
game.py1...2def main(screen, orders):3...
盤面を描画する
さて、盤面を描画する関数を定義していきます。
ガイスターでは良いおばけが盤の角から外に出ると勝ちというルールがあるので、
角に矢印を描いています。
draw.py1...2def _arrow(screen, coord, direction):3'''4矢印を描く56screen : pygame.display.set_mode7coord : tuple <- (int, int)8矢先の座標9direction : str <- 'U', 'D'10'U' - 上, 'D' - 下11'''12assert direction == 'U' or direction == 'D',\13'draw._arrow の引数 direction は "U", "D" の値を取ります'14_coord = np.asarray(coord)15if direction == 'D':16pygame.draw.line(screen, BLACK,17_coord, _coord + (PIECE_SIZE/2, -PIECE_SIZE/2), 2)18pygame.draw.line(screen, BLACK,19_coord, _coord - (PIECE_SIZE/2, PIECE_SIZE/2), 2)20pygame.draw.line(screen, BLACK,21_coord, _coord - (0, PIECE_SIZE), 2)22else:23pygame.draw.line(screen, BLACK,24_coord, _coord + (PIECE_SIZE/2, PIECE_SIZE/2), 2)25pygame.draw.line(screen, BLACK,26_coord, _coord + (-PIECE_SIZE/2, PIECE_SIZE/2), 2)27pygame.draw.line(screen, BLACK,28_coord, _coord + (0, PIECE_SIZE), 2)29...30def board(screen, board, turn):31'''32ゲームボードと盤面上の駒を描く3334screen : pygame.display.set_mode35board : dict <- {(int, int): Piece}36駒の位置とオブジェクト37turn : int <- 0, 1, 2380 - 先攻の駒を開く, 1 - 後攻の駒を開く, 2 - 両方開く,39'''40assert turn == 0 or turn == 1 or turn == 2, 'draw.board の引数 turn は 0, 1, 2 の値を取ります'41# グリッド42_grid(screen, MARGIN, 6, 6)43# 角の矢印44_padding = (SQUARE_SIZE - PIECE_SIZE)/245_arrow(screen, MARGIN+(SQUARE_SIZE/2, _padding), 'U')46_arrow(screen, MARGIN+(11*SQUARE_SIZE/2, _padding), 'U')47_arrow(screen, DISP_SIZE-MARGIN-(SQUARE_SIZE/2, _padding), 'D')48_arrow(screen, DISP_SIZE-MARGIN-(11*SQUARE_SIZE/2, _padding), 'D')49# 駒50for pos, piece in board.items():51if turn == 2:52if piece.side == 0:53_piece(screen, RED if piece.color == 'R' else BLUE, pos)54else:55_piece(screen, RED if piece.color == 'R' else BLUE, pos, True)56elif turn == 0:57if piece.side == 0:58_piece(screen, RED if piece.color == 'R' else BLUE, pos)59else:60_piece(screen, GREY, pos, True)61elif turn == 1:62if piece.side == 0:63_piece(screen, GREY, pos)64else:65_piece(screen, RED if piece.color == 'R' else BLUE, pos, True)
BLACK
やGREY
は色の定数で、config.py
に定義しています。
これをgame.py
で呼び出します。
game.py1...2import draw3...4def main(screen, orders):5# ゲームボード {(int, int): Piece}6_board = orders78while True:9screen.fill(IVORY)10draw.board(screen, _board, 0)11pygame.display.update()12...
これで駒の配置を決定したあとに、初期盤面が表示されます。
は駒の移動とターン交代時の画面を用意します。
読んでくれてありがとうございました。
では