Example #1
0
    def apply(self, move: Move):
        """Applies a move to the board."""

        s = self._pieces[move.source]

        # Defaults to a BASIC JUMP move if not specified
        if not move.move_type:
            move.move_type = enums.MoveType.JUMP
        if not move.move_variant:
            move.move_variant = enums.MoveVariant.BASIC

        # Call the quantum board to apply the move
        meas = self.board.do_move(move)

        # Cache the probability distribution
        self._probs = self.board.get_probability_distribution(self.reps)

        # Set the turn to be the next player
        self.white_moves = not self.white_moves

        # If the move was not successful, return
        # Otherwise, update classical bits
        if not meas:
            return meas

        # Assume that squares with low probability are empty
        if self._probs[bu.square_to_bit(move.source)] < 0.01:
            self._pieces[move.source] = c.EMPTY

        # Update classical bits
        self._pieces[move.target] = s
        if move.target2:
            self._pieces[move.target2] = s
        if move.source2 and self._probs[bu.square_to_bit(move.source2)] < 0.01:
            self._pieces[move.source2] = c.EMPTY

        # Update en passant and castling flags
        self.ep_flag = self._ep_flag(move, s)
        if s == c.KING:
            self.castling_flags['O-O'] = False
            self.castling_flags['O-O-O'] = False
        if s == -c.KING:
            self.castling_flags['o-o'] = False
            self.castling_flags['o-o-o'] = False
        if move.source == 'a1' or move.target == 'a1':
            self.castling_flags['O-O-O'] = False
        if move.source == 'a8' or move.target == 'a8':
            self.castling_flags['o-o-o'] = False
        if move.source == 'h1' or move.target == 'h1':
            self.castling_flags['O-O'] = False
        if move.source == 'h8' or move.target == 'h8':
            self.castling_flags['o-o'] = False

        return meas
Example #2
0
def test_string_round_trip():
    move_strings = (
        'a1b4:JUMP:BASIC',
        'a1b4.m0:JUMP:BASIC',
        'a1b4.m1:JUMP:BASIC',
        'a1b4.m1:JUMP:BASIC',
        'c8^g4f5.m0:SPLIT_SLIDE:BASIC',
    )
    for s in move_strings:
        assert s == Move.from_string(s).to_string(include_type=True)
Example #3
0
def test_move_round_trip():
    moves = (Move('a1',
                  'b4',
                  move_type=enums.MoveType.JUMP,
                  move_variant=enums.MoveVariant.BASIC),
             Move('a1',
                  'b4',
                  move_type=enums.MoveType.JUMP,
                  move_variant=enums.MoveVariant.BASIC,
                  measurement=0),
             Move('a1',
                  'b4',
                  move_type=enums.MoveType.JUMP,
                  move_variant=enums.MoveVariant.BASIC,
                  measurement=1),
             Move('a1',
                  'b4',
                  move_type=enums.MoveType.JUMP,
                  move_variant=enums.MoveVariant.BASIC,
                  measurement=1),
             Move('c8',
                  'g4',
                  target2='f5',
                  move_type=enums.MoveType.SPLIT_SLIDE,
                  move_variant=enums.MoveVariant.BASIC,
                  measurement=0))
    for m in moves:
        assert m == Move.from_string(m.to_string(include_type=True))
Example #4
0
def test_to_string():
    assert str(Move('a1', 'b4')) == 'a1b4'
    assert str(Move('a1', 'b4', target2='c4')) == 'a1^b4c4'
    assert str(Move('a1', 'b4', source2='b1')) == 'a1b1^b4'
    assert str(Move('a1', 'b4', measurement=1)) == 'a1b4.m1'
    assert str(Move('a1', 'b4', source2='b1', measurement=0)) == 'a1b1^b4.m0'
    assert Move('c8',
                'g4',
                target2='f5',
                move_type=enums.MoveType.SPLIT_SLIDE,
                move_variant=enums.MoveVariant.BASIC,
                measurement=0).to_string(
                    include_type=True) == 'c8^g4f5.m0:SPLIT_SLIDE:BASIC'
