Ejemplo n.º 1
0
    def get_move(self) -> chess.engine.PlayResult:
        """ Parses new move expected format <from_square><to_square> or a game end offer

        Prints the current position and waits for an input move.
        Input move should be in format <from_square><to_square> ie. e2e4 to move pawn to e4. The move should be
        legal in the current position

        :returns: Move played in engine plays format
        """
        print(self._board)
        while True:
            txt = input("Set move:")
            txt = txt.replace(" ", "")
            draw = False
            if txt == "resign":
                self._resigned = True
                return engine.PlayResult(None, None, resigned=True)
            if "draw" in txt:
                draw = True
                txt = txt.replace("draw", "")
            try:
                move = chess.Move.from_uci(txt)
                if move in self._board.legal_moves:
                    break
                else:
                    print("not a legal move")
            except ValueError:
                print("Not parsed")
        self._board.push(move)
        return engine.PlayResult(move, None, draw_offered=draw)
Ejemplo n.º 2
0
    def _hw_detect_move_player(self):
        """ Detect move from player

        We compare the expected occupancy with the actual occupancy. If two squares are different then
        we set the square where the clients piece used to be as the from square and the other square as the too
        square. Using this we create a candidate move (from_square, to_square). If this is a legal move then we
        will place this move in the output field. Note that the chess library will detect any other side effects
        from the move like captures, promotions and en passant. The internal board will be updated correctly
        and the diff will be marked on the board.
        """
        draw_offer = False
        while not self.game_is_over():
            occupancy = self._hwi.get_occupancy()
            diff = self._diff_occupancy_board(occupancy)
            self._hwi.mark_squares(diff)

            offers = self._hwi.game_end_offers()
            if offers is HardwareInterface.Offer.RESIGN:
                self._resigned = True
                return engine.PlayResult(None, None, resigned=True)
            if offers is HardwareInterface.Offer.DRAW:
                draw_offer = True

            # Detect all changed squares
            squares = []
            for file in range(8):
                for rank in range(8):
                    if diff[file][rank]:
                        squares.append(chess.square(file, rank))

            if len(squares) == 2:  # Try to convert changed squares to legal move
                with self._board_lock:  # Promotion not implemented
                    if self._board.piece_at(squares[0]) is not None and \
                            self._board.piece_at(squares[0]).color == self.color:
                        move = chess.Move(squares[0], squares[1])
                    elif self._board.piece_at(squares[1]) is not None and \
                            self._board.piece_at(squares[1]).color == self.color:
                        move = chess.Move(squares[1], squares[0])
                    else:
                        continue
                    if self._board.piece_at(move.from_square).piece_type == chess.PAWN and \
                            ((self.color == chess.WHITE and chess.square_rank(move.to_square) == 7) or
                             (self.color == chess.BLACK and chess.square_rank(move.to_square) == 0)):
                        move.promotion = chess.QUEEN
                    move_is_legal = move in self._board.legal_moves  # save result to not use two locks
                    if move_is_legal and move.promotion is not None:
                        move.promotion = self._hwi.promotion_piece()

                if move_is_legal:
                    with self._board_lock:
                        self._board.push(move)
                    with self._playResult_lock:
                        self._output_playResult = engine.PlayResult(move, None, draw_offered=draw_offer)
                    return
            self._update_display()
            sleep(SLEEP_TIME)
    def test__hw_play_move_opponent_move_promote(self):
        """ Test promotion move """
        self.hc._board.set_fen(
            "4k3/P7/8/8/8/8/8/4K3 w - - 0 1")  # Lone pawn a7
        self.hc._input_playResult = engine.PlayResult(
            chess.Move(chess.square(0, 6), chess.square(0, 7), chess.QUEEN),
            None)  # New move a8Q

        # Configure occupancy for first, second and third call
        set_occupancy(self.hwi_mock, self.hc._board)
        occ1 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2 = deepcopy(self.hwi_mock.get_occupancy.return_value)

        occ2[0][6] = False
        occ2[0][7] = True
        self.hwi_mock.get_occupancy.side_effect = [occ1, occ2]

        # Itr 1 no move, itr 2 promoted piece, itr 3 terminate
        with patch.object(HardwareClient.HardwareClient,
                          'game_is_over') as game_over_mock:
            game_over_mock.side_effect = [False, False, True]
            self.hc._hw_play_move_opponent()

        self.assertIsNone(self.hc._input_playResult,
                          "Input move has not been processed correctly")

        # Check that in first iterations a7 and a8 are marked
        marking2 = [[False] * 8 for _ in range(8)]
        marking1 = deepcopy(marking2)
        marking1[0][6] = True
        marking1[0][7] = True
        self.hwi_mock.mark_squares.assert_has_calls(
            [call(marking1), call(marking2)],
            "Incorrect squares have been marked")
    def test__hw_play_move_opponent_move_castle(self):
        """ Test castle input move """
        self.hc._board.set_fen("4k3/8/8/8/8/8/8/4K2R w K - 0 1")
        self.hc._input_playResult = engine.PlayResult(
            chess.Move(chess.square(4, 0), chess.square(6, 0)),
            None)  # New move O-O

        # Configure occupancy for first, second and third call
        set_occupancy(self.hwi_mock, self.hc._board)
        occ1 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2[4][0] = False
        occ2[6][0] = True
        self.hwi_mock.get_occupancy.side_effect = [occ1, occ2]

        # Itr 1 no move, itr 2 move, itr 4 terminate
        with patch.object(HardwareClient.HardwareClient,
                          'game_is_over') as game_over_mock:
            game_over_mock.side_effect = [False, False, True]
            self.hc._hw_play_move_opponent()

        self.assertIsNone(self.hc._input_playResult,
                          "Input move has not been processed correctly")

        # Check that in first iteration e1 and g1 are marked and next iteration f1 and h1 are marked
        marking1 = [[False] * 8 for _ in range(8)]
        marking2 = deepcopy(marking1)
        marking1[4][0] = True
        marking1[6][0] = True
        marking2[7][
            0] = True  # Rook move still needs to be played but king move is sufficient to finish method
        marking2[5][0] = True
        self.hwi_mock.mark_squares.assert_has_calls(
            [call(marking1), call(marking2)],
            "Incorrect squares have been marked")
    def test__hw_play_move_opponent_move_normal(self):
        """ Test normal input move """
        self.hc._input_playResult = engine.PlayResult(
            chess.Move(chess.square(0, 1), chess.square(0, 3)),
            None)  # New move pawn a4

        # Configure occupancy for first and second call
        set_occupancy(self.hwi_mock, self.hc._board)
        occ1 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2[0][1] = False
        occ2[0][3] = True
        self.hwi_mock.get_occupancy.side_effect = [occ1, occ2]

        # Play move itr 1 no move, itr 2 move made, itr 3 terminate
        with patch.object(HardwareClient.HardwareClient,
                          'game_is_over') as game_over_mock:
            game_over_mock.side_effect = [False, False, True]
            self.hc._hw_play_move_opponent()

        self.assertIsNone(self.hc._input_playResult,
                          "Input move has not been processed correctly")

        # Check that in first iteration a2 and a4 are marked and next iteration no squares are marked
        marking2 = [[False] * 8 for _ in range(8)]
        marking1 = deepcopy(marking2)
        marking1[0][1] = True
        marking1[0][3] = True
        self.hwi_mock.mark_squares.assert_has_calls(
            [call(marking1), call(marking2)],
            "Incorrect squares have been marked")
 def test_get_move(self):
     """ Test get_move method"""
     play_result = engine.PlayResult(
         chess.Move(chess.square(0, 1), chess.square(0, 3)), None)
     self.hc._output_playResult = play_result
     self.assertEqual(self.hc.get_move(), play_result,
                      "Output move not returned")
     self.assertEqual(self.hc._output_playResult, None,
                      "Internal output field not cleared")
 def test__hw_control_case_3(self):
     """ Test _hw_control wait for opponent move to be played on hardware"""
     self.hc._board.turn = chess.BLACK
     self.hc.color = chess.WHITE
     self.hc._input_playResult = engine.PlayResult(
         chess.Move(chess.square(0, 1), chess.square(0, 3)), None)
     with patch.object(HardwareClient.HardwareClient,
                       'game_is_over') as game_over_mock:
         game_over_mock.side_effect = [False, True]
         with patch.object(HardwareClient.HardwareClient,
                           '_hw_play_move_opponent') as target_mock:
             self.hc._hw_control()
             self.assertTrue(target_mock.called,
                             "Expected method was not called")
    def test_set_move(self):
        """ Test set_move method """
        input_pr = engine.PlayResult(
            chess.Move(chess.square(0, 1), chess.square(0, 3)), None)
        self.hc.set_move(input_pr)
        internal_pr = self.hc._input_playResult
        self.assertNotEqual(
            internal_pr, input_pr,
            "HC should not reuse input object for threadsafety")

        #  Move overrides __eq__ check if move is a new object
        input_pr.move.to_square = chess.square(0, 2)
        self.assertNotEqual(
            internal_pr.move, input_pr.move,
            "HC should not reuse input object for threadsafety")
    def test__hw_play_move_opponent_move_capture(self):
        """ Test capture input move """
        self.hc._board.push(chess.Move(chess.square(1, 1),
                                       chess.square(1, 3)))  # b4
        self.hc._board.push(chess.Move(chess.square(0, 6),
                                       chess.square(0, 4)))  # a5
        self.hc._input_playResult = engine.PlayResult(
            chess.Move(chess.square(1, 3), chess.square(0, 4)),
            None)  # New move capture a5

        # Configure occupancy for first, second and third call
        set_occupancy(self.hwi_mock, self.hc._board)
        occ1 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ3 = deepcopy(self.hwi_mock.get_occupancy.return_value)
        occ2[1][3] = False
        occ2[0][4] = False
        occ3[1][3] = False
        occ3[0][4] = True
        self.hwi_mock.get_occupancy.side_effect = [occ1, occ2, occ3]

        # Itr 1 no move, itr 2 remove captured piece, itr3 add new piece, itr 4 terminate
        with patch.object(HardwareClient.HardwareClient,
                          'game_is_over') as game_over_mock:
            game_over_mock.side_effect = [False, False, False, True]
            self.hc._hw_play_move_opponent()

        self.assertIsNone(self.hc._input_playResult,
                          "Input move has not been processed correctly")

        # Check that in first two iterations b4 and a5 are marked and next iteration no squares are marked
        marking3 = [[False] * 8 for _ in range(8)]
        marking1 = deepcopy(marking3)
        marking1[1][3] = True
        marking1[0][4] = True
        marking2 = deepcopy(marking1)
        self.hwi_mock.mark_squares.assert_has_calls(
            [call(marking1), call(marking2),
             call(marking3)], "Incorrect squares have been marked")
 def test__hw_play_move_opponent_move_None(self):
     """ Test if method does not crash when input is not a move """
     self.hc._input_playResult = engine.PlayResult(None,
                                                   None,
                                                   resigned=True)
     self.hc._hw_play_move_opponent()
 def test__hw_wait_move_opponent(self):
     """ Test if method terminates once input is given """
     self.hc._input_playResult = engine.PlayResult(
         chess.Move(chess.square(0, 1), chess.square(0, 3)), None)
     self.hc._hw_wait_move_opponent()