質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2448閲覧

Pythonで実装したリバーシプログラム内で辞書型を使うと例外を吐かずに停止する

退会済みユーザー

退会済みユーザー

総合スコア0

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2018/04/01 09:45

編集2018/04/05 11:46

前提・実現したいこと

Python3.6.4でリバーシのプログラムを作成しています。GUI部分はtkinterで実装し、Threadingとソケット通信を使用して非同期処理でAI同士で対局を行わせるプログラムを作成しております。
今回、新しい機能として「戻る」「進む」ボタンをクリックすることで対局中でもボードの状態を遡れるような機能を実装しようとしています。

ボードの状態は辞書型のrecordという変数で管理し、具体的なコードではサーバーの処理側で、以下のようにrecordに毎ターンボードの状態を追加していこうと考えております。

board.record[board.turn_count] = {'board':copy.deepcopy(board.discs), 'newest_place':board.newest_place}

しかし、毎回17手目でプログラムが停止してしまい例外もキャッチできないため、解決方法が分からず困っております。
以下のソースコードのBoardクラスのself.recordという変数で、BoardクラスのreverseDiscという関数内でrecordに要素を追加していっております。

該当のソースコード

サーバーのコード(クライアントであるAIx2とボードの状態、着手場所などをやり取りしております)

python

1# -*- coding:utf-8 -*- 2"""Script for Tkinter GUI VimReversi.""" 3from threading import Thread 4import tkinter as tk 5import socket 6import select 7import pickle 8import copy 9 10from vr_board import Board 11from vr_gui import GUI 12 13 14server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 15readfds = set([server_sock]) 16host = '127.0.0.1' # server host 17port = 4000 # port number 18bufsize = 4096 # buffer size 19backlog = 2 # max queue number 20 21 22def server_core(board, gui,): 23 try: 24 server_sock.bind((host, port)) 25 server_sock.listen(backlog) 26 27 while True: 28 rready, wready, xready = select.select(readfds, [], []) 29 for sock in rready: 30 if sock is server_sock: 31 conn, address = server_sock.accept() 32 readfds.add(conn) 33 else: 34 """ receive """ 35 msg = sock.recv(bufsize) 36 msg = pickle.loads(msg) 37 player_turn = None 38 placeloc = None 39 cand_move = board.getCanPlace(board.turn) 40 41 print(msg) # print recv message 42 43 # if receive message is valid 44 player_turn = msg['turn'] 45 placeloc = msg['placeloc'] 46 47 # set player name 48 gui.setName(player_turn, msg['software_name']) 49 50 # place disc 51 if(player_turn == board.turn): 52 if(board.reverseDisc(player_turn, placeloc) == True): 53 board.newest_place = placeloc # last placed disc 54 55 board.record[board.turn_count] = {'board':copy.deepcopy(board.discs), 'newest_place':board.newest_place} 56 57 # draw game info on the listbox 58 gui.addList(player_turn, board.turn_count, placeloc) 59 board.turn_count += 1 60 61 board.switch_turn() 62 cand_move = board.getCanPlace(board.turn) 63 board.pass_count = 0 64 65 elif(msg['pass_flg'] == True): # player passes play 66 board.switch_turn() 67 cand_move = board.getCanPlace(board.turn) 68 board.pass_count += 1 69 """""""""""" # end receive 70 71 """ send """ 72 server_info = {} 73 server_info['clicked_index'] = gui.clicked_index 74 server_info['board'] = copy.deepcopy(board) 75 server_info['candidate_move'] = cand_move 76 77 # the game hasn't start yet 78 if(not gui.start_flg): 79 server_info['board'].turn = 'None' 80 server_info['candidate_move'] = [] 81 82 snd_msg = pickle.dumps(server_info) # dump pickle 83 sock.send(snd_msg) 84 """""""""""" # end send 85 86 # finish the game. 87 if(board.turn_count > 60 or board.pass_count >= 2): 88 print('game finished! gg!') 89 for rdd in readfds: 90 rdd.close() 91 return 92 93 # end the game and shutdown the server. 94 if(gui.end_flg): 95 print('server shutdown!') 96 for rdd in readfds: 97 rdd.close() 98 return 99 finally: 100 for sock in readfds: 101 sock.close() 102 103 104def main(): 105 root = tk.Tk() # create root window 106 root.title("VimRev") # window title 107 root.geometry("960x720") # window size 960x720 108 root.resizable(0, 0) # Prohibit change of window size 109 110 board = Board() 111 gui = GUI(root) 112 server_thread = Thread(target=server_core, name='server_thread', args=(board, gui,)) 113 server_thread.start() 114 115 # left click callback 116 root.bind("<Button-1>", gui.click) 117 118 # key press callback 119 root.bind("<Control-s>", gui.key) # start the game. 120 root.bind("<Control-q>", gui.key) # end the game. 121 root.bind("<Control-c>", gui.key) # end the game. 122 123 root.after(100, gui.draw, board) 124 root.mainloop() # Starts GUI execution. 125 126 127if __name__ == '__main__': 128 main()

ボードクラスやAIクラスも実装しておりますが、文字数の関係で記載することができなかったため、下記のGithubのリンクを参照していただければ幸いです。

補足情報(FW/ツールのバージョンなど)

プログラムはPython3.6.4で実装しており、今回の機能を実装していないバージョンをGithubで公開しております。

プログラムを実行するには、以下の方法に従ってください。

  1. サーバーを起動します。

python src/vr_server.py

  1. サーバーが起動している状態でプレーヤーx2をを起動します。

python src/vr_ai.py -n BlackPlayer -m Black <- 先手
python src/vr_ai.py -n WhitePlayer -m White <- 後手

  1. プレーヤーを2つ起動したら、ウィンドウをアクティブにした状態でCtrl-sで対局を開始できます。

また、対局中にCtrl-c or Ctrl-qを押すことで、サーバーを停止し、対局を終了することができます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

半分投げやりな質問になってしまい、大変申し訳ございませんでした。
私の方で色々検証したところ、解決方法が分かりましたので以下に示します。

まず、Boardクラスのインスタンスboard内のrecordという辞書型に以下のように毎ターンボードの状態と、そのターンに着手した場所を追加していっております。

board.record[board.turn_count] = {'board':copy.deepcopy(board.discs), 'newest_place':board.newest_place}
(ここで、board.turn_countは何ターン目かをint型で保持し、board.discsは石の配置をllistで保持、board.newest_placeは最後に石が置かれた場所のインデックスを保持しております。)

その後、クライアントであるAIやプレーヤーにboardをPickle化して送信するのですが、毎ターンboard.recordの容量が増えていっているため、17ターン目でバッファサイズを上回り、正しく通信ができていない状態でした。

そこで、新たにGUIクラスを作成し、そちらにボードの状態を記録するようにしたところ実現したいことを達成することができました。

投稿2018/04/05 11:55

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問