Example #1
0
    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = EnemyCardSizer(wx.HORIZONTAL,
                                                parent=self._panel)
        self._bottom_player_sizer = CardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
            on_click=self._on_bottom_player_card_click)

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = ControlSizer(wx.HORIZONTAL, parent=self._panel)
        self._control_sizer.set_on_discard_button_click(
            self._on_discard_button_click)
        self._control_sizer.set_on_enough_button_click(
            self._on_enough_button_click)
        self._control_sizer.set_on_take_button_click(
            self._on_take_button_click)

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(self._top_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)
        self._main_sizer.Add(self._table_sizer,
                             flag=wx.ALIGN_CENTER_VERTICAL,
                             proportion=1)
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(self._bottom_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Layout()
        self.Refresh()
Example #2
0
    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = EnemyCardSizer(
            wx.HORIZONTAL, parent=self._panel
        )
        self._bottom_player_sizer = CardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
            on_click=self._on_bottom_player_card_click
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = ControlSizer(wx.HORIZONTAL, parent=self._panel)
        self._control_sizer.set_on_discard_button_click(
            self._on_discard_button_click
        )
        self._control_sizer.set_on_enough_button_click(
            self._on_enough_button_click
        )
        self._control_sizer.set_on_take_button_click(
            self._on_take_button_click
        )

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(
            self._top_player_sizer, flag=wx.EXPAND, proportion=1
        )
        self._main_sizer.Add(
            self._table_sizer, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1
        )
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(
            self._bottom_player_sizer, flag=wx.EXPAND, proportion=1
        )

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Layout()
        self.Refresh()
Example #3
0
    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )
        self._bottom_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._to_game_start_button = wx.Button(parent=self._panel, label=u'<<')
        self._to_game_start_button.Bind(wx.EVT_BUTTON,
                                        self._menu_on_to_game_start)
        self._prev_button = wx.Button(parent=self._panel, label=u'<')
        self._prev_button.Bind(wx.EVT_BUTTON, self._menu_on_prev)
        self._next_button = wx.Button(parent=self._panel, label=u'>')
        self._next_button.Bind(wx.EVT_BUTTON, self._menu_on_next)
        self._to_game_end_button = wx.Button(parent=self._panel, label=u'>>')
        self._to_game_end_button.Bind(wx.EVT_BUTTON, self._menu_on_to_game_end)
        self._control_sizer.AddMany([
            self._to_game_start_button, self._prev_button, self._next_button,
            self._to_game_end_button
        ])

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(self._top_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)
        self._main_sizer.Add(self._table_sizer,
                             flag=wx.ALIGN_CENTER_VERTICAL,
                             proportion=1)
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(self._bottom_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Refresh()
        self.Layout()

        self.PLAYER_SIZER_MAP = {
            LogViewer.PLAYER1: self._bottom_player_sizer,
            LogViewer.PLAYER2: self._top_player_sizer,
        }

        self._menu_on_open()
Example #4
0
class ViewLogFrame(wx.Frame):
    WIDTH = 800
    HEIGHT = 500

    LAST_DIR_SETTING = 'last_log_dir'

    def __init__(self):
        super(ViewLogFrame, self).__init__(parent=None,
                                           title='Durak log viewer',
                                           size=(self.WIDTH, self.HEIGHT))

        self._log_viever = None

        self._create_layout()

    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )
        self._bottom_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._to_game_start_button = wx.Button(parent=self._panel, label=u'<<')
        self._to_game_start_button.Bind(wx.EVT_BUTTON,
                                        self._menu_on_to_game_start)
        self._prev_button = wx.Button(parent=self._panel, label=u'<')
        self._prev_button.Bind(wx.EVT_BUTTON, self._menu_on_prev)
        self._next_button = wx.Button(parent=self._panel, label=u'>')
        self._next_button.Bind(wx.EVT_BUTTON, self._menu_on_next)
        self._to_game_end_button = wx.Button(parent=self._panel, label=u'>>')
        self._to_game_end_button.Bind(wx.EVT_BUTTON, self._menu_on_to_game_end)
        self._control_sizer.AddMany([
            self._to_game_start_button, self._prev_button, self._next_button,
            self._to_game_end_button
        ])

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(self._top_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)
        self._main_sizer.Add(self._table_sizer,
                             flag=wx.ALIGN_CENTER_VERTICAL,
                             proportion=1)
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(self._bottom_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Refresh()
        self.Layout()

        self.PLAYER_SIZER_MAP = {
            LogViewer.PLAYER1: self._bottom_player_sizer,
            LogViewer.PLAYER2: self._top_player_sizer,
        }

        self._menu_on_open()

    def _create_menu(self):
        self.menu = wx.MenuBar()
        menu = wx.Menu()  # Файл
        item = menu.Append(wx.ID_OPEN, u'Открыть файл лога...',
                           u'Открыть файл лога')
        self.Bind(wx.EVT_MENU, self._menu_on_open, item)
        item = menu.Append(wx.ID_ANY, u'Список игр...\tCTRL+L',
                           u'Просмотреть список игр в открытом файле')
        self.Bind(wx.EVT_MENU, self._menu_on_select_game, item)
        item = menu.Append(wx.ID_EXIT, u'Выход', u'Закрыть программу')
        self.Bind(wx.EVT_MENU, self._on_close, item)
        self.menu.Append(menu, u'Файл')

        menu = wx.Menu()  # Текущая игра
        self._to_game_start_menu_item = menu.Append(wx.ID_ANY,
                                                    u'В начало <<\tHOME',
                                                    u'Перейти в начало игры')
        self.Bind(wx.EVT_MENU, self._menu_on_to_game_start,
                  self._to_game_start_menu_item)
        self._prev_menu_item = menu.Append(wx.ID_ANY, u'Назад <\tCTRL+LEFT',
                                           u'Вернуться на ход назад')
        self.Bind(wx.EVT_MENU, self._menu_on_prev, self._prev_menu_item)
        self._next_menu_item = menu.Append(wx.ID_ANY, u'Вперед >\tCTRL+RIGHT',
                                           u'Перейти на ход вперед')
        self.Bind(wx.EVT_MENU, self._menu_on_next, self._next_menu_item)
        self._to_game_end_menu_item = menu.Append(wx.ID_ANY,
                                                  u'В конец >>\tEND',
                                                  u'Перейти в конец игры')
        self.Bind(wx.EVT_MENU, self._menu_on_to_game_end,
                  self._to_game_end_menu_item)
        self.menu.Append(menu, u'Текущая игра')

        self.SetMenuBar(self.menu)

    def _on_close(self, event):
        if event.GetEventType() in wx.EVT_CLOSE.evtType:
            event.Skip()
        else:
            self.Close()

    def _menu_on_open(self, event=None):
        last_directory = get_setting(self.LAST_DIR_SETTING,
                                     os.path.expanduser('~'))
        dialog = wx.FileDialog(self, u'Выберите файл лога', last_directory, '',
                               '*', wx.OPEN)
        if dialog.ShowModal() != wx.ID_OK:
            self.Close()

        filename = dialog.GetPath()
        dialog.Destroy()

        self._log_viever = LogViewer(filename)
        set_setting(self.LAST_DIR_SETTING, os.path.dirname(filename))

        self._menu_on_select_game()

    def _menu_on_select_game(self, event=None):
        choices = [
            '%d. %s' % (i, game['title'])
            for i, game in enumerate(self._log_viever.iterindex(), start=1)
        ]
        dialog = wx.SingleChoiceDialog(
            self,
            u'Выберите игру',
            u'Список игр',
            choices,
            wx.CHOICEDLG_STYLE,
        )
        dialog.SetSize((500, 400))
        dialog.Refresh()
        if dialog.ShowModal() == wx.ID_OK:
            self._log_viever.load_game(dialog.GetSelection())
            self._bottom_player_sizer.set_label(self._log_viever.player1_name)
            self._top_player_sizer.set_label(self._log_viever.player2_name)
            self._menu_on_to_game_start()

        dialog.Destroy()

    def _menu_on_to_game_start(self, event=None):
        self._log_viever.to_game_start()

        if not self._log_viever.has_next:
            return

        self._deck.set_opened_trump(self._log_viever.opened_trump)

        for card_sizer in (self._bottom_player_sizer, self._top_player_sizer):
            card_sizer.trump = self._log_viever.opened_trump
            card_sizer.remove_all()

        log_event = self._log_viever.get_next()
        self._new_move(log_event)

        self._panel.Layout()
        self.Layout()
        self.Refresh()

        self._set_enabled_controls()

    def _menu_on_to_game_end(self, event=None):
        self._log_viever.to_game_end()

        if not self._log_viever.has_prev:
            return

        log_event = self._log_viever.get_prev()
        self._new_move(log_event, with_table=True)

        self._set_enabled_controls()

    def _menu_on_next(self, event):
        if not self._log_viever.has_next:
            return

        log_event = self._log_viever.get_next()
        if log_event['event_type'] == self._log_viever.NEW_MOVE:
            self._new_move(log_event)
            return

        card_sizer = self.PLAYER_SIZER_MAP[log_event['to_move']]
        table_method = getattr(self._table, log_event['event_type'])

        card = log_event['card']
        table_method(card)
        card_sizer.remove_card(card, do_layout=True)

        self._set_enabled_controls()

    def _menu_on_prev(self, event):
        if not self._log_viever.has_prev:
            return

        log_event = self._log_viever.get_prev()
        if log_event['event_type'] == self._log_viever.NEW_MOVE:
            self._new_move(log_event, with_table=True)
            return

        card_sizer = self.PLAYER_SIZER_MAP[log_event['to_move']]

        card = log_event['card']
        self._table.pop()
        card_sizer.add_card(card, do_layout=True)

        self._set_enabled_controls()

    def _new_move(self, log_event, with_table=False):
        assert log_event['event_type'] == self._log_viever.NEW_MOVE

        self._table.remove_all()
        self._deck.set_card_count(log_event['deck_count'])

        if with_table:
            for index, card in enumerate(log_event['moves_and_responds']):
                if index % 2:
                    self._table.respond(card)
                else:
                    self._table.move(card)

            for card in log_event['given_more']:
                self._table.give_more(card)

        card_sizers = (self._bottom_player_sizer, self._top_player_sizer)
        card_sets = (log_event['player1_cards'], log_event['player2_cards'])

        for card_sizer, card_set in zip(card_sizers, card_sets):
            if with_table:
                card_sizer.remove_all()
                cards_to_add = card_set - set(log_event['moves_and_responds'] +
                                              log_event['given_more'])
            else:
                cards_to_add = card_set - card_sizer.cards

            for card in cards_to_add:
                card_sizer.add_card(card)
            card_sizer.Layout()

        if log_event['to_move'] == self._log_viever.PLAYER1:
            self.SetStatusText(u'Атакует игрок снизу')
        else:
            self.SetStatusText(u'Атакует игрок сверху')

        self._set_enabled_controls()

    def _set_enabled_controls(self):
        all_controls = {
            self._to_game_start_button,
            self._to_game_end_button,
            self._next_button,
            self._prev_button,
            self._to_game_end_menu_item,
            self._to_game_start_menu_item,
            self._prev_menu_item,
            self._next_menu_item,
        }
        [control.Enable(False) for control in all_controls]

        if not self._log_viever or not self._log_viever.has_game_loaded:
            return

        if self._log_viever.has_prev:
            self._to_game_start_button.Enable()
            self._prev_button.Enable()
            self._to_game_start_menu_item.Enable()
            self._prev_menu_item.Enable()

        if self._log_viever.has_next:
            self._to_game_end_button.Enable()
            self._next_button.Enable()
            self._to_game_end_menu_item.Enable()
            self._next_menu_item.Enable()
Example #5
0
class PlayFrame(wx.Frame):
    WIDTH = 800
    HEIGHT = 450

    HUMAN = GameController.PLAYER1
    ENGINE = GameController.PLAYER2

    def __init__(self):
        super(PlayFrame, self).__init__(parent=None,
                                        title='Durak GUI',
                                        size=(self.WIDTH, self.HEIGHT))

        self._create_layout()

        self._controller = GameController(
            player1_name='HUMAN',
            player2_name='ENGINE',
            log_filename=get_filename('last_game'),
            overwrite_log=True)
        self._engine = None
        self._engine_path = None

        self._trump = None

        self._start_new_game()

    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = EnemyCardSizer(wx.HORIZONTAL,
                                                parent=self._panel)
        self._bottom_player_sizer = CardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
            on_click=self._on_bottom_player_card_click)

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = ControlSizer(wx.HORIZONTAL, parent=self._panel)
        self._control_sizer.set_on_discard_button_click(
            self._on_discard_button_click)
        self._control_sizer.set_on_enough_button_click(
            self._on_enough_button_click)
        self._control_sizer.set_on_take_button_click(
            self._on_take_button_click)

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(self._top_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)
        self._main_sizer.Add(self._table_sizer,
                             flag=wx.ALIGN_CENTER_VERTICAL,
                             proportion=1)
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(self._bottom_player_sizer,
                             flag=wx.EXPAND,
                             proportion=1)

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Layout()
        self.Refresh()

    def _create_menu(self):
        self.menu = wx.MenuBar()
        menu = wx.Menu()  # Игра
        item = menu.Append(wx.ID_NEW, u'Новая игра', u'Начать новую игру')
        self.Bind(wx.EVT_MENU, self._start_new_game, item)
        item = menu.Append(wx.ID_EXIT, u'Выход', u'Выйти из игры')
        self.Bind(wx.EVT_MENU, self._on_close, item)
        self.menu.Append(menu, u'Игра')

        menu = wx.Menu()  # Настройки
        self._by_winner_menu_item = menu.AppendCheckItem(
            wx.ID_ANY, u'Ход под дурака',
            u'В новой игре ходит первым победитель в предыдущей')
        self.Bind(wx.EVT_MENU, None, self._by_winner_menu_item)
        item = menu.Append(wx.ID_ANY, u'Выбрать движок...',
                           u'Выбрать компьютерного соперника')
        self.Bind(wx.EVT_MENU, self._on_select_engine, item)
        self.menu.Append(menu, u'Настройки')

        self.SetMenuBar(self.menu)

    def _on_close(self, event):
        self._stop_engine()
        if event.GetEventType() in wx.EVT_CLOSE.evtType:
            event.Skip()
        else:
            self.Close()

    def _start_new_game(self, event=None):
        self._stop_engine()
        self._engine = EngineWrapper(self._get_engine_path())
        self.SetTitle('Durak GUI (vs %s)' % self._get_engine_path())

        new_game_data = self._controller.start_new_game(
            ignore_winner=not self._by_winner_menu_item.IsChecked())
        self._trump = new_game_data['trump']

        self._deck.set_card_count(self._controller.deck_count)
        self._deck.set_opened_trump(self._trump)

        self._engine.init(self._trump)
        self._engine.deal(new_game_data['player2_cards'],
                          self._controller.get_game_data_for(self.ENGINE))
        self._top_player_sizer.set_count(len(new_game_data['player2_cards']))

        self._table.remove_all()

        self._bottom_player_sizer.remove_all()
        self._bottom_player_sizer.trump = self._trump
        for card in new_game_data['player1_cards']:
            self._bottom_player_sizer.add_card(card)
        self._bottom_player_sizer.Layout()

        self._set_enabled_cards_and_controls()

        self._panel.Layout()
        self.Layout()
        self.Refresh()

        if self._controller.to_move == self.ENGINE:
            self._engine_move()

    def _set_enabled_cards_and_controls(self):
        human_cards = self._bottom_player_sizer.cards
        on_table = self._table.cards
        self._control_sizer.hide_all()

        if (self._controller.to_move == self.HUMAN
                and self._controller.state == self._controller.States.MOVING):
            self._bottom_player_sizer.set_enabled_cards(
                human_cards.cards_that_can_be_added_to(on_table))
            if on_table:
                self._control_sizer.show_button(self._control_sizer.DISCARD)
                self.SetStatusText(u'Подкидывайте еще, если есть')
            else:
                self.SetStatusText(u'Ходите')
        elif (self._controller.to_move == self.HUMAN and self._controller.state
              == self._controller.States.GIVING_MORE):
            self._control_sizer.show_button(self._control_sizer.ENOUGH)
            if self._remained_give_more > 0:
                self._bottom_player_sizer.set_enabled_cards(
                    human_cards.cards_that_can_be_added_to(on_table))
                self.SetStatusText((u'Беру. Подкидывайте еще, если есть. '
                                    u'Еще можно подкинуть карт: %d.') %
                                   self._remained_give_more)
            else:
                self._bottom_player_sizer.disable_all()
                self.SetStatusText(u'Беру. Больше подкидывать нельзя')

        elif (self._controller.to_move == self.ENGINE and
              self._controller.state == self._controller.States.RESPONDING):
            assert on_table

            self._bottom_player_sizer.set_enabled_cards(
                human_cards.cards_that_can_beat(on_table[-1]))
            self._control_sizer.show_button(self._control_sizer.TAKE)
            self.SetStatusText(u'Отбивайтесь')
        else:
            self._bottom_player_sizer.disable_all()
            self.SetStatusText(u'Думаю...')

    def _on_bottom_player_card_click(self, event):
        card = event.GetEventObject().card
        call_next = None

        if self._controller.state == self._controller.States.MOVING:
            self._controller.register_move(card)
            self._table.move(card)
            call_next = self._engine_respond
        elif self._controller.state == self._controller.States.RESPONDING:
            self._controller.register_response(card)
            self._table.respond(card)
            call_next = self._engine_move
        elif self._controller.state == self._controller.States.GIVING_MORE:
            self._table.give_more(card)

        self._bottom_player_sizer.remove_card(card, do_layout=True)
        self._set_enabled_cards_and_controls()

        self._check_for_game_over()

        if self._controller.state is not None and call_next is not None:
            call_next()

    def _on_discard_button_click(self, event):
        assert self._controller.to_move == self.HUMAN
        assert self._controller.state == self._controller.States.MOVING

        self._control_sizer.hide_all()
        self._controller.register_move(None)
        self._check_for_game_over()
        if self._controller.state == self._controller.States.DEALING:
            self._deal()
            self._engine_move()

    def _on_enough_button_click(self, event):
        assert self._controller.to_move == self.HUMAN
        assert self._controller.state == self._controller.States.GIVING_MORE

        self._control_sizer.hide_all()
        self._controller.register_give_more(self._table.given_more)
        self._check_for_game_over()
        if self._controller.state == self._controller.States.DEALING:
            self._deal()

    def _on_take_button_click(self, event):
        assert self._controller.to_move == self.ENGINE
        assert self._controller.state == self._controller.States.RESPONDING

        self._control_sizer.hide_all()
        self._controller.register_response(None)
        cards = self._engine.give_more(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE))

        # Здесь решается следующая задача: если движку есть, что подкинуть,
        # мы показываем карты на столе полсекунды, а только потом заканчиваем
        # ход. Использовать time.sleep здесь нельзя, т. к. он заблокирует
        # весь GUI. Поэтому используем колбек.
        if cards:
            for card in cards:
                self._table.give_more(card)

            delay = 500
        else:
            delay = 0

        def _rest_of_the_method():
            self._controller.register_give_more(self._table.given_more)
            self._check_for_game_over()
            if self._controller.state == self._controller.States.DEALING:
                self._deal()
                self._engine_move()

        wx.CallLater(delay, _rest_of_the_method)

    def _engine_respond(self):
        assert self._controller.state == self._controller.States.RESPONDING
        assert self._controller.to_move == self.HUMAN

        card = self._engine.respond(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE))
        self._controller.register_response(card)

        if card:
            self._table.respond(card)
            self._top_player_sizer.decrement()

        if self._controller.state == self._controller.States.DEALING:
            self._deal()
            self._engine_move()
        else:
            self._set_enabled_cards_and_controls()
            self._check_for_game_over()

    def _engine_move(self):
        assert self._controller.state == self._controller.States.MOVING
        assert self._controller.to_move == self.ENGINE

        card = self._engine.move(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE))
        self._controller.register_move(card)

        if card:
            self._table.move(card)
            self._top_player_sizer.decrement()

        if self._controller.state == self._controller.States.DEALING:
            self._deal()
        else:
            self._set_enabled_cards_and_controls()
            self._check_for_game_over()

    def _deal(self):
        deal_data = self._controller.deal()
        self._engine.deal(deal_data['player2_cards'],
                          self._controller.get_game_data_for(self.ENGINE))
        self._top_player_sizer.increment(len(deal_data['player2_cards']))

        self._table.remove_all()

        self._deck.set_card_count(self._controller.deck_count)

        for card in deal_data['player1_cards']:
            self._bottom_player_sizer.add_card(card)
        self._bottom_player_sizer.Layout()

        self._set_enabled_cards_and_controls()

    def _check_for_game_over(self):
        if not self._controller.is_game_over():
            return

        wx.CallLater(0, self._new_game_dialog)

    def _new_game_dialog(self):
        if self._controller.winner == self.HUMAN:
            text = u'Победа!'
        elif self._controller.winner == self.ENGINE:
            text = u'Вы проиграли.'
        else:
            text = u'Ничья.'

        dialog = wx.MessageDialog(
            None, u'%s Сыграем еще раз?' % text, u'Игра окончена',
            wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
        if (dialog.ShowModal() == wx.ID_YES):
            self._start_new_game()
        else:
            self.Close()

    def _stop_engine(self):
        if self._engine is not None:
            self._engine.game_end()
            self._engine = None

    @property
    def _remained_give_more(self):
        assert self._controller.state == self._controller.States.GIVING_MORE

        return ((self._top_player_sizer.count - 1) -
                len(self._table.given_more))

    def _on_select_engine(self, event):
        dialog = SelectEngineDialog()
        dialog.ShowModal()
        if dialog.selected_engine:
            self._engine_path = dialog.selected_engine

        dialog.Destroy()

    def _get_engine_path(self):
        if self._engine_path is not None:
            return self._engine_path

        path = None
        engines = get_setting(consts.ENGINES_SETTING, consts.DEFAULT_ENGINES)
        for engine_data in engines:
            if engine_data.get('selected'):
                path = engine_data['path']
                break

        self._engine_path = path
        return self._engine_path
Example #6
0
class PlayFrame(wx.Frame):
    WIDTH = 800
    HEIGHT = 450

    HUMAN = GameController.PLAYER1
    ENGINE = GameController.PLAYER2

    def __init__(self):
        super(PlayFrame, self).__init__(
            parent=None,
            title='Durak GUI',
            size=(self.WIDTH, self.HEIGHT)
        )

        self._create_layout()

        self._controller = GameController(
            player1_name='HUMAN',
            player2_name='ENGINE',
            log_filename=get_filename('last_game'),
            overwrite_log=True
        )
        self._engine = None
        self._engine_path = None

        self._trump = None

        self._start_new_game()

    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = EnemyCardSizer(
            wx.HORIZONTAL, parent=self._panel
        )
        self._bottom_player_sizer = CardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
            on_click=self._on_bottom_player_card_click
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = ControlSizer(wx.HORIZONTAL, parent=self._panel)
        self._control_sizer.set_on_discard_button_click(
            self._on_discard_button_click
        )
        self._control_sizer.set_on_enough_button_click(
            self._on_enough_button_click
        )
        self._control_sizer.set_on_take_button_click(
            self._on_take_button_click
        )

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(
            self._top_player_sizer, flag=wx.EXPAND, proportion=1
        )
        self._main_sizer.Add(
            self._table_sizer, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1
        )
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(
            self._bottom_player_sizer, flag=wx.EXPAND, proportion=1
        )

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Layout()
        self.Refresh()

    def _create_menu(self):
        self.menu = wx.MenuBar()
        menu = wx.Menu()  # Игра
        item = menu.Append(wx.ID_NEW, u'Новая игра', u'Начать новую игру')
        self.Bind(wx.EVT_MENU, self._start_new_game, item)
        item = menu.Append(wx.ID_EXIT, u'Выход', u'Выйти из игры')
        self.Bind(wx.EVT_MENU, self._on_close, item)
        self.menu.Append(menu, u'Игра')

        menu = wx.Menu()  # Настройки
        self._by_winner_menu_item = menu.AppendCheckItem(
            wx.ID_ANY,
            u'Ход под дурака',
            u'В новой игре ходит первым победитель в предыдущей'
        )
        self.Bind(wx.EVT_MENU, None, self._by_winner_menu_item)
        item = menu.Append(
            wx.ID_ANY, u'Выбрать движок...', u'Выбрать компьютерного соперника'
        )
        self.Bind(wx.EVT_MENU, self._on_select_engine, item)
        self.menu.Append(menu, u'Настройки')

        self.SetMenuBar(self.menu)

    def _on_close(self, event):
        self._stop_engine()
        if event.GetEventType() in wx.EVT_CLOSE.evtType:
            event.Skip()
        else:
            self.Close()

    def _start_new_game(self, event=None):
        self._stop_engine()
        self._engine = EngineWrapper(self._get_engine_path())
        self.SetTitle('Durak GUI (vs %s)' % self._get_engine_path())

        new_game_data = self._controller.start_new_game(
            ignore_winner=not self._by_winner_menu_item.IsChecked()
        )
        self._trump = new_game_data['trump']

        self._deck.set_card_count(self._controller.deck_count)
        self._deck.set_opened_trump(self._trump)

        self._engine.init(self._trump)
        self._engine.deal(
            new_game_data['player2_cards'],
            self._controller.get_game_data_for(self.ENGINE)
        )
        self._top_player_sizer.set_count(len(new_game_data['player2_cards']))

        self._table.remove_all()

        self._bottom_player_sizer.remove_all()
        self._bottom_player_sizer.trump = self._trump
        for card in new_game_data['player1_cards']:
            self._bottom_player_sizer.add_card(card)
        self._bottom_player_sizer.Layout()

        self._set_enabled_cards_and_controls()

        self._panel.Layout()
        self.Layout()
        self.Refresh()

        if self._controller.to_move == self.ENGINE:
            self._engine_move()

    def _set_enabled_cards_and_controls(self):
        human_cards = self._bottom_player_sizer.cards
        on_table = self._table.cards
        self._control_sizer.hide_all()

        if (self._controller.to_move == self.HUMAN and
                self._controller.state == self._controller.States.MOVING):
            self._bottom_player_sizer.set_enabled_cards(
                human_cards.cards_that_can_be_added_to(on_table)
            )
            if on_table:
                self._control_sizer.show_button(self._control_sizer.DISCARD)
                self.SetStatusText(u'Подкидывайте еще, если есть')
            else:
                self.SetStatusText(u'Ходите')
        elif (self._controller.to_move == self.HUMAN and
                self._controller.state == self._controller.States.GIVING_MORE):
            self._control_sizer.show_button(self._control_sizer.ENOUGH)
            if self._remained_give_more > 0:
                self._bottom_player_sizer.set_enabled_cards(
                    human_cards.cards_that_can_be_added_to(on_table)
                )
                self.SetStatusText(
                    (u'Беру. Подкидывайте еще, если есть. '
                     u'Еще можно подкинуть карт: %d.') %
                     self._remained_give_more
                )
            else:
                self._bottom_player_sizer.disable_all()
                self.SetStatusText(u'Беру. Больше подкидывать нельзя')

        elif (self._controller.to_move == self.ENGINE and
                self._controller.state == self._controller.States.RESPONDING):
            assert on_table

            self._bottom_player_sizer.set_enabled_cards(
                human_cards.cards_that_can_beat(on_table[-1])
            )
            self._control_sizer.show_button(self._control_sizer.TAKE)
            self.SetStatusText(u'Отбивайтесь')
        else:
            self._bottom_player_sizer.disable_all()
            self.SetStatusText(u'Думаю...')

    def _on_bottom_player_card_click(self, event):
        card = event.GetEventObject().card
        call_next = None

        if self._controller.state == self._controller.States.MOVING:
            self._controller.register_move(card)
            self._table.move(card)
            call_next = self._engine_respond
        elif self._controller.state == self._controller.States.RESPONDING:
            self._controller.register_response(card)
            self._table.respond(card)
            call_next = self._engine_move
        elif self._controller.state == self._controller.States.GIVING_MORE:
            self._table.give_more(card)

        self._bottom_player_sizer.remove_card(card, do_layout=True)
        self._set_enabled_cards_and_controls()

        self._check_for_game_over()

        if self._controller.state is not None and call_next is not None:
            call_next()

    def _on_discard_button_click(self, event):
        assert self._controller.to_move == self.HUMAN
        assert self._controller.state == self._controller.States.MOVING

        self._control_sizer.hide_all()
        self._controller.register_move(None)
        self._check_for_game_over()
        if self._controller.state == self._controller.States.DEALING:
            self._deal()
            self._engine_move()

    def _on_enough_button_click(self, event):
        assert self._controller.to_move == self.HUMAN
        assert self._controller.state == self._controller.States.GIVING_MORE

        self._control_sizer.hide_all()
        self._controller.register_give_more(self._table.given_more)
        self._check_for_game_over()
        if self._controller.state == self._controller.States.DEALING:
            self._deal()

    def _on_take_button_click(self, event):
        assert self._controller.to_move == self.ENGINE
        assert self._controller.state == self._controller.States.RESPONDING

        self._control_sizer.hide_all()
        self._controller.register_response(None)
        cards = self._engine.give_more(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE)
        )

        # Здесь решается следующая задача: если движку есть, что подкинуть,
        # мы показываем карты на столе полсекунды, а только потом заканчиваем
        # ход. Использовать time.sleep здесь нельзя, т. к. он заблокирует
        # весь GUI. Поэтому используем колбек.
        if cards:
            for card in cards:
                self._table.give_more(card)

            delay = 500
        else:
            delay = 0

        def _rest_of_the_method():
            self._controller.register_give_more(self._table.given_more)
            self._check_for_game_over()
            if self._controller.state == self._controller.States.DEALING:
                self._deal()
                self._engine_move()

        wx.CallLater(delay, _rest_of_the_method)

    def _engine_respond(self):
        assert self._controller.state == self._controller.States.RESPONDING
        assert self._controller.to_move == self.HUMAN

        card = self._engine.respond(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE)
        )
        self._controller.register_response(card)

        if card:
            self._table.respond(card)
            self._top_player_sizer.decrement()

        if self._controller.state == self._controller.States.DEALING:
            self._deal()
            self._engine_move()
        else:
            self._set_enabled_cards_and_controls()
            self._check_for_game_over()

    def _engine_move(self):
        assert self._controller.state == self._controller.States.MOVING
        assert self._controller.to_move == self.ENGINE

        card = self._engine.move(
            self._controller.on_table,
            self._controller.get_game_data_for(self.ENGINE)
        )
        self._controller.register_move(card)

        if card:
            self._table.move(card)
            self._top_player_sizer.decrement()

        if self._controller.state == self._controller.States.DEALING:
            self._deal()
        else:
            self._set_enabled_cards_and_controls()
            self._check_for_game_over()

    def _deal(self):
        deal_data = self._controller.deal()
        self._engine.deal(
            deal_data['player2_cards'],
            self._controller.get_game_data_for(self.ENGINE)
        )
        self._top_player_sizer.increment(len(deal_data['player2_cards']))

        self._table.remove_all()

        self._deck.set_card_count(self._controller.deck_count)

        for card in deal_data['player1_cards']:
            self._bottom_player_sizer.add_card(card)
        self._bottom_player_sizer.Layout()

        self._set_enabled_cards_and_controls()

    def _check_for_game_over(self):
        if not self._controller.is_game_over():
            return

        wx.CallLater(0, self._new_game_dialog)

    def _new_game_dialog(self):
        if self._controller.winner == self.HUMAN:
            text = u'Победа!'
        elif self._controller.winner == self.ENGINE:
            text = u'Вы проиграли.'
        else:
            text = u'Ничья.'

        dialog = wx.MessageDialog(
            None,
            u'%s Сыграем еще раз?' % text,
            u'Игра окончена',
            wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION
        )
        if (dialog.ShowModal() == wx.ID_YES):
            self._start_new_game()
        else:
            self.Close()

    def _stop_engine(self):
        if self._engine is not None:
            self._engine.game_end()
            self._engine = None

    @property
    def _remained_give_more(self):
        assert self._controller.state == self._controller.States.GIVING_MORE

        return (
            (self._top_player_sizer.count - 1) -
            len(self._table.given_more)
        )

    def _on_select_engine(self, event):
        dialog = SelectEngineDialog()
        dialog.ShowModal()
        if dialog.selected_engine:
            self._engine_path = dialog.selected_engine

        dialog.Destroy()

    def _get_engine_path(self):
        if self._engine_path is not None:
            return self._engine_path

        path = None
        engines = get_setting(consts.ENGINES_SETTING, consts.DEFAULT_ENGINES)
        for engine_data in engines:
            if engine_data.get('selected'):
                path = engine_data['path']
                break

        self._engine_path = path
        return self._engine_path
