はじめに
- Python を少し囓った素人が、「学習型じゃんけん」を作ってみました。 (⇒ SQL版)
- 学習型といっても、強化学習とかではありません。単純な統計です。
- 「こう書いた方がスマートだ」など、突っ込み大歓迎です。
環境
- Jupyter Notebook 6.1.4
- Python 3.8.5 (default, Sep 3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
アルゴリズム
- 人間は、単純な選択を短時間に繰り返したときに、無意識に癖が出ます。
- 直近4回の手の並びの出現頻度を記録して、直近3回の並びから最も頻度の高い次の手を予測します。
- 初期化時に出現履歴と頻度表を適当な乱数で埋めてあります。
使い方
- 人間が手を入力する前に、プログラムは手の内を見せます。
- 見たくない場合は、
firstOut
をFalse
にしてください。
- 見たくない場合は、
- 人間側の手を
1
~3
の数字で入力します。- 機械側の手を見ずに入力するとフェアです。
-
0
を入力するとゲームを終了します。
- 人間が手を選ぶと、勝敗を判定し、結果と戦績を表示します。
コード
janken.py
import random as rnd
# 初期化
log = [rnd.randrange(3) for _ in range(4)] # 履歴
frequency = [[[[rnd.randrange(3) for _ in range(3)] for _ in range(3)] for _ in range(3)] for _ in range(3)] # 頻度
handName = { 0:'Goo', 1:'Choki', 2:'Par' } # 手の名前
prompt = "[1:Goo, 2:Choki, 3:Par, 0:End]? " # プロンプト
human = 0 # 人間の勝利数
computer = 0 # 機械の勝利数
draw = 0 # 引き分け数
firstOut = True # 先出し
# 手の表示
def printHand(hand, isHuman = False, secret = False):
print(f"{'Human' if isHuman else 'Computer'} hand: {handName[hand]}{(' (secret)' if secret else '')}")
# 結果と戦績の表示
def printResult(comHand, humHand):
global human, computer, draw
if comHand == humHand:
result = 'draw'
draw += 1
elif comHand == humHand - 1 or comHand == humHand + 2:
result = 'Human lose!'
computer += 1
else:
result = 'Human win!!'
human += 1
print(f"{result} (computer:{computer} human:{human} draw:{draw})")
# 次の手の取得
def getNextHand(isHuman = False):
if isHuman:
while True:
try:
hand = int(input(prompt))
except:
pass
else:
if hand >= 0 and hand <= 3:
return hand - 1
print('input error')
else:
f = frequency[log[1]][log[2]][log[3]] # 人間の次の手の出現頻度
return (f.index(max(f)) + 2) % 3 # 最頻手に勝てる手
# 履歴と頻度の更新
def updateLog(hand):
global log, frequency
log.append(hand)
log = log[-4:]
frequency[log[0]][log[1]][log[2]][log[3]] += 1
# メインループ
while True:
print(f"--- {human + computer + draw + 1} ---") # ゲーム数表示
com = getNextHand() # 機械の次の手の算出
if firstOut: printHand(com, secret = True) # COMの手表示 先に見せてしまう
hand = getNextHand(isHuman = True) # 人間の次の手の取得
if hand < 0: break # 終了
printHand(hand, isHuman = True) # HUMの手表示
if not firstOut: printHand(com) # COMの手表示 後で見せる
printResult(com, hand) # 結果表示
updateLog(hand) # 記録
result
--- 1 ---
Computer hand: Par (secret)
[1:Goo, 2:Choki, 3:Par, 0:End]? 1
Human hand: Goo
Human lose! (computer:1 human:0 draw:0)
--- 2 ---
Computer hand: Par (secret)
[1:Goo, 2:Choki, 3:Par, 0:End]? 2
Human hand: Choki
Human win!! (computer:1 human:1 draw:0)
--- 3 ---
Computer hand: Par (secret)
[1:Goo, 2:Choki, 3:Par, 0:End]? 3
Human hand: Par
draw (computer:1 human:1 draw:1)
--- 4 ---
Computer hand: Choki (secret)
[1:Goo, 2:Choki, 3:Par, 0:End]? 0