# many code from http://d.hatena.ne.jp/yatt/20100129/1264791420 from logging import getLogger import wx from wx.core import CommandEvent from reversi_zero.config import Config, GuiConfig, PlayWithHumanConfig from reversi_zero.env.reversi_env import Player from reversi_zero.play_game.game_model import PlayWithHuman, GameEvent logger = getLogger(__name__) def start(config: Config): config.play_with_human.update_play_config(config.play) reversi_model = PlayWithHuman(config) app = wx.App() Frame(reversi_model, config.gui).Show() app.MainLoop() def notify(caption, message): dialog = wx.MessageDialog(None, message=message, caption=caption, style=wx.OK) dialog.ShowModal() dialog.Destroy() class Frame(wx.Frame): def __init__(self, model: PlayWithHuman, gui_config: GuiConfig): self.model = model self.gui_config = gui_config self.is_flip_vertical = False self.show_player_evaluation = True wx.Frame.__init__(self, None, -1, self.gui_config.window_title, size=self.gui_config.window_size) # panel self.panel = wx.Panel(self) self.panel.Bind(wx.EVT_LEFT_DOWN, self.try_move) self.panel.Bind(wx.EVT_PAINT, self.refresh) self.new_game(human_is_black=True) # menu bar menu = wx.Menu() menu.Append(1, u"New Game(Black)") menu.Append(2, u"New Game(White)") menu.AppendSeparator() menu.Append(5, u"Flip Vertical") menu.Append(6, u"Show/Hide Player evaluation") menu.AppendSeparator() menu.Append(9, u"quit") menu_bar = wx.MenuBar() menu_bar.Append(menu, u"menu") self.SetMenuBar(menu_bar) self.Bind(wx.EVT_MENU, self.handle_new_game, id=1) self.Bind(wx.EVT_MENU, self.handle_new_game, id=2) self.Bind(wx.EVT_MENU, self.handle_flip_vertical, id=5) self.Bind(wx.EVT_MENU, self.handle_show_hide_player_evaluation, id=6) self.Bind(wx.EVT_MENU, self.handle_quit, id=9) # status bar self.CreateStatusBar() self.model.add_observer(self.handle_game_event) def handle_game_event(self, event): if event == GameEvent.update: self.panel.Refresh() self.update_status_bar() wx.Yield() elif event == GameEvent.over: self.game_over() elif event == GameEvent.ai_move: self.ai_move() def handle_quit(self, event: CommandEvent): self.Close() def handle_new_game(self, event: CommandEvent): self.new_game(human_is_black=event.GetId() == 1) def handle_flip_vertical(self, event): self.is_flip_vertical = not self.is_flip_vertical self.panel.Refresh() def handle_show_hide_player_evaluation(self, event): self.show_player_evaluation = not self.show_player_evaluation self.panel.Refresh() def new_game(self, human_is_black): self.model.start_game(human_is_black=human_is_black) self.model.play_next_turn() def ai_move(self): self.panel.Refresh() self.update_status_bar() wx.Yield() self.model.move_by_ai() self.model.play_next_turn() def try_move(self, event): if self.model.over: return # calculate coordinate from window coordinate event_x, event_y = event.GetX(), event.GetY() w, h = self.panel.GetSize() x = int(event_x / (w / 8)) y = int(event_y / (h / 8)) if self.is_flip_vertical: y = 7-y if not self.model.available(x, y): return self.model.move(x, y) self.model.play_next_turn() def game_over(self): # if game is over then display dialog black, white = self.model.number_of_black_and_white mes = "black: %d\nwhite: %d\n" % (black, white) if black == white: mes += "** draw **" else: mes += "winner: %s" % ["black", "white"][black < white] notify("game is over", mes) # elif self.reversi.passed != None: # notify("passing turn", "pass") def update_status_bar(self): msg = "current player is " + ["White", "Black"][self.model.next_player == Player.black] if self.model.last_evaluation: msg += f"|AI Confidence={self.model.last_evaluation:.4f}" self.SetStatusText(msg) def refresh(self, event): dc = wx.PaintDC(self.panel) self.update_status_bar() w, h = self.panel.GetSize() # background dc.SetBrush(wx.Brush("#228b22")) dc.DrawRectangle(0, 0, w, h) # grid dc.SetBrush(wx.Brush("black")) px, py = w / 8, h / 8 for y in range(8): dc.DrawLine(y * px, 0, y * px, h) dc.DrawLine(0, y * py, w, y * py) dc.DrawLine(w - 1, 0, w - 1, h - 1) dc.DrawLine(0, h - 1, w - 1, h - 1) # stones brushes = {Player.white: wx.Brush("white"), Player.black: wx.Brush("black")} for y in range(8): vy = 7-y if self.is_flip_vertical else y for x in range(8): c = self.model.stone(x, y) if c is not None: dc.SetBrush(brushes[c]) dc.DrawEllipse(x * px, vy * py, px, py) if self.model.last_history: q_value = self.model.last_history.values[y*8+x] n_value = self.model.last_history.visit[y*8+x] enemy_q_value = - self.model.last_history.enemy_values[y*8+x] enemy_n_value = self.model.last_history.enemy_visit[y*8+x] dc.SetTextForeground(wx.Colour("blue")) if n_value: dc.DrawText(f"{int(n_value):d}", x*px+2, vy*py+2) if q_value: if q_value < 0: dc.SetTextForeground(wx.Colour("red")) dc.DrawText(f"{int(q_value*100):d}", x*px+2, (vy+1)*py-16) if self.show_player_evaluation: dc.SetTextForeground(wx.Colour("purple")) if enemy_n_value: dc.DrawText(f"{int(enemy_n_value):2d}", (x+1)*px-20, vy*py+2) if enemy_q_value: if enemy_q_value < 0: dc.SetTextForeground(wx.Colour("orange")) dc.DrawText(f"{int(enemy_q_value*100):2d}", (x+1)*px-24, (vy+1)*py-16)