Example #7
0
    def setUp(self):
        self._app = wx.PySimpleApp()

        self.panel = TablePanel(parent=None)
Example #8
0
class TablePanelTest(unittest.TestCase):
    def setUp(self):
        self._app = wx.PySimpleApp()

        self.panel = TablePanel(parent=None)

    def test_add_card_creates_card_and_adds_it(self):
        card = DurakCard('6H')
        self.assertFalse(card in self.panel._cards)

        with patch('durak.gui.widgets.Card') as Card_mock:
            self.panel._add_card(card, (1, 2))
            Card_mock.assert_called_once_with(
                parent=self.panel, card=card, pos=(1, 2)
            )
            self.assertTrue(card in self.panel._cards)

    def test_move(self):
        card = DurakCard('6H')
        with patch.object(self.panel, '_add_card') as add_card_mock:
            self.panel.move(card)
            add_card_mock.assert_called_once_with(
                card,
                (0 + self.panel.PADDING_LEFT, self.panel.UPPER_CARD_Y_OFFSET)
            )

    def test_respond(self):
        card_mock = Mock()
        card_mock.GetPosition.return_value = wx.Point(10, 10)
        self.panel._cards[DurakCard('6H')] = card_mock
        card = DurakCard('7H')

        with patch.object(self.panel, '_add_card') as add_card_mock:
            self.panel.respond(card)
            add_card_mock.assert_called_once_with(
                card,
                wx.Point(10, 10) + wx.Point(
                    self.panel.UPPER_CARD_X_OFFSET,
                    - self.panel.UPPER_CARD_Y_OFFSET
                )
            )

    def test_give_more(self):
        card_mock = Mock()
        card_mock.GetPosition.return_value = (10, 10)
        self.panel._cards[DurakCard('6H')] = card_mock
        card = DurakCard('7H')

        self.assertEqual(self.panel._given_more, [])

        with patch.object(self.panel, '_add_card') as add_card_mock:
            self.panel.give_more(card)
            add_card_mock.assert_called_once_with(
                card,
                (
                    10 + self.panel.GIVEN_MORE_OFFSET,
                    self.panel.UPPER_CARD_Y_OFFSET
                )
            )
            self.assertEqual(self.panel._given_more, [DurakCard('7H')])

    def test_remove_all_destroys_all_the_cards(self):
        cards = [DurakCard('6H'), DurakCard('7H'), DurakCard('8H')]
        card_values = [Mock(), Mock(), Mock()]

        self.panel._cards = dict(zip(cards, card_values))
        self.panel._give_more_mode = True

        self.panel.remove_all()
        self.assertTrue(all(card.Destroy.called for card in card_values))
        self.assertEqual(self.panel._cards, {})
        self.assertEqual(self.panel._given_more, [])

    def test_cards_property(self):
        cards = [DurakCard('6H'), DurakCard('7H'), DurakCard('8H')]
        card_values = [Mock(), Mock(), Mock()]

        self.panel._cards = OrderedDict(zip(cards, card_values))

        self.assertEqual(self.panel.cards, cards)

    def test_given_more_property(self):
        cards = [DurakCard('6H'), DurakCard('7H')]
        self.panel._given_more = cards

        self.assertEqual(self.panel.given_more, cards)

    def test_is_odd_property(self):
        self.panel._cards[DurakCard('6H')] = Mock()
        self.assertTrue(self.panel._is_odd)

        self.panel._cards[DurakCard('7H')] = Mock()
        self.assertFalse(self.panel._is_odd)

    def test_pop_if_no_given_more(self):
        card_mock = Mock()
        self.panel._cards[DurakCard('6H')] = card_mock

        self.assertEqual(self.panel.pop(), DurakCard('6H'))
        self.assertDictEqual(self.panel._cards, {})
        card_mock.Destroy.assert_called()

    def test_pop_with_given_more(self):
        card_mock = Mock()
        self.panel._cards[DurakCard('6H')] = Mock()
        self.panel._cards[DurakCard('7H')] = card_mock
        self.panel._given_more = [DurakCard('7H')]

        self.assertEqual(self.panel.pop(), DurakCard('7H'))
        self.assertItemsEqual(self.panel._cards.keys(), [DurakCard('6H')])
        card_mock.Destroy.assert_called()
