no_one / app.py
nullne's picture
Add application file
eb34cec
import streamlit as st
import numpy as np
import Arena
from MCTS import MCTS
from no_one.NoOneGame import NoOneGame
from no_one.pytorch.NNet import NNetWrapper as NNet
from utils import *
import time
square_content = {
-1: "❌",
+0: "·",
+1: "⭕"
}
players = [{"name": "❌"}, {"name": "⭕"}]
player_types = [":rainbow[AI]", "Human"]
ai_ranks = ["Super", "First Blood", "Random"]
ai_models = {
"Super": "super.pth.tar",
"First Blood": "firstBlood.pth.tar",
"Random": "random.pth.tar",
}
game = NoOneGame(4)
def check_clicked(i, j):
if st.session_state.clicked == (i, j):
return True
return False
class HumanPlayer:
def __init__(self, game):
self.game = game
def encode(self, src, target):
row, col = src[0] - target[0], src[1] - target[1]
if (row, col) == (0, -1):
d = 1
elif (row, col) == (0, 1):
d = 3
elif (row, col) == (-1, 0):
d = 0
elif (row, col) == (1, 0):
d = 2
else:
return None
return self.game.encodeAction(src, d)
def play(self, board, player, src, target):
action = self.encode(src, target)
if action is None:
return None
valids = self.game.getValidMoves(board, player)
if valids[action] != 1:
return None
return action
def ai_player(ai_model):
n1 = NNet(game)
n1.load_checkpoint('./models/', ai_models[ai_model])
args1 = dotdict({'numMCTSSims': 50, 'cpuct':1.0})
msts1 = MCTS(game, n1, args1)
return lambda x: np.argmax(msts1.getActionProb(x, temp=0))
def human_step(i, j):
if st.session_state.clicked is None:
if st.session_state.board[i, j] != st.session_state.player:
st.toast('Invalid move!')
return
st.session_state.clicked = (i, j)
return
elif st.session_state.clicked == (i, j):
# selection canceled
st.session_state.clicked = None
return
elif st.session_state.board[i, j] == st.session_state.player:
st.session_state.clicked = (i, j)
return
else:
# move
player = st.session_state.players[st.session_state.player + 1]
action = player.play(st.session_state.board, st.session_state.player, st.session_state.clicked, (i, j))
if action is None:
st.toast('Invalid move!')
return
move(action)
st.session_state.clicked = None
def ai_step():
p = st.session_state.player
action = st.session_state.players[p+1](game.getCanonicalForm(st.session_state.board, st.session_state.player))
# st.session_state.board, st.session_state.player = game.getNextState(
# st.session_state.board, p, action,
# )
move(action)
def move(action):
p = st.session_state.player
st.session_state.board, st.session_state.player = game.getNextState(
st.session_state.board, p, action,
)
res = game.getGameEnded(st.session_state.board, p)
if res != 0:
st.session_state.winner = res*p
st.session_state.win[res*p] += 1
st.balloons()
def reinit():
st.session_state.reinit = True
def init(post_init=False):
st.session_state.reinit = False
if not post_init:
st.session_state.win = {-1: 0, 1: 0}
st.session_state.board = game.getInitBoard()
st.session_state.player = -1
st.session_state.players = [None for i in range(2)]
for i, setting in enumerate(st.session_state.player_settings):
if setting["pt"] == player_types[1]:
st.session_state.players[i] = HumanPlayer(game)
else:
st.session_state.players[i] = ai_player(setting["ai_model"])
st.session_state.players.insert(1, None)
st.session_state.winner = None
st.session_state.clicked = None
def player_to_index(p):
if p == -1:
return 0
elif p == 1:
return 1
def main():
if "player_settings" not in st.session_state:
st.session_state.player_settings = [
{"pt": player_types[1], "ai_model": ai_ranks[0]},
{"pt": player_types[0], "ai_model": ai_ranks[1]},
]
if "reinit" not in st.session_state:
st.session_state.reinit = False
fire, settings = st.columns([1, 2])
fire.button('New Game', on_click=init, args=(True,))
with settings.expander(
'Settings',
expanded=False,
):
st.warning('Any setting changing will restart the game immediately', icon="⚠️")
for i, p in enumerate(st.columns([0.5, 0.5])):
with p:
# players[i]
st.session_state.player_settings[i]["pt"] = st.radio(
f"Who will play %s" % players[i]["name"],
player_types,
key=f"xp_type_{i}",
horizontal=True,
index=player_types.index(st.session_state.player_settings[i]["pt"]),
on_change=reinit,
)
if st.session_state.player_settings[i]["pt"] == player_types[0]:
st.session_state.player_settings[i]["ai_model"] = st.radio(
"AI rank",
ai_ranks,
key=f"xp_rank_{i}",
index=ai_ranks.index(st.session_state.player_settings[i]["ai_model"]),
on_change=reinit,
)
st.divider()
st.checkbox("Show me how to play", key="show_how_to_play")
if "board" not in st.session_state or st.session_state.reinit:
init()
if st.session_state.show_how_to_play:
with st.sidebar:
st.title("How to play")
how_to = '''1. 游戏的目标是吃掉对方的棋子
开局双方各有四枚棋子,被吃剩一枚棋子即可判负
2. 棋子可以向上下左右移动到空白位置
3. 如何吃子
1. 移动后的棋子需要跟本方其他某个棋子,在水平方向,或者垂直方向上连住
2. 如果连住后的棋子两端有对方一个棋子,那么这个对方的棋子就被吃掉
3. 如果对方也有两颗棋子,则互相不吃
4. 如果落子在对方连起来的两枚棋子一端,也不会被吃
5. 如果选择 AI vs AI,需要手动点击比分下面的回合按钮,触发下一次落子
'''
st.markdown(how_to)
xp, score, op = st.columns([2, 8, 2])
for i, p in enumerate([xp, op]):
p.title(players[i]["name"], anchor=False)
caption = st.session_state.player_settings[i]["pt"]
if st.session_state.player_settings[i]["pt"] == player_types[0]:
caption = st.session_state.player_settings[i]["ai_model"] + " " + caption
p.caption(caption)
# st.session_state
if st.session_state.player_settings[player_to_index(st.session_state.player)]['pt'] != "Human" and st.session_state.winner is None:
ai_step()
st.divider()
for i, row in enumerate(st.session_state.board):
cols = st.columns([5, 1, 1, 1, 1, 5])
for j, field in enumerate(row):
cols[j + 1].button(
square_content[field],
key=f"{i}-{j}",
type=f'{"primary" if check_clicked(i, j) else "secondary"}',
on_click=human_step,
args=(i, j),
)
s = score.columns([2, 2, 2])
s[1].title(f'{st.session_state.win[-1]} : {st.session_state.win[1]}', anchor=False)
s[1].button(
f'{"❌" if st.session_state.player == -1 else "⭕"}\'s turn'
if not st.session_state.winner
else f'🏁 Game finished'
)
if __name__ == '__main__':
main()