diff --git a/unitary/examples/quantum_chinese_chess/board.py b/unitary/examples/quantum_chinese_chess/board.py index 109801ea..ce77b851 100644 --- a/unitary/examples/quantum_chinese_chess/board.py +++ b/unitary/examples/quantum_chinese_chess/board.py @@ -20,6 +20,7 @@ Type, Language, MoveVariant, + TerminalType, ) from unitary.examples.quantum_chinese_chess.piece import Piece from unitary.examples.quantum_chinese_chess.move import Jump @@ -28,6 +29,20 @@ # The default initial state of the game. _INITIAL_FEN = "RHEAKAEHR/9/1C5C1/P1P1P1P1P/9/9/p1p1p1p1p/1c5c1/9/rheakaehr w---1" +# Constants for printing board +_RESET = "\033[0m" +_BOLD = "\033[01m" +# background +_BG_GREY = "\033[47m" +# foreground +_FG_BLACK = "\033[30m" +_FG_RED = "\033[31m" +_FG_LIGHT_RED = "\033[91m" +_FG_LIGHT_GREY = "\033[37m" +# full width chars +_FULL_SPACE = "\N{IDEOGRAPHIC SPACE}" +_FULL_A = ord("\N{FULLWIDTH LATIN SMALL LETTER A}") + class Board: """Board holds the assemble of all pieces. Each piece could be either in classical or quantum state.""" @@ -41,7 +56,7 @@ def __init__( self.king_locations = king_locations self.lang = Language.EN # The default language is English. - def set_language(self, lang: Language): + def set_language(self, lang: Language) -> None: self.lang = lang @classmethod @@ -83,47 +98,159 @@ def from_fen(cls, fen: str = _INITIAL_FEN) -> "Board": # TODO(): maybe add check to make sure the input fen itself is correct. return cls(board, current_player, king_locations) - def __str__(self): - num_rows = 10 - board_string = ["\n "] - # Print the top line of col letters. - for col in "abcdefghi": - board_string.append(f" {col}") - board_string.append("\n") - for row in range(num_rows - 1, -1, -1): - # Print the row index on the left. - board_string.append(f"{row} ") - for col in "abcdefghi": - piece = self.board[f"{col}{row}"] + # TODO(): print players' names in their corresponding side of the board. + # TODO(): check if there is better way to automatic determine the current terminal + # type, e.g. colab / sublime terminus vs glinux / mac terminal. + # TODO(): right now all possibilities are printed in black, maybe update to print + # in the same color as the corresponding pieces. + # TODO(): in some scenarios the black entangled pieces seems too light/weak to see. + def to_str( + self, + terminal: TerminalType, + probabilities: List[float] = None, + peek_result: List[int] = None, + ) -> str: + """ + Print the board into string. + + Args: + terminal: type of the terminal that the game is currently running on; + probabilities: the probabilities of each piece of the board, in length of 90; + peek_result: for one peek of the board, provide a list of ints with length 90, + with int=1 indicating the piece is there (for this peek); + """ + + def add_piece_symbol( + board_string: str, + piece: Piece, + peek_result: List[int], + index: int, + terminal: TerminalType, + ): + if peek_result is None and piece.is_entangled: + if piece.color == Color.RED: + board_string += _FG_LIGHT_RED + else: + # bold works on mac terminal and gLinux terminal, + # but not on sublime terminus + if terminal != TerminalType.COLAB_OR_SUBLIME_TERMINUS: + board_string += _BOLD + board_string += _FG_LIGHT_GREY + else: + if terminal != TerminalType.COLAB_OR_SUBLIME_TERMINUS: + board_string += _BOLD + if piece.color == Color.RED: + board_string += _FG_RED + if ( + peek_result is None + or piece.type_ == Type.EMPTY + or peek_result[index] == 1 + ): board_string += piece.symbol(self.lang) - if self.lang == Language.EN: - board_string.append(" ") - # Print the row index on the right. - board_string.append(f" {row}\n") - board_string.append(" ") - # Print the bottom line of col letters. - for col in "abcdefghi": - board_string.append(f" {col}") - board_string.append("\n") + # If an entangled piece is peeked to be empty, we print empty. + elif piece.is_entangled and peek_result[index] == 0: + board_string += Type.symbol(Type.EMPTY, Color.NA, self.lang) + board_string += _RESET + + num_rows = 10 + if self.lang == Language.EN: + board_string = ["\n "] + # Print the top line of col letters. + board_string += _BG_GREY + board_string += _FG_BLACK + for col in "abcdefghi": + board_string.append(f" {col} ") + board_string += "\b" + _RESET + " \n" + index = 0 + for row in range(num_rows): + # Print the row index on the left. + board_string.append(f"{row} ") + # Print each piece of this row, including empty piece. + for col in "abcdefghi": + piece = self.board[f"{col}{row}"] + add_piece_symbol(board_string, piece, peek_result, index, terminal) + if col != "i": + board_string.append(" ") + board_string += _RESET + index += 1 + # Print the row index on the right. + board_string += f" {row}" + _RESET + "\n" + # Print the sampled prob. of the pieces in the above row. + if probabilities is not None: + board_string += " " + board_string += _BG_GREY + board_string += _FG_BLACK + for i in range(row * 9, (row + 1) * 9): + # We only print non-zero probabilities + if probabilities[i] >= 1e-3: + board_string.append("{:.1f} ".format(probabilities[i])) + else: + board_string.append(" ") + board_string += "\b" + _RESET + " \n" + board_string.append(" ") + # Print the bottom line of col letters. + board_string += _BG_GREY + board_string += _FG_BLACK + for col in "abcdefghi": + board_string.append(f" {col} ") + board_string += "\b" + _RESET + " \n" + return "".join(board_string) + else: # Print Chinese + full width characters + board_string = ["\n" + _FULL_SPACE + " "] + # Print the top line of col letters. + board_string += _BG_GREY + board_string += _FG_BLACK + for col in range(_FULL_A, _FULL_A + 9): + board_string.append(f"{chr(col)}") + if col != _FULL_A + 8: + board_string += _FULL_SPACE * 2 + board_string += " \n" + _RESET + index = 0 + for row in range(num_rows): + # Print the row index on the left. + board_string.append(f"{row}" + _FULL_SPACE) + # Print each piece of this row, including empty piece. + for col in "abcdefghi": + piece = self.board[f"{col}{row}"] + add_piece_symbol(board_string, piece, peek_result, index, terminal) + if col != "i": + board_string.append(_FULL_SPACE * 2) + index += 1 + # Print the row index on the right. + board_string += _FULL_SPACE * 2 + f"{row}\n" + # Print the sampled prob. of the pieces in the above row. + if probabilities is not None: + board_string += _FULL_SPACE + " " + board_string += _BG_GREY + board_string += _FG_BLACK + for i in range(row * 9, (row + 1) * 9): + # We only print non-zero probabilities + if terminal != TerminalType.COLAB_OR_SUBLIME_TERMINUS: + # space + _FULL_SPACE works for mac terminal and gLinux terminal. + if probabilities[i] >= 1e-3: + board_string.append( + "{:.1f} ".format(probabilities[i]) + _FULL_SPACE + ) + else: + board_string.append(" " + _FULL_SPACE) + else: + if probabilities[i] >= 1e-3: + # space + space works for sublime terminus. + board_string.append("{:.1f} ".format(probabilities[i])) + else: + board_string.append(" ") + board_string += "\b" + _RESET + _FULL_SPACE + "\n" + # Print the bottom line of col letters. + board_string.append(_FULL_SPACE + " ") + board_string += _BG_GREY + board_string += _FG_BLACK + for col in range(_FULL_A, _FULL_A + 9): + board_string.append(f"{chr(col)}") + if col != _FULL_A + 8: + board_string += _FULL_SPACE * 2 + board_string += " " + _RESET + "\n" return "".join(board_string) - # We need to turn letters into their full-width counterparts to align - # a mix of letters + Chinese characters. - chars = "".join(chr(c) for c in range(ord(" "), ord("z"))) - full_width_chars = "\N{IDEOGRAPHIC SPACE}" + "".join( - chr(c) - for c in range( - ord("\N{FULLWIDTH EXCLAMATION MARK}"), - ord("\N{FULLWIDTH LATIN SMALL LETTER Z}"), - ) - ) - translation = str.maketrans(chars, full_width_chars) - return ( - "".join(board_string) - .replace(" ", "") - .replace("abcdefghi", " abcdefghi") - .translate(translation) - ) def path_pieces(self, source: str, target: str) -> Tuple[List[str], List[str]]: """Returns the nonempty classical and quantum pieces from source to target (excluded).""" diff --git a/unitary/examples/quantum_chinese_chess/board_test.py b/unitary/examples/quantum_chinese_chess/board_test.py index a8812228..20b64f13 100644 --- a/unitary/examples/quantum_chinese_chess/board_test.py +++ b/unitary/examples/quantum_chinese_chess/board_test.py @@ -17,6 +17,7 @@ Color, Type, SquareState, + TerminalType, ) from unitary.examples.quantum_chinese_chess.board import Board from unitary.examples.quantum_chinese_chess.piece import Piece @@ -28,90 +29,106 @@ set_board, ) from unitary import alpha +import re def test_init_with_default_fen(): board = Board.from_fen() + + # test English print assert ( - board.__str__() + re.sub("\\033\[\d{1,2}m", "", board.to_str(TerminalType.MAC_OR_LINUX)).replace( + "\b", "" + ) == """ - a b c d e f g h i -9 r h e a k a e h r 9 -8 . . . . . . . . . 8 -7 . c . . . . . c . 7 -6 p . p . p . p . p 6 -5 . . . . . . . . . 5 -4 . . . . . . . . . 4 -3 P . P . P . P . P 3 -2 . C . . . . . C . 2 -1 . . . . . . . . . 1 -0 R H E A K A E H R 0 - a b c d e f g h i + a b c d e f g h i +0 R H E A K A E H R 0 +1 · · · · · · · · · 1 +2 · C · · · · · C · 2 +3 P · P · P · P · P 3 +4 · · · · · · · · · 4 +5 · · · · · · · · · 5 +6 p · p · p · p · p 6 +7 · c · · · · · c · 7 +8 · · · · · · · · · 8 +9 r h e a k a e h r 9 + a b c d e f g h i """ ) + # test Chinese print board.set_language(Language.ZH) assert ( - board.__str__() + re.sub("\\033\[\d{1,2}m", "", board.to_str(TerminalType.MAC_OR_LINUX)).replace( + "\b", "" + ) == """ - abcdefghi -9車馬相仕帥仕相馬車9 -8.........8 -7.砲.....砲.7 -6卒.卒.卒.卒.卒6 -5.........5 -4.........4 -3兵.兵.兵.兵.兵3 -2.炮.....炮.2 -1.........1 -0车马象士将士象马车0 - abcdefghi +  a  b  c  d  e  f  g  h  i +0 车  马  象  士  将  士  象  马  车  0 +1 ・  ・  ・  ・  ・  ・  ・  ・  ・  1 +2 ・  炮  ・  ・  ・  ・  ・  炮  ・  2 +3 兵  ・  兵  ・  兵  ・  兵  ・  兵  3 +4 ・  ・  ・  ・  ・  ・  ・  ・  ・  4 +5 ・  ・  ・  ・  ・  ・  ・  ・  ・  5 +6 卒  ・  卒  ・  卒  ・  卒  ・  卒  6 +7 ・  砲  ・  ・  ・  ・  ・  砲  ・  7 +8 ・  ・  ・  ・  ・  ・  ・  ・  ・  8 +9 車  馬  相  仕  帥  仕  相  馬  車  9 +  a  b  c  d  e  f  g  h  i """ ) + # check locations of KINGs assert board.king_locations == ["e0", "e9"] def test_init_with_specified_fen(): board = Board.from_fen("4kaR2/4a4/3hR4/7H1/9/9/9/9/4Ap1r1/3AK3c w---1 ") + # test English print assert ( - board.__str__() + re.sub("\\033\[\d{1,2}m", "", board.to_str(TerminalType.MAC_OR_LINUX)).replace( + "\b", "" + ) == """ - a b c d e f g h i -9 . . . A K . . . c 9 -8 . . . . A p . r . 8 -7 . . . . . . . . . 7 -6 . . . . . . . . . 6 -5 . . . . . . . . . 5 -4 . . . . . . . . . 4 -3 . . . . . . . H . 3 -2 . . . h R . . . . 2 -1 . . . . a . . . . 1 -0 . . . . k a R . . 0 - a b c d e f g h i + a b c d e f g h i +0 · · · · k a R · · 0 +1 · · · · a · · · · 1 +2 · · · h R · · · · 2 +3 · · · · · · · H · 3 +4 · · · · · · · · · 4 +5 · · · · · · · · · 5 +6 · · · · · · · · · 6 +7 · · · · · · · · · 7 +8 · · · · A p · r · 8 +9 · · · A K · · · c 9 + a b c d e f g h i """ ) + # test Chinese print board.set_language(Language.ZH) assert ( - board.__str__() + re.sub("\\033\[\d{1,2}m", "", board.to_str(TerminalType.MAC_OR_LINUX)).replace( + "\b", "" + ) == """ - abcdefghi -9...士将...砲9 -8....士卒.車.8 -7.........7 -6.........6 -5.........5 -4.........4 -3.......马.3 -2...馬车....2 -1....仕....1 -0....帥仕车..0 - abcdefghi +  a  b  c  d  e  f  g  h  i +0 ・  ・  ・  ・  帥  仕  车  ・  ・  0 +1 ・  ・  ・  ・  仕  ・  ・  ・  ・  1 +2 ・  ・  ・  馬  车  ・  ・  ・  ・  2 +3 ・  ・  ・  ・  ・  ・  ・  马  ・  3 +4 ・  ・  ・  ・  ・  ・  ・  ・  ・  4 +5 ・  ・  ・  ・  ・  ・  ・  ・  ・  5 +6 ・  ・  ・  ・  ・  ・  ・  ・  ・  6 +7 ・  ・  ・  ・  ・  ・  ・  ・  ・  7 +8 ・  ・  ・  ・  士  卒  ・  車  ・  8 +9 ・  ・  ・  士  将  ・  ・  ・  砲  9 +  a  b  c  d  e  f  g  h  i """ ) + # check locations of KINGs assert board.king_locations == ["e0", "e9"] diff --git a/unitary/examples/quantum_chinese_chess/chess.py b/unitary/examples/quantum_chinese_chess/chess.py index 58cc2656..e0c35ebe 100644 --- a/unitary/examples/quantum_chinese_chess/chess.py +++ b/unitary/examples/quantum_chinese_chess/chess.py @@ -20,6 +20,7 @@ Color, MoveType, MoveVariant, + TerminalType, ) from unitary.examples.quantum_chinese_chess.move import ( Jump, @@ -32,15 +33,19 @@ ) import readline -# List of accepable commands. +# List of acceptable commands. _HELP_TEXT = """ Each location on the board is represented by two characters [abcdefghi][0-9], i.e. from a0 to i9. You may input (s=source, t=target) - s1t1 to do a slide move, e.g. "a1a4"; - s1^t1t2 to do a split move, e.g. "a1^b1a2"; - s1s2^t1 to do a merge move, e.g. "b1a2^a1"; + Other commands: - - "exit" to quit + - "peek": to peek (print a sample of) the current board state + - "peek all": to print all possible board states with associated probabilities + - "undo": to undo last move - "help": to see this message again + - "exit": to quit the game """ _WELCOME_MESSAGE = """ @@ -63,6 +68,13 @@ def print_welcome(self) -> None: self.lang = Language.ZH else: self.lang = Language.EN + terminal = input( + "Are you running this game on Colab or Sublime Terminus? (y/n) " + ) + if terminal == "y": + self.terminal = TerminalType.COLAB_OR_SUBLIME_TERMINUS + else: + self.terminal = TerminalType.MAC_OR_LINUX name_0 = input("Player 0's name (default to be Player_0): ") self.players_name.append("Player_0" if len(name_0) == 0 else name_0) name_1 = input("Player 1's name (default to be Player_1): ") @@ -73,7 +85,7 @@ def __init__(self): self.print_welcome() self.board = Board.from_fen() self.board.set_language(self.lang) - print(self.board) + print(self.board.to_str(self.terminal)) self.game_state = GameState.CONTINUES self.current_player = self.board.current_player self.debug_level = 3 @@ -442,13 +454,23 @@ def next_move(self) -> Tuple[bool, str]: self.game_state = GameState(1 - self.current_player) output = "Exiting." elif input_str.lower() == "peek": - # TODO(): make it look like the normal board. Right now it's only for debugging purposes. - print(self.board.board.peek(convert_to_enum=False)) + print( + self.board.to_str( + self.terminal, None, self.board.board.peek(convert_to_enum=False)[0] + ) + ) + elif input_str.lower() == "peek all": + all_boards = self.board.board.get_correlated_histogram() + sorted_boards = sorted(all_boards.items(), key=lambda x: x[1], reverse=True) + for board, count in sorted_boards: + print( + "\n ====== With probability ~ {:.1f} ======".format(count / 100.0) + ) + print(self.board.to_str(self.terminal, None, list(board))) elif input_str.lower() == "undo": if self.undo(): return True, "Undoing." return False, "Failed to undo." - else: try: # The move is success if no ValueError is raised. @@ -465,10 +487,6 @@ def update_board_by_sampling(self) -> List[float]: This method is called after each quantum move, and runs (100x) sampling of the board to identify and fix those cases. """ - # TODO(): return the sampled probabilities and pass it into the print method - # of the board to print it together with the board, or better use mathemetical - # matrix calculations to determine the probability, and use it (with some error - # threshold) to update the piece infos. probs = self.board.board.get_binary_probabilities() num_rows = 10 num_cols = 9 @@ -480,8 +498,11 @@ def update_board_by_sampling(self) -> List[float]: # Change it to be more meaningful values maybe when we do error mitigation. if prob < 1e-3: piece.reset() + probs[row * num_cols + ord(col) - ord("a")] = 0 elif prob > 1 - 1e-3: piece.is_entangled = False + probs[row * num_cols + ord(col) - ord("a")] = 1 + return probs def game_over(self) -> None: """Checks if the game is over, and update self.game_state accordingly.""" @@ -550,6 +571,7 @@ def undo(self) -> bool: def play(self) -> None: """The loop where each player takes turn to play.""" + probs = None while True: move_success, output = self.next_move() if not move_success: @@ -567,8 +589,9 @@ def play(self) -> None: probs = self.update_board_by_sampling() # Save the current states. self.save_snapshot() - # TODO(): pass probs into the following method to print probabilities. - print(self.board) + else: + probs = self.update_board_by_sampling() + print(self.board.to_str(self.terminal, probs)) if self.game_state == GameState.CONTINUES: # If the game continues, switch the player. self.current_player = 1 - self.current_player diff --git a/unitary/examples/quantum_chinese_chess/chess_test.py b/unitary/examples/quantum_chinese_chess/chess_test.py index e8adc19e..ba307107 100644 --- a/unitary/examples/quantum_chinese_chess/chess_test.py +++ b/unitary/examples/quantum_chinese_chess/chess_test.py @@ -30,16 +30,18 @@ SquareState, MoveType, MoveVariant, + TerminalType, ) def test_game_init(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) output = io.StringIO() sys.stdout = output game = QuantumChineseChess() assert game.lang == Language.ZH + assert game.terminal == TerminalType.MAC_OR_LINUX assert game.players_name == ["Bob", "Ben"] assert game.current_player == 0 assert "Welcome" in output.getvalue() @@ -47,7 +49,7 @@ def test_game_init(monkeypatch): def test_parse_input_string_success(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() assert game.parse_input_string("a1b1") == (["a1"], ["b1"]) @@ -56,7 +58,7 @@ def test_parse_input_string_success(monkeypatch): def test_parse_input_string_fail(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() with pytest.raises(ValueError, match="Invalid sources/targets string "): @@ -76,7 +78,7 @@ def test_parse_input_string_fail(monkeypatch): def test_apply_move_fail(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() with pytest.raises(ValueError, match="Could not move empty piece."): @@ -94,7 +96,7 @@ def test_apply_move_fail(monkeypatch): def test_game_invalid_move(monkeypatch): output = io.StringIO() sys.stdout = output - inputs = iter(["y", "Bob", "Ben", "a1n1", "exit"]) + inputs = iter(["y", "n", "Bob", "Ben", "a1n1", "exit"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() game.play() @@ -108,7 +110,7 @@ def test_game_invalid_move(monkeypatch): def test_check_classical_rule(monkeypatch): output = io.StringIO() sys.stdout = output - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = game.board.board @@ -207,7 +209,7 @@ def test_check_classical_rule(monkeypatch): def test_classify_move_fail(monkeypatch): output = io.StringIO() sys.stdout = output - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = game.board.board @@ -259,7 +261,7 @@ def test_classify_move_fail(monkeypatch): def test_classify_move_success(monkeypatch): output = io.StringIO() sys.stdout = output - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = game.board.board @@ -367,7 +369,7 @@ def test_classify_move_success(monkeypatch): def test_update_board_by_sampling(monkeypatch): output = io.StringIO() sys.stdout = output - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = game.board.board @@ -390,7 +392,7 @@ def test_update_board_by_sampling(monkeypatch): def test_undo_single_effect_per_move(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = set_board(["a1", "b1", "c1"]) @@ -431,7 +433,7 @@ def test_undo_single_effect_per_move(monkeypatch): def test_undo_multiple_effects_per_move(monkeypatch): - inputs = iter(["y", "Bob", "Ben"]) + inputs = iter(["y", "n", "Bob", "Ben"]) monkeypatch.setattr("builtins.input", lambda _: next(inputs)) game = QuantumChineseChess() board = set_board(["a1", "b1", "c1"]) diff --git a/unitary/examples/quantum_chinese_chess/enums.py b/unitary/examples/quantum_chinese_chess/enums.py index f017cec2..1f01c4a5 100644 --- a/unitary/examples/quantum_chinese_chess/enums.py +++ b/unitary/examples/quantum_chinese_chess/enums.py @@ -85,7 +85,6 @@ class Type(enum.Enum): - Chinese black """ - EMPTY = (".", ".", ".", ".") PAWN = ("P", "p", "兵", "卒") CANNON = ("C", "c", "炮", "砲") ROOK = ("R", "r", "车", "車") @@ -93,6 +92,13 @@ class Type(enum.Enum): ELEPHANT = ("E", "e", "象", "相") ADVISOR = ("A", "a", "士", "仕") KING = ("K", "k", "将", "帥") + # \u00B7 is a half width mid dot, and \u30FB is full width + EMPTY = ( + "\u00B7", + "\u00B7", + "\u30FB", + "\u30FB", + ) @staticmethod def type_of(c: str) -> Optional["Type"]: @@ -104,22 +110,29 @@ def type_of(c: str) -> Optional["Type"]: "e": Type.ELEPHANT, "a": Type.ADVISOR, "k": Type.KING, - ".": Type.EMPTY, + "\u00B7": Type.EMPTY, }.get(c.lower(), None) @staticmethod def symbol(type_: "Type", color: Color, lang: Language = Language.EN) -> str: """Returns symbol of the given piece according to its color and desired language.""" - if type_ == Type.EMPTY: - return type_.value[0] if lang == Language.EN: # Return English symbols if color == Color.RED: return type_.value[0] - elif color == Color.BLACK: + else: return type_.value[1] elif lang == Language.ZH: # Return Chinese symbols if color == Color.RED: return type_.value[2] - elif color == Color.BLACK: + else: return type_.value[3] raise ValueError("Unexpected combinations of language and color.") + + +class TerminalType(enum.Enum): + """Type of the terminal that the game is running. This affects + how to properly print boards. + """ + + MAC_OR_LINUX = 0 + COLAB_OR_SUBLIME_TERMINUS = 1 diff --git a/unitary/examples/quantum_chinese_chess/enums_test.py b/unitary/examples/quantum_chinese_chess/enums_test.py index 894a4991..2f77c6e4 100644 --- a/unitary/examples/quantum_chinese_chess/enums_test.py +++ b/unitary/examples/quantum_chinese_chess/enums_test.py @@ -19,7 +19,7 @@ def test_type_of(): assert Type.type_of("P") == Type.PAWN assert Type.type_of("k") == Type.KING assert Type.type_of("K") == Type.KING - assert Type.type_of(".") == Type.EMPTY + assert Type.type_of("·") == Type.EMPTY assert Type.type_of("b") == None @@ -34,7 +34,7 @@ def test_symbol(): assert Type.symbol(Type.HORSE, Color.RED, Language.ZH) == "马" assert Type.symbol(Type.HORSE, Color.BLACK, Language.ZH) == "馬" - assert Type.symbol(Type.EMPTY, Color.RED) == "." - assert Type.symbol(Type.EMPTY, Color.BLACK) == "." - assert Type.symbol(Type.EMPTY, Color.RED, Language.ZH) == "." - assert Type.symbol(Type.EMPTY, Color.BLACK, Language.ZH) == "." + assert Type.symbol(Type.EMPTY, Color.RED) == "·" + assert Type.symbol(Type.EMPTY, Color.BLACK) == "·" + assert Type.symbol(Type.EMPTY, Color.RED, Language.ZH) == "・" + assert Type.symbol(Type.EMPTY, Color.BLACK, Language.ZH) == "・" diff --git a/unitary/examples/quantum_chinese_chess/piece_test.py b/unitary/examples/quantum_chinese_chess/piece_test.py index 291a2691..650960ea 100644 --- a/unitary/examples/quantum_chinese_chess/piece_test.py +++ b/unitary/examples/quantum_chinese_chess/piece_test.py @@ -34,9 +34,9 @@ def test_symbol(): assert p1.symbol(Language.ZH) == "馬" p2 = Piece("c2", SquareState.EMPTY, Type.EMPTY, Color.NA) - assert p2.symbol() == "." - assert p2.__str__() == "." - assert p2.symbol(Language.ZH) == "." + assert p2.symbol() == "·" + assert p2.__str__() == "·" + assert p2.symbol(Language.ZH) == "・" def test_enum():