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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

1777閲覧

3目並べ:ミニマックス法での評価

step_by_step

総合スコア8

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2020/05/02 03:59

編集2020/05/02 04:08

コードの写し書きを、「python ではじめるアルゴリズム入門」(翔泳社)からしているのですが、

打ち手の評価を行う minmax 関数内の下記の check 関数を用いて3目並んだ時点での
終了条件の記述の挙動がいまひとつわからず... minmax関数での、勝ちを 1, 負けを -1,
引き分けを 0 打ち手の評価値としていることは理解できるのですが、そのうえで、評価値を
どこにどのように return されるのでしょうか?

python

1def minmax(p1, p2, turn): 2 if check(p2): #3目並びをは判定する check 関数を呼び出し 3 if turn: 4 return 1 #勝ち 5 else: 6 return -1 #負け 7 board = p1 | p2 8 if board == 0b111111111: 9 return 0 #引き分け 10 11 12#以下省略

minmax関数の呼び出し元の play 関数内で参照すると、 r の内包表記の一部として return されている
のですが、ここの挙動が具体的によくわからず、何か手がかりをご教示いただければ幸いです。

python

1 2def play(p1, p2, turn): 3#先行部分省略 4 w = [i for i in range(9) if (board & (1 << i)) == 0] #打てる領域(="0") を9bitのビット列から順次探索 5 r = [minmax(p2, p1 | (1 << i ), True) for i in w] # 領域の評価 6 j = w[r.index(max(r))] #評価の一番高いものを確定し、 7 play(p2, p1 | (1 << j ), not turn) #打ち手とする 8#以下省略 9

python

1import random 2import math 3 4goal = [ 5 0b111000000, 0b000111000, 0b000000111, 0b100100100, 6 0b010010010, 0b001001001, 0b100010001, 0b001010100 7] 8 9#3つ並んだか判定 10 11def check(player): 12 for mask in goal: 13 if player & mask == mask: 14 return True 15 return False 16 17def minmax(p1, p2, turn): 18 if check(p2): 19 if turn: 20 return 1 21 else: 22 return -1 23 24 board = p1 | p2 25 if board == 0b111111111: 26 return 0 27 28 w = [i for i in range(9) if (board & (1<< i )) == 0] 29 30 if turn: 31 return min([minmax(p2, p1 | (1 << i ), not turn) for i in w]) 32 else: 33 return max([minmax(p2, p1 | (1 << i ), not turn) for i in w]) 34 35 36def play(p1, p2, turn): 37 38 if check(p2): 39 print("settled", [bin(p1), bin(p2)]) 40 return 41 42 board = p1 | p2 43 print("current_board: ", bin(board)) 44 45 if board == 0b111111111: 46 print([bin(p1), bin(p2), "draw"]) 47 return 48 w = [i for i in range(9) if (board & (1 << i)) == 0] 49 r = [minmax(p2, p1 | (1 << i ), True) for i in w] 50 j = w[r.index(max(r))] 51 play(p2, p1 | (1 << j ), not turn) 52 53play(0, 0, True) 54

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

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

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

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

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

guest

回答1

0

ベストアンサー

minmax関数がよくわからないという質問で良いですか…?

#以下省略 としてしまっていますが、多分ここが一番重要では無いかと

python

1 if turn: 2 return min([minmax(p2, p1 | (1 << i ), not turn) for i in w]) 3 else: 4 return max([minmax(p2, p1 | (1 << i ), not turn) for i in w])
  • w はプレイヤーが 置ける位置
  • xxx for i in wi に プレイヤーが置ける位置を代入しながらループを回す
  • p1 | (1 << i ) p1 が i に石を置く
  • minmax(p2, placed_p1, not turn) 次は p2 が置くターンですが、実装を簡略化するため、p2と p1 を交換して、p1にプレイさせます。
  • turn がコードを難読化させているような気がします。今、プレイヤーを交換したので、評価が逆転します。評価が逆転したことを伝えるために、turnを使っています

こう書けばturnを無くせそうです(デバッグしていないので間違っているかもしれませんが…)
別のアルゴリズムに置き換わっている可能性があるので、撤回します

python

1def minmax(p1, p2): 2 if check(p2): 3 return -1 4 5 board = p1 | p2 6 if board == 0b111111111: 7 return 0 8 9 w = [i for i in range(9) if (board & (1<< i )) == 0] 10 11 return max([-minmax(p2, p1 | (1 << i )) for i in w])

投稿2020/05/02 04:48

編集2020/05/02 16:59
maai

総合スコア463

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

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

step_by_step

2020/05/02 15:17 編集

質問内容が漠然としている中、示唆いただきありがとうございます。 引き続き理解が曖昧なところがあり、ご容赦いただきたいのですが、 挙げていただいた箇所で `if turn:` では   `min()` で返し、 `else: ` では `max()` で返すのは、何に対応しているのでしょうか。 min と max で交互に行っていくのは理解できているのですが、 順番としての必然性はありますでしょうか? (` if turn: ` を`max() ` で返し、`else:` を `min()`で返す形で 始めても基本的には同じことでしょうか?) turn をなくす記述まで理解が追いついておらず、恐縮です。
step_by_step

2020/05/02 15:30

連投すみません、上記の質問があまりにも漠然としていたため、 聞き方を変えさせていただきます。 turn がそもそもこのコードで果たしている役割の理解が曖昧だと思うのですが、 こちらは turn = True のときは先攻のターン turn = False のときは後攻のターン というのを管理しているフラグという理解で合っていますでしょうか?
maai

2020/05/02 16:57

(ソースコードが正しいという前提で書きますが…) 「play(0, 0, True)」で始まって、True、Falseと交互に変わっているため、 play関数は、「turn = True のときは先攻のターン」という考え方で正しそうです。 minmax関数は、後攻でも「minmax(p2, p1 | (1 << i ), True)」と turn = True で呼び出しているため、 turnが先攻か後攻かを表している訳では無いことが分かります。 「if check(p2): if turn: return 1」から、 p2 が揃ったら turn = True の時は嬉しいようです。 「turn = True のときは p2 が評価したい打ち手の盤面」が近いのかな…と思っています。 - - - 自分も混乱気味なので、整理してみました。turn の真偽を関数名に移動させました。 minmax_false は評価したくない相手が揃ってしまったので-1を返している、とも取れるように見えます ``` def minmax_true(p1, p2): if check(p2): return 1 board = p1 | p2 if board == 0b111111111: return 0 w = [i for i in range(9) if (board & (1<< i )) == 0] return min([minmax_false(p2, p1 | (1 << i )) for i in w]) def minmax_false(p1, p2): if check(p2): return -1 board = p1 | p2 if board == 0b111111111: return 0 w = [i for i in range(9) if (board & (1<< i )) == 0] return max([minmax_true(p2, p1 | (1 << i )) for i in w]) ```
step_by_step

2020/05/05 03:21

咀嚼するのに時間がかかってしまいましたが、 play 関数と、minmax関数で turn のコンテクストが違うことが理解できました。 関数の引数名は原著ママなのですが、こういった構成でしたら、それぞれの関数で 引数の名前は変えたほうが良いのかもという気付きも得られました。 ご示唆のほど、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問