Example #9
0
    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )
        self._bottom_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._to_game_start_button = wx.Button(parent=self._panel, label=u'<<')
        self._to_game_start_button.Bind(
            wx.EVT_BUTTON, self._menu_on_to_game_start
        )
        self._prev_button = wx.Button(parent=self._panel, label=u'<')
        self._prev_button.Bind(wx.EVT_BUTTON, self._menu_on_prev)
        self._next_button = wx.Button(parent=self._panel, label=u'>')
        self._next_button.Bind(wx.EVT_BUTTON, self._menu_on_next)
        self._to_game_end_button = wx.Button(parent=self._panel, label=u'>>')
        self._to_game_end_button.Bind(wx.EVT_BUTTON, self._menu_on_to_game_end)
        self._control_sizer.AddMany([
            self._to_game_start_button,
            self._prev_button,
            self._next_button,
            self._to_game_end_button
        ])

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(
            self._top_player_sizer, flag=wx.EXPAND, proportion=1
        )
        self._main_sizer.Add(
            self._table_sizer, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1
        )
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(
            self._bottom_player_sizer, flag=wx.EXPAND, proportion=1
        )

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Refresh()
        self.Layout()

        self.PLAYER_SIZER_MAP = {
            LogViewer.PLAYER1: self._bottom_player_sizer,
            LogViewer.PLAYER2: self._top_player_sizer,
        }

        self._menu_on_open()
