前提・実現したいこと
Python3.6.4でリバーシのプログラムを作成しています。GUI部分はtkinterで実装し、マウスの左クリックで石を打てるようにしています。
後々重い処理を行うAIの作成を考えているため、AIの思考部分(?)を非同期で処理を行えるような設計にしておきたいのですが、
コードの書き方が分からず困っております。
該当のソースコード
Python
1#!/usr/bin/python3 2# -*- coding: utf8 -*- 3import tkinter as tk 4import sys 5import random 6 7 8class PlayerObject(object): 9 10 myTurn = None 11 def __init__(self, myTurn): 12 self.myTurn = myTurn 13 14 def placeDisc(self, board, global_turn, b_index,): 15 16 list_canplace = None 17 18 # 自分のターンの時、着手可能場所を取得 19 if(self.myTurn == global_turn): 20 list_canplace = board.getCanPlace(self.myTurn) 21 22 # 自分のターンじゃないときは何もしない 23 if(self.myTurn != global_turn or b_index is None): 24 return "None" 25 26 # 着手できる場所が無いときはパス 27 if(len(list_canplace) == 0): 28 return "Pass" 29 30 # クリックした場所に着手可能なとき 31 if(board.discs[b_index].type == "CanPlace"): 32 board.resetNewDisc() 33 board.reverseDisc(self.myTurn, b_index) 34 return "Done" 35 36 # クリックした場所に着手できないとき 37 elif(board.discs[b_index].type != "CanPlace"): 38 return "None" 39 40 41class Player(PlayerObject): 42 43 def __init__(self, myTurn): 44 super().__init__(myTurn) 45 46 47class AI(PlayerObject): 48 49 def __init__(self, myTurn): 50 super().__init__(myTurn) 51 52 def placeDisc(self, board, global_turn, b_index,): 53 # 自分のターンじゃないときは何もしない 54 if(self.myTurn != global_turn): 55 return "None" 56 57 list_canplace = board.getCanPlace(self.myTurn) 58 # 着手できる場所が無いときはパス 59 if(len(list_canplace) == 0): 60 return "Pass" 61 62 # 着手可能な場所からランダムに選択して打つ 63 random.shuffle(list_canplace) # 着手可能場所のリストをランダムにシャッフル 64 board.resetNewDisc() 65 board.reverseDisc(self.myTurn, list_canplace[0]) 66 return "Done" 67 68 69class GameManager(object): 70 71 root = None 72 frame = None 73 canvas = None 74 Board = None 75 Player1 = None 76 Player2 = None 77 Turn = None 78 clicked_board_index = None 79 pass_count = None 80 81 def __init__(self, root, Board, Player1, Player2): 82 self.root = root 83 self.frame = tk.Frame(width=960, height=720) 84 self.frame.place(x=0, y=0) 85 self.canvas = tk.Canvas(self.frame, width=720, height=720) 86 self.canvas.place(x=0, y=0) 87 88 self.Board = Board 89 self.Player1 = Player1 90 self.Player2 = Player2 91 92 self.Turn = "Black" 93 self.pass_count = 0 94 95 # 左クリック時にコールバック関数self.clickを呼び出す 96 self.root.bind("<Button-1>", self.click) 97 98 # ボードの着手可能場所を全て"空き"にする 99 def clean_board(self, ): 100 for index, disc in enumerate(self.Board.discs): 101 if(disc.type == "CanPlace"): 102 self.Board.discs[index].type = "Space" 103 104 def click(self, mouse): 105 106 # clicked out of the board 107 if(mouse.x > 720): 108 return 109 110 index = int(mouse.y / 90 + 1) * 10 + int(mouse.x / 90 + 1) 111 self.clicked_board_index = index 112 113 def draw(self, ): 114 115 self.canvas.delete("board") # ボードを消す 116 self.canvas.delete("disc") # ボード上の石を消す 117 118 # ボードを描画 119 self.canvas.create_rectangle(0, 0, 720, 720, fill='#1E824C', tag="board") 120 121 # ボードのマスを描画 122 for i in range(9): 123 self.canvas.create_line( 124 i * 90, 0, i * 90, 720, width=1.2, fill="Black", tag="board") 125 self.canvas.create_line( 126 0, i * 90, 720, i * 90, width=1.2, fill="Black", tag="board") 127 128 # ボードの丸印を描画 129 self.canvas.create_oval(180 - 4, 180 - 4, 180 + 4, 180 + 4, fill="Black", outline="Black", tag="board") 130 self.canvas.create_oval(180 - 4, 540 - 4, 180 + 4, 540 + 4, fill="Black", outline="Black", tag="board") 131 self.canvas.create_oval(540 - 4, 180 - 4, 540 + 4, 180 + 4, fill="Black", outline="Black", tag="board") 132 self.canvas.create_oval(540 - 4, 540 - 4, 540 + 4, 540 + 4, fill="Black", outline="Black", tag="board") 133 134 # 石を描画 135 for index, disc in enumerate(self.Board.discs): 136 center_x = 45 + int((index-1) % 10) * 90 137 center_y = 45 + int((index-10) / 10) * 90 138 if(disc.type == "Black"): 139 self.canvas.create_oval(center_x - 43, center_y - 43, center_x + 43, center_y + 43, fill="Black", outline="Black", tag="disc") 140 elif(disc.type == "White"): 141 self.canvas.create_oval(center_x - 44, center_y - 44, center_x + 44, center_y + 44, fill="White", outline="Black", tag="disc") 142 elif(disc.type == "CanPlace"): 143 self.canvas.create_oval(center_x - 5, center_y - 5, center_x + 5, center_y + 5, fill="OliveDrab1", outline="OliveDrab1", tag="disc") 144 # 最後に打たれた石の場合、マークを付ける 145 if(disc.newest_place): 146 self.canvas.create_oval(center_x - 8, center_y - 8, center_x + 8, center_y + 8, fill="Red", outline="Red", tag="disc") 147 148 self.canvas.pack() 149 150 # コンソール出力テスト 151 ''' 152 print(len(self.Board.discs)) 153 for index, disc in enumerate(self.Board.discs): 154 str = " " if index % 10 != 9 else '\n' 155 if(disc.type == "Ban"): 156 print("@" + str, end="") 157 elif(disc.type == "Space"): 158 print("*" + str, end="") 159 elif(disc.type == "Black"): 160 print("o" + str, end="") 161 elif(disc.type == "White"): 162 print("x" + str, end="") 163 else: 164 print("?" + str, end="") 165 ''' 166 167 def play(self, ): 168 169 p1_status = self.Player1.placeDisc(self.Board, self.Turn, self.clicked_board_index) 170 p2_status = self.Player2.placeDisc(self.Board, self.Turn, self.clicked_board_index) 171 172 if(p1_status == "Pass"): 173 self.pass_count += 1 174 self.Turn = "Black" if (self.Turn == "White") else "White" 175 elif(p1_status == "Done"): 176 self.pass_count = 0 177 self.Turn = "Black" if (self.Turn == "White") else "White" 178 179 if(p2_status == "Pass"): 180 self.pass_count += 1 181 self.Turn = "Black" if (self.Turn == "White") else "White" 182 elif(p2_status == "Done"): 183 self.pass_count = 0 184 self.Turn = "Black" if (self.Turn == "White") else "White" 185 186 self.draw() 187 188 if(self.pass_count < 2): 189 self.root.after(10, self.play) 190 191 192def main(): 193 194 Player1 = None # プレーヤー1 195 Player2 = None # プレーヤー2 196 197 args = sys.argv # コマンドライン引数 198 if(len(args) >= 3): 199 Player1 = Player("Black") if args[1] == '-player' else AI("Black") if args[1] == '-ai' else None 200 Player2 = Player("White") if args[2] == '-player' else AI("White") if args[2] == '-ai' else None 201 elif(len(args) == 2): 202 Player1 = AI("Black") if args[1] == '-training' else None 203 Player2 = AI("White") if args[1] == '-training' else None 204 if(Player1 is None or Player2 is None): 205 args = [] 206 207 if(len(args) < 2): 208 print("error!!") 209 print("If you want to play Player vs Player mode, execute below command") 210 print(" $python main.py -player -player") 211 print("") 212 print("If you want to play Player vs AI mode, execute below command") 213 print(" $python main.py -player -ai") 214 print("") 215 print("If you want to play AI vs AI mode, execute below command") 216 print(" $python main.py -ai -ai") 217 print("") 218 print("If you want to play AI training mode, execute below command") 219 print(" $python main.py -training") 220 print("") 221 sys.exit() 222 223 root = tk.Tk() # rootウィンドウを作成 224 root.title("VimRev") # rootウィンドウのタイトルを変える 225 root.geometry("960x720") # rootウィンドウの大きさを960x720に 226 root.resizable(0, 0) # 縦、横共に画面サイズの変更を禁止 227 228 board = Board() # ボードクラス 229 gameManager = GameManager(root, board, Player1, Player2) # ゲームマネージャークラス 230 gameManager.play() 231 232 root.mainloop() # メインループ 233 234 235if __name__ == '__main__': 236 main() 237
試したこと
キューやパイプを用いてプロセス間でデータのやり取りを行う方法など調べ、見様見真似で実装してみたのですが、スレッドやプロセスなどについて詳しくないため実装が困難でした。
現状ですと、例えばAIクラス中のplaceDisc関数の最初の方で10000回ループを回すと処理が奪われてしまい、その間マウスのクリックやウィンドウ移動などが出来なくなってしまいます。
やりたいこととしてはAI部分は別プロセス(?)で処理させておき、処理が終わったタイミングで結果を反映するといったことです。
AIの処理が行われている間も、マウスのクリックや他のイベント処理は呼び出せるようにしたいです。
もし宜しければ、Pythonでこのような処理を実現するために、どういった内容(プロセスやスレッド、通信?)を勉強すればいいかということも併せて教えていただければ幸いです。
プログラム全体の設計などにつきましても、今より良い方法やアドバイス等がありましたらご教示頂けると有難いです。
以上、宜しくお願い致します。
補足情報(FW/ツールのバージョンなど)
プログラムはPython3.6.4で実装しております。
このソースコードを公開している私のGithubのページ
Player vs Playerを行うには以下のコマンドを実行してください。
$Python source.py -player -player
Player vs AIでは以下のコマンドを実行してください。
(-player, -aiは先に書いた方が先手となります。)
$Python source.py -player -ai
回答2件
あなたの回答
tips
プレビュー