Example #5
0
def test_to_string():
    assert str(Move("a1", "b4")) == "a1b4"
    assert str(Move("a1", "b4", target2="c4")) == "a1^b4c4"
    assert str(Move("a1", "b4", source2="b1")) == "a1b1^b4"
    assert str(Move("a1", "b4", measurement=1)) == "a1b4.m1"
    assert str(Move("a1", "b4", source2="b1", measurement=0)) == "a1b1^b4.m0"
    assert (Move(
        "c8",
        "g4",
        target2="f5",
        move_type=enums.MoveType.SPLIT_SLIDE,
        move_variant=enums.MoveVariant.BASIC,
        measurement=0,
    ).to_string(include_type=True) == "c8^g4f5.m0:SPLIT_SLIDE:BASIC")
Example #6
0
def test_move_round_trip():
    moves = (
        Move(
            "a1",
            "b4",
            move_type=enums.MoveType.JUMP,
            move_variant=enums.MoveVariant.BASIC,
        ),
        Move(
            "a1",
            "b4",
            move_type=enums.MoveType.JUMP,
            move_variant=enums.MoveVariant.BASIC,
            measurement=0,
        ),
        Move(
            "a1",
            "b4",
            move_type=enums.MoveType.JUMP,
            move_variant=enums.MoveVariant.BASIC,
            measurement=1,
        ),
        Move(
            "a1",
            "b4",
            move_type=enums.MoveType.JUMP,
            move_variant=enums.MoveVariant.BASIC,
            measurement=1,
        ),
        Move(
            "c8",
            "g4",
            target2="f5",
            move_type=enums.MoveType.SPLIT_SLIDE,
            move_variant=enums.MoveVariant.BASIC,
            measurement=0,
        ),
    )
    for m in moves:
        assert m == Move.from_string(m.to_string(include_type=True))
Example #7
0
def test_from_string():
    assert Move.from_string('a1b4:JUMP:BASIC') == Move(
        'a1',
        'b4',
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC)
    assert Move.from_string('a1b4.m0:JUMP:BASIC') == Move(
        'a1',
        'b4',
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=0)
    assert Move.from_string('a1b4.m1:JUMP:BASIC') == Move(
        'a1',
        'b4',
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=1)
    assert Move.from_string('a1b4.m1:JUMP:BASIC') == Move(
        'a1',
        'b4',
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=1)
    assert Move.from_string('c8^g4f5.m0:SPLIT_SLIDE:BASIC') == Move(
        'c8',
        'g4',
        target2='f5',
        move_type=enums.MoveType.SPLIT_SLIDE,
        move_variant=enums.MoveVariant.BASIC,
        measurement=0)

    assert Move.from_string('a1^a4d1:SPLIT_SLIDE:BASIC') == Move(
        'a1',
        'a4',
        target2='d1',
        move_type=enums.MoveType.SPLIT_SLIDE,
        move_variant=enums.MoveVariant.BASIC)
    assert Move.from_string('c3^c4c5:SPLIT_JUMP:BASIC') == Move(
        'c3',
        'c4',
        target2='c5',
        move_type=enums.MoveType.SPLIT_JUMP,
        move_variant=enums.MoveVariant.BASIC)
    assert Move.from_string('a4d4^d1:MERGE_SLIDE:BASIC') == Move(
        'a4',
        'd1',
        source2='d4',
        move_type=enums.MoveType.MERGE_SLIDE,
        move_variant=enums.MoveVariant.BASIC)