Example #10
0
class ViewLogFrame(wx.Frame):
    WIDTH = 800
    HEIGHT = 500

    LAST_DIR_SETTING = 'last_log_dir'

    def __init__(self):
        super(ViewLogFrame, self).__init__(
            parent=None,
            title='Durak log viewer',
            size=(self.WIDTH, self.HEIGHT)
        )

        self._log_viever = None

        self._create_layout()

    def _create_layout(self):
        self.Bind(wx.EVT_CLOSE, self._on_close)

        self._panel = wx.Panel(parent=self)

        self._top_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )
        self._bottom_player_sizer = LabeledCardSizer(
            wx.HORIZONTAL,
            parent=self._panel,
        )

        self._table_sizer = wx.FlexGridSizer(cols=2, rows=1)
        self._table_sizer.AddGrowableCol(0)

        self._table = TablePanel(parent=self._panel)
        self._table_sizer.Add(self._table, proportion=1)

        self._deck = DeckPanel(self._panel, size=(100, 130))
        self._table_sizer.Add(self._deck)

        self._control_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._to_game_start_button = wx.Button(parent=self._panel, label=u'<<')
        self._to_game_start_button.Bind(
            wx.EVT_BUTTON, self._menu_on_to_game_start
        )
        self._prev_button = wx.Button(parent=self._panel, label=u'<')
        self._prev_button.Bind(wx.EVT_BUTTON, self._menu_on_prev)
        self._next_button = wx.Button(parent=self._panel, label=u'>')
        self._next_button.Bind(wx.EVT_BUTTON, self._menu_on_next)
        self._to_game_end_button = wx.Button(parent=self._panel, label=u'>>')
        self._to_game_end_button.Bind(wx.EVT_BUTTON, self._menu_on_to_game_end)
        self._control_sizer.AddMany([
            self._to_game_start_button,
            self._prev_button,
            self._next_button,
            self._to_game_end_button
        ])

        self._main_sizer = wx.FlexGridSizer(cols=1, rows=4)
        self._main_sizer.AddGrowableCol(0)
        self._main_sizer.AddGrowableRow(1)
        self._main_sizer.Add(
            self._top_player_sizer, flag=wx.EXPAND, proportion=1
        )
        self._main_sizer.Add(
            self._table_sizer, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1
        )
        self._main_sizer.Add(self._control_sizer, flag=wx.EXPAND, proportion=1)
        self._main_sizer.Add(
            self._bottom_player_sizer, flag=wx.EXPAND, proportion=1
        )

        self._panel.SetSizer(self._main_sizer)

        self.CreateStatusBar()

        self._create_menu()

        self._panel.Layout()
        self.Refresh()
        self.Layout()

        self.PLAYER_SIZER_MAP = {
            LogViewer.PLAYER1: self._bottom_player_sizer,
            LogViewer.PLAYER2: self._top_player_sizer,
        }

        self._menu_on_open()

    def _create_menu(self):
        self.menu = wx.MenuBar()
        menu = wx.Menu()  # Файл
        item = menu.Append(
            wx.ID_OPEN, u'Открыть файл лога...', u'Открыть файл лога'
        )
        self.Bind(wx.EVT_MENU, self._menu_on_open, item)
        item = menu.Append(
            wx.ID_ANY,
            u'Список игр...\tCTRL+L',
            u'Просмотреть список игр в открытом файле'
        )
        self.Bind(wx.EVT_MENU, self._menu_on_select_game, item)
        item = menu.Append(wx.ID_EXIT, u'Выход', u'Закрыть программу')
        self.Bind(wx.EVT_MENU, self._on_close, item)
        self.menu.Append(menu, u'Файл')

        menu = wx.Menu()  # Текущая игра
        self._to_game_start_menu_item = menu.Append(
            wx.ID_ANY, u'В начало <<\tHOME', u'Перейти в начало игры'
        )
        self.Bind(
            wx.EVT_MENU,
            self._menu_on_to_game_start,
            self._to_game_start_menu_item
        )
        self._prev_menu_item = menu.Append(
            wx.ID_ANY, u'Назад <\tCTRL+LEFT', u'Вернуться на ход назад'
        )
        self.Bind(wx.EVT_MENU, self._menu_on_prev, self._prev_menu_item)
        self._next_menu_item = menu.Append(
            wx.ID_ANY, u'Вперед >\tCTRL+RIGHT', u'Перейти на ход вперед'
        )
        self.Bind(wx.EVT_MENU, self._menu_on_next, self._next_menu_item)
        self._to_game_end_menu_item = menu.Append(
            wx.ID_ANY, u'В конец >>\tEND', u'Перейти в конец игры'
        )
        self.Bind(
            wx.EVT_MENU,
            self._menu_on_to_game_end,
            self._to_game_end_menu_item
        )
        self.menu.Append(menu, u'Текущая игра')

        self.SetMenuBar(self.menu)

    def _on_close(self, event):
        if event.GetEventType() in wx.EVT_CLOSE.evtType:
            event.Skip()
        else:
            self.Close()

    def _menu_on_open(self, event=None):
        last_directory = get_setting(
            self.LAST_DIR_SETTING, os.path.expanduser('~')
        )
        dialog = wx.FileDialog(
            self, u'Выберите файл лога', last_directory, '', '*', wx.OPEN
        )
        if dialog.ShowModal() != wx.ID_OK:
            self.Close()

        filename = dialog.GetPath()
        dialog.Destroy()

        self._log_viever = LogViewer(filename)
        set_setting(self.LAST_DIR_SETTING, os.path.dirname(filename))

        self._menu_on_select_game()

    def _menu_on_select_game(self, event=None):
        choices = [
            '%d. %s' % (i, game['title']) for i, game
            in enumerate(self._log_viever.iterindex(), start=1)
        ]
        dialog = wx.SingleChoiceDialog(
            self,
            u'Выберите игру',
            u'Список игр',
            choices,
            wx.CHOICEDLG_STYLE,
        )
        dialog.SetSize((500, 400))
        dialog.Refresh()
        if dialog.ShowModal() == wx.ID_OK:
            self._log_viever.load_game(dialog.GetSelection())
            self._bottom_player_sizer.set_label(self._log_viever.player1_name)
            self._top_player_sizer.set_label(self._log_viever.player2_name)
            self._menu_on_to_game_start()

        dialog.Destroy()

    def _menu_on_to_game_start(self, event=None):
        self._log_viever.to_game_start()

        if not self._log_viever.has_next:
            return

        self._deck.set_opened_trump(self._log_viever.opened_trump)

        for card_sizer in (self._bottom_player_sizer, self._top_player_sizer):
            card_sizer.trump = self._log_viever.opened_trump
            card_sizer.remove_all()

        log_event = self._log_viever.get_next()
        self._new_move(log_event)

        self._panel.Layout()
        self.Layout()
        self.Refresh()

        self._set_enabled_controls()

    def _menu_on_to_game_end(self, event=None):
        self._log_viever.to_game_end()

        if not self._log_viever.has_prev:
            return

        log_event = self._log_viever.get_prev()
        self._new_move(log_event, with_table=True)

        self._set_enabled_controls()

    def _menu_on_next(self, event):
        if not self._log_viever.has_next:
            return

        log_event = self._log_viever.get_next()
        if log_event['event_type'] == self._log_viever.NEW_MOVE:
            self._new_move(log_event)
            return

        card_sizer = self.PLAYER_SIZER_MAP[log_event['to_move']]
        table_method = getattr(self._table, log_event['event_type'])

        card = log_event['card']
        table_method(card)
        card_sizer.remove_card(card, do_layout=True)

        self._set_enabled_controls()

    def _menu_on_prev(self, event):
        if not self._log_viever.has_prev:
            return

        log_event = self._log_viever.get_prev()
        if log_event['event_type'] == self._log_viever.NEW_MOVE:
            self._new_move(log_event, with_table=True)
            return

        card_sizer = self.PLAYER_SIZER_MAP[log_event['to_move']]

        card = log_event['card']
        self._table.pop()
        card_sizer.add_card(card, do_layout=True)

        self._set_enabled_controls()

    def _new_move(self, log_event, with_table=False):
        assert log_event['event_type'] == self._log_viever.NEW_MOVE

        self._table.remove_all()
        self._deck.set_card_count(log_event['deck_count'])

        if with_table:
            for index, card in enumerate(log_event['moves_and_responds']):
                if index % 2:
                    self._table.respond(card)
                else:
                    self._table.move(card)


            for card in log_event['given_more']:
                self._table.give_more(card)


        card_sizers = (self._bottom_player_sizer, self._top_player_sizer)
        card_sets = (log_event['player1_cards'], log_event['player2_cards'])

        for card_sizer, card_set in zip(card_sizers, card_sets):
            if with_table:
                card_sizer.remove_all()
                cards_to_add = card_set - set(
                    log_event['moves_and_responds'] + log_event['given_more']
                )
            else:
                cards_to_add = card_set - card_sizer.cards

            for card in cards_to_add:
                card_sizer.add_card(card)
            card_sizer.Layout()

        if log_event['to_move'] == self._log_viever.PLAYER1:
            self.SetStatusText(u'Атакует игрок снизу')
        else:
            self.SetStatusText(u'Атакует игрок сверху')

        self._set_enabled_controls()

    def _set_enabled_controls(self):
        all_controls = {
            self._to_game_start_button,
            self._to_game_end_button,
            self._next_button,
            self._prev_button,
            self._to_game_end_menu_item,
            self._to_game_start_menu_item,
            self._prev_menu_item,
            self._next_menu_item,
        }
        [control.Enable(False) for control in all_controls]

        if not self._log_viever or not self._log_viever.has_game_loaded:
            return

        if self._log_viever.has_prev:
            self._to_game_start_button.Enable()
            self._prev_button.Enable()
            self._to_game_start_menu_item.Enable()
            self._prev_menu_item.Enable()

        if self._log_viever.has_next:
            self._to_game_end_button.Enable()
            self._next_button.Enable()
            self._to_game_end_menu_item.Enable()
            self._next_menu_item.Enable()