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
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)
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))
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'
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")
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))
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)
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)
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, )
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)
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')
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)