Example #8
0
def test_equality():
    assert Move('a1', 'b4') == Move('a1', 'b4')
    assert (Move(source='g3',
                 source2='c3',
                 target='e4',
                 move_type=enums.MoveType.MERGE_JUMP) == Move(
                     source='g3',
                     source2='c3',
                     target='e4',
                     move_type=enums.MoveType.MERGE_JUMP))
    assert (Move(source='g3',
                 source2='c3',
                 target='e4',
                 move_type=enums.MoveType.MERGE_JUMP) != Move(
                     source='g3',
                     source2='d6',
                     target='e4',
                     move_type=enums.MoveType.MERGE_JUMP))
    assert Move('a1', 'b4') != Move('a1', 'b5')
    assert Move('a1', 'b4') != "a1b4"

    assert Move('a1', 'b4', measurement=1) == Move('a1', 'b4', measurement=1)
    assert Move('a1', 'b4', measurement=1) != Move('a1', 'b4', measurement=0)
    assert Move('a1', 'b4', measurement=1) != Move('a1', 'b4')

    assert Move('a1', 'b4', move_type=enums.MoveType.JUMP) == Move(
        'a1', 'b4', move_type=enums.MoveType.JUMP)
    assert Move('a1', 'b4', move_type=enums.MoveType.JUMP) != Move(
        'a1', 'b4', move_type=enums.MoveType.SLIDE)
Example #9
0
def test_from_string():
    assert Move.from_string("a1b4:JUMP:BASIC") == Move(
        "a1",
        "b4",
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC)
    assert Move.from_string("a1b4.m0:JUMP:BASIC") == Move(
        "a1",
        "b4",
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=0,
    )
    assert Move.from_string("a1b4.m1:JUMP:BASIC") == Move(
        "a1",
        "b4",
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=1,
    )
    assert Move.from_string("a1b4.m1:JUMP:BASIC") == Move(
        "a1",
        "b4",
        move_type=enums.MoveType.JUMP,
        move_variant=enums.MoveVariant.BASIC,
        measurement=1,
    )
    assert Move.from_string("c8^g4f5.m0:SPLIT_SLIDE:BASIC") == Move(
        "c8",
        "g4",
        target2="f5",
        move_type=enums.MoveType.SPLIT_SLIDE,
        move_variant=enums.MoveVariant.BASIC,
        measurement=0,
    )

    assert Move.from_string("a1^a4d1:SPLIT_SLIDE:BASIC") == Move(
        "a1",
        "a4",
        target2="d1",
        move_type=enums.MoveType.SPLIT_SLIDE,
        move_variant=enums.MoveVariant.BASIC,
    )
    assert Move.from_string("c3^c4c5:SPLIT_JUMP:BASIC") == Move(
        "c3",
        "c4",
        target2="c5",
        move_type=enums.MoveType.SPLIT_JUMP,
        move_variant=enums.MoveVariant.BASIC,
    )
    assert Move.from_string("a4d4^d1:MERGE_SLIDE:BASIC") == Move(
        "a4",
        "d1",
        source2="d4",
        move_type=enums.MoveType.MERGE_SLIDE,
        move_variant=enums.MoveVariant.BASIC,
    )
Example #10
0
def test_equality():
    assert Move("a1", "b4") == Move("a1", "b4")
    assert Move(source="g3",
                source2="c3",
                target="e4",
                move_type=enums.MoveType.MERGE_JUMP) == Move(
                    source="g3",
                    source2="c3",
                    target="e4",
                    move_type=enums.MoveType.MERGE_JUMP)
    assert Move(source="g3",
                source2="c3",
                target="e4",
                move_type=enums.MoveType.MERGE_JUMP) != Move(
                    source="g3",
                    source2="d6",
                    target="e4",
                    move_type=enums.MoveType.MERGE_JUMP)
    assert Move("a1", "b4") != Move("a1", "b5")
    assert Move("a1", "b4") != "a1b4"

    assert Move("a1", "b4", measurement=1) == Move("a1", "b4", measurement=1)
    assert Move("a1", "b4", measurement=1) != Move("a1", "b4", measurement=0)
    assert Move("a1", "b4", measurement=1) != Move("a1", "b4")

    assert Move("a1", "b4", move_type=enums.MoveType.JUMP) == Move(
        "a1", "b4", move_type=enums.MoveType.JUMP)
    assert Move("a1", "b4", move_type=enums.MoveType.JUMP) != Move(
        "a1", "b4", move_type=enums.MoveType.SLIDE)
Example #11
0
    def do_move(self, m: move.Move) -> int:
        """Performs a move on the quantum board.

        Based on the type and variant of the move requested,
        this function augments the circuit, classical registers,
        and post-selection criteria to perform the board.

        Returns:  The measurement that was performed, or 1 if
            no measurement was required.
        """
        if not m.move_type:
            raise ValueError('No Move defined')
        if m.move_type == enums.MoveType.NULL_TYPE:
            raise ValueError('Move has null type')
        if m.move_type == enums.MoveType.UNSPECIFIED_STANDARD:
            raise ValueError('Move type is unspecified')

        # Reset accumulations here because function has conditional return branches
        self.accumulations_repetitions = None

        # Add move to the move move_history
        self.move_history.append(m)

        sbit = square_to_bit(m.source)
        tbit = square_to_bit(m.target)
        squbit = bit_to_qubit(sbit)
        tqubit = bit_to_qubit(tbit)

        if (m.move_variant == enums.MoveVariant.CAPTURE
                or m.move_type == enums.MoveType.PAWN_EP
                or m.move_type == enums.MoveType.PAWN_CAPTURE):
            # TODO: figure out if it is a deterministic capture.
            for val in list(self.allowed_pieces):
                self.allowed_pieces.add(val - 1)

        if m.move_type == enums.MoveType.PAWN_EP:
            # For en passant, first determine the square of the pawn being
            # captured, which should be next to the target.
            if m.target[1] == '6':
                epbit = square_to_bit(m.target[0] + '5')
            elif m.target[1] == '2':
                epbit = square_to_bit(m.target[0] + '4')
            else:
                raise ValueError(f'Invalid en passant target {m.target}')
            epqubit = bit_to_qubit(epbit)

            # For the classical version, set the bits appropriately
            if (epqubit not in self.entangled_squares
                    and squbit not in self.entangled_squares
                    and tqubit not in self.entangled_squares):
                if (not nth_bit_of(epbit, self.state)
                        or not nth_bit_of(sbit, self.state)
                        or nth_bit_of(tbit, self.state)):
                    raise ValueError('Invalid classical e.p. move')

                self.state = set_nth_bit(epbit, self.state, False)
                self.state = set_nth_bit(sbit, self.state, False)
                self.state = set_nth_bit(tbit, self.state, True)
                return 1

            # If any squares are quantum, it's a quantum move
            self.add_entangled(squbit, tqubit, epqubit)

            # Capture e.p. post-select on the source
            if m.move_variant == enums.MoveVariant.CAPTURE:
                is_there = self.post_select_on(squbit)
                if not is_there:
                    return 0
                self.add_entangled(squbit)
                path_ancilla = self.new_ancilla()
                captured_ancilla = self.new_ancilla()
                captured_ancilla2 = self.new_ancilla()
                # capture e.p. has a special circuit
                self.circuit.append(
                    qm.capture_ep(squbit, tqubit, epqubit, self.new_ancilla(),
                                  self.new_ancilla(), self.new_ancilla()))
                return 1

            # Blocked/excluded e.p. post-select on the target
            if m.move_variant == enums.MoveVariant.EXCLUDED:
                is_there = self.post_select_on(tqubit)
                if is_there:
                    return 0
                self.add_entangled(tqubit)
            self.circuit.append(
                qm.en_passant(squbit, tqubit, epqubit, self.new_ancilla(),
                              self.new_ancilla()))
            return 1

        if m.move_type == enums.MoveType.PAWN_CAPTURE:
            # For pawn capture, first measure source.
            is_there = self.post_select_on(squbit)
            if not is_there:
                return 0
            if tqubit in self.entangled_squares:
                old_tqubit = self.unhook(tqubit)
                self.add_entangled(squbit, tqubit)

                self.circuit.append(
                    qm.controlled_operation(cirq.ISWAP, [squbit, tqubit],
                                            [old_tqubit], []))
            else:
                # Classical case
                self.state = set_nth_bit(sbit, self.state, False)
                self.state = set_nth_bit(tbit, self.state, True)
            return 1

        if m.move_type == enums.MoveType.SPLIT_SLIDE:
            tbit2 = square_to_bit(m.target2)
            tqubit2 = bit_to_qubit(tbit2)

            # Find all the squares on both paths
            path_qubits = self.path_qubits(m.source, m.target)
            path_qubits2 = self.path_qubits(m.source, m.target2)

            if len(path_qubits) == 0 and len(path_qubits2) == 0:
                # No interposing squares, just jump.
                m.move_type = enums.MoveType.SPLIT_JUMP
            else:
                self.add_entangled(squbit, tqubit, tqubit2)
                path1 = self.create_path_ancilla(path_qubits)
                path2 = self.create_path_ancilla(path_qubits2)
                ancilla = self.new_ancilla()
                self.circuit.append(
                    qm.split_slide(squbit, tqubit, tqubit2, path1, path2,
                                   ancilla))
                return 1

        if m.move_type == enums.MoveType.MERGE_SLIDE:
            sbit2 = square_to_bit(m.source2)
            squbit2 = bit_to_qubit(sbit2)
            self.add_entangled(squbit, squbit2, tqubit)

            # Find all the squares on both paths
            path_qubits = self.path_qubits(m.source, m.target)
            path_qubits2 = self.path_qubits(m.source2, m.target)
            if len(path_qubits) == 0 and len(path_qubits2) == 0:
                # No interposing squares, just jump.
                m.move_type = enums.MoveType.MERGE_JUMP
            else:
                path1 = self.create_path_ancilla(path_qubits)
                path2 = self.create_path_ancilla(path_qubits2)
                ancilla = self.new_ancilla()
                self.circuit.append(
                    qm.merge_slide(squbit, tqubit, squbit2, path1, path2,
                                   ancilla))
                return 1

        if (m.move_type == enums.MoveType.SLIDE
                or m.move_type == enums.MoveType.PAWN_TWO_STEP):
            path_qubits = self.path_qubits(m.source, m.target)
            if len(path_qubits) == 0:
                # No path, change to jump
                m.move_type = enums.MoveType.JUMP

        if (m.move_type == enums.MoveType.SLIDE
                or m.move_type == enums.MoveType.PAWN_TWO_STEP):
            for p in path_qubits:
                if (p not in self.entangled_squares
                        and nth_bit_of(qubit_to_bit(p), self.state)):
                    # Classical piece in the way
                    return 0

            # For excluded case, measure target
            if m.move_variant == enums.MoveVariant.EXCLUDED:
                is_there = self.post_select_on(tqubit)
                if is_there:
                    return 0

            self.add_entangled(squbit, tqubit)
            if m.move_variant == enums.MoveVariant.CAPTURE:
                capture_ancilla = self.new_ancilla()
                self.circuit.append(
                    qm.controlled_operation(cirq.X, [capture_ancilla],
                                            [squbit], path_qubits))

                # We need to add the captured_ancilla to entangled squares
                # So that we measure it
                self.entangled_squares.add(capture_ancilla)
                capture_allowed = self.post_select_on(capture_ancilla)

                if not capture_allowed:
                    return 0
                else:
                    # Perform the captured slide
                    self.add_entangled(squbit)
                    # Remove the target from the board into an ancilla
                    # and set bit to zero
                    self.unhook(tqubit)
                    self.state = set_nth_bit(tbit, self.state, False)

                    # Re-add target since we need to swap into the square
                    self.add_entangled(tqubit)

                    # Perform the actual move
                    self.circuit.append(qm.normal_move(squbit, tqubit))

                    # Set source to empty
                    self.unhook(squbit)
                    self.state = set_nth_bit(sbit, self.state, False)

                    # Now set the whole path to empty
                    for p in path_qubits:
                        self.state = set_nth_bit(qubit_to_bit(p), self.state,
                                                 False)
                        self.unhook(p)
                    return 1
            # Basic slide (or successful excluded slide)

            # Add all involved squares into entanglement
            self.add_entangled(squbit, tqubit, *path_qubits)

            if len(path_qubits) == 1:
                # For path of one, no ancilla needed
                self.circuit.append(qm.slide_move(squbit, tqubit, path_qubits))
                return 1
            # Longer paths require a path ancilla
            ancilla = self.new_ancilla()
            self.circuit.append(
                qm.slide_move(squbit, tqubit, path_qubits, ancilla))
            return 1

        if (m.move_type == enums.MoveType.JUMP
                or m.move_type == enums.MoveType.PAWN_STEP):
            if (squbit not in self.entangled_squares
                    and tqubit not in self.entangled_squares):
                # Classical version
                self.state = set_nth_bit(sbit, self.state, False)
                self.state = set_nth_bit(tbit, self.state, True)
                return 1

            # Measure source for capture
            if m.move_variant == enums.MoveVariant.CAPTURE:
                is_there = self.post_select_on(squbit)
                if not is_there:
                    return 0
                self.unhook(tqubit)

            # Measure target for excluded
            if m.move_variant == enums.MoveVariant.EXCLUDED:
                is_there = self.post_select_on(tqubit)
                if is_there:
                    return 0

            # Only convert source qubit to ancilla if target
            # is empty
            unhook = tqubit not in self.entangled_squares
            self.add_entangled(squbit, tqubit)

            # Execute jump
            self.circuit.append(qm.normal_move(squbit, tqubit))

            if unhook or m.move_variant != enums.MoveVariant.BASIC:
                # The source is empty.
                # Change source qubit to be an ancilla
                # and set classical bit to zero
                self.state = set_nth_bit(sbit, self.state, False)
                self.unhook(squbit)

            return 1

        if m.move_type == enums.MoveType.SPLIT_JUMP:
            tbit2 = square_to_bit(m.target2)
            tqubit2 = bit_to_qubit(tbit2)
            self.add_entangled(squbit, tqubit, tqubit2)
            self.circuit.append(qm.split_move(squbit, tqubit, tqubit2))
            self.state = set_nth_bit(sbit, self.state, False)
            self.unhook(squbit)
            return 1

        if m.move_type == enums.MoveType.MERGE_JUMP:
            sbit2 = square_to_bit(m.source2)
            squbit2 = bit_to_qubit(sbit2)
            self.add_entangled(squbit, squbit2, tqubit)
            self.circuit.append(qm.merge_move(squbit, squbit2, tqubit))
            # TODO: should the source qubit be 'unhooked'?
            return 1

        if m.move_type == enums.MoveType.KS_CASTLE:
            # Figure out the rook squares
            if sbit == square_to_bit('e1') and tbit == square_to_bit('g1'):
                rook_sbit = square_to_bit('h1')
                rook_tbit = square_to_bit('f1')
            elif sbit == square_to_bit('e8') and tbit == square_to_bit('g8'):
                rook_sbit = square_to_bit('h8')
                rook_tbit = square_to_bit('f8')
            else:
                raise ValueError(f'Invalid kingside castling move')
            rook_squbit = bit_to_qubit(rook_sbit)
            rook_tqubit = bit_to_qubit(rook_tbit)

            # Piece in non-superposition in the way, not legal
            if (nth_bit_of(rook_tbit, self.state)
                    and rook_tqubit not in self.entangled_squares):
                return 0
            if (nth_bit_of(tbit, self.state)
                    and tqubit not in self.entangled_squares):
                return 0

            # Not in superposition, just castle
            if (rook_tqubit not in self.entangled_squares
                    and tqubit not in self.entangled_squares):
                self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
                return 1

            # Both intervening squares in superposition
            if (rook_tqubit in self.entangled_squares
                    and tqubit in self.entangled_squares):
                castle_ancilla = self.create_path_ancilla(
                    [rook_tqubit, tqubit])
                self.entangled_squares.add(castle_ancilla)
                castle_allowed = self.post_select_on(castle_ancilla)
                if castle_allowed:
                    self.unhook(rook_tqubit)
                    self.unhook(tqubit)
                    self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
                    return 1
                else:
                    self.post_selection[castle_ancilla] = castle_allowed
                    return 0

            # One intervening square in superposition
            if rook_tqubit in self.entangled_squares:
                measure_qubit = rook_tqubit
                measure_bit = rook_tbit
            else:
                measure_qubit = tqubit
                measure_bit = tbit
            is_there = self.post_select_on(measure_qubit)
            if is_there:
                return 0
            self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
            return 1

        if m.move_type == enums.MoveType.QS_CASTLE:

            # Figure out the rook squares and the b-file square involved
            if sbit == square_to_bit('e1') and tbit == square_to_bit('c1'):
                rook_sbit = square_to_bit('a1')
                rook_tbit = square_to_bit('d1')
                b_bit = square_to_bit('b1')
            elif sbit == square_to_bit('e8') and tbit == square_to_bit('c8'):
                rook_sbit = square_to_bit('a8')
                rook_tbit = square_to_bit('d8')
                b_bit = square_to_bit('b8')
            else:
                raise ValueError(f'Invalid queenside castling move')
            rook_squbit = bit_to_qubit(rook_sbit)
            rook_tqubit = bit_to_qubit(rook_tbit)
            b_qubit = bit_to_qubit(b_bit)

            # Piece in non-superposition in the way, not legal
            if (nth_bit_of(rook_tbit, self.state)
                    and rook_tqubit not in self.entangled_squares):
                return 0
            if (nth_bit_of(tbit, self.state)
                    and tqubit not in self.entangled_squares):
                return 0
            if (b_bit is not None and nth_bit_of(b_bit, self.state)
                    and b_qubit not in self.entangled_squares):
                return 0

            # Not in superposition, just castle
            if (rook_tqubit not in self.entangled_squares
                    and tqubit not in self.entangled_squares
                    and b_qubit not in self.entangled_squares):
                self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
                return 1

            # Neither intervening squares in superposition
            if (rook_tqubit not in self.entangled_squares
                    and tqubit not in self.entangled_squares):
                if b_qubit not in self.entangled_squares:
                    self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
                else:
                    self.queenside_castle(squbit, rook_squbit, tqubit,
                                          rook_tqubit, b_qubit)
                return 1

            # Both intervening squares in superposition
            if (rook_tqubit in self.entangled_squares
                    and tqubit in self.entangled_squares):
                castle_ancilla = self.create_path_ancilla(
                    [rook_tqubit, tqubit])
                self.entangled_squares.add(castle_ancilla)
                castle_allowed = self.post_select_on(castle_ancilla)
                if castle_allowed:
                    self.unhook(rook_tqubit)
                    self.unhook(tqubit)
                    if b_qubit not in self.entangled_squares:
                        self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
                    else:
                        self.queenside_castle(squbit, rook_squbit, tqubit,
                                              rook_tqubit, b_qubit)
                    return 1
                else:
                    self.post_selection[castle_ancilla] = castle_allowed
                    return 0

            # One intervening square in superposition
            if rook_tqubit in self.entangled_squares:
                measure_qubit = rook_tqubit
                measure_bit = rook_tbit
            else:
                measure_qubit = tqubit
                measure_bit = tbit
            is_there = self.post_select_on(measure_qubit)
            if is_there:
                return 0
            if b_qubit not in self.entangled_squares:
                self.set_castle(sbit, rook_sbit, tbit, rook_tbit)
            else:
                self.queenside_castle(squbit, rook_squbit, tqubit, rook_tqubit,
                                      b_qubit)
            return 1

        raise ValueError(f'Move type {m.move_type} not supported')
Example #12
0
def test_equality():
    assert Move('a1', 'b4') == Move('a1', 'b4')
    assert Move('a1', 'b4') != Move('a1', 'b5')
    assert Move('a1', 'b4') != "a1b4"

    assert Move('a1', 'b4', measurement=1) == Move('a1', 'b4', measurement=1)
    assert Move('a1', 'b4', measurement=1) != Move('a1', 'b4', measurement=0)
    assert Move('a1', 'b4', measurement=1) != Move('a1', 'b4')

    assert Move('a1', 'b4', move_type=enums.MoveType.JUMP) == Move(
        'a1', 'b4', move_type=enums.MoveType.JUMP)
    assert Move('a1', 'b4', move_type=enums.MoveType.JUMP) != Move(
        'a1', 'b4', move_type=enums.MoveType.SLIDE)