EMPTY = "." LETTERS = "abcdefgh" NAMES = {"b": "Black", "r": "Red"} EXAMPLES = {"b": "b6 a5", "r": "a3 b4"} def new_board(): board = [] for r in range(8): row = [] for c in range(8): if (r + c) % 2 == 1 and r < 3: row.append("b") elif (r + c) % 2 == 1 and r > 4: row.append("r") else: row.append(EMPTY) board.append(row) return board def print_board(board): print() print(" a b c d e f g h") for r in range(8): print(f" {8 - r} " + " ".join(board[r]) + f" {8 - r}") print(" a b c d e f g h") print() def opponent(player): if player == "r": return "b" return "r" def directions(piece): if piece.isupper(): return [(-1, -1), (-1, 1), (1, -1), (1, 1)] if piece == "r": return [(-1, -1), (-1, 1)] return [(1, -1), (1, 1)] def on_board(r, c): return 0 <= r < 8 and 0 <= c < 8 def piece_moves(board, r, c): steps = [] jumps = [] piece = board[r][c] enemy = opponent(piece.lower()) for dr, dc in directions(piece): r1, c1 = r + dr, c + dc r2, c2 = r + 2 * dr, c + 2 * dc if not on_board(r1, c1): continue if board[r1][c1] == EMPTY: steps.append((r, c, r1, c1)) elif board[r1][c1].lower() == enemy and on_board(r2, c2) and board[r2][c2] == EMPTY: jumps.append((r, c, r2, c2)) return steps, jumps def all_moves(board, player): steps = [] jumps = [] for r in range(8): for c in range(8): if board[r][c].lower() == player: s, j = piece_moves(board, r, c) steps += s jumps += j if jumps: return jumps return steps def make_move(board, move): r1, c1, r2, c2 = move piece = board[r1][c1] board[r1][c1] = EMPTY board[r2][c2] = piece jumped = abs(r2 - r1) == 2 if jumped: board[(r1 + r2) // 2][(c1 + c2) // 2] = EMPTY crowned = False if (piece == "r" and r2 == 0) or (piece == "b" and r2 == 7): board[r2][c2] = piece.upper() crowned = True return jumped, crowned def parse_square(text): if len(text) != 2 or text[0] not in LETTERS or text[1] not in "12345678": return None return 8 - int(text[1]), LETTERS.index(text[0]) def parse_move(text): parts = text.split() if len(parts) != 2: return None start = parse_square(parts[0]) end = parse_square(parts[1]) if start is None or end is None: return None return start + end def ask_move(player, moves): while True: text = input(f"{NAMES[player]}'s move (example: {EXAMPLES[player]}) or q to quit: ").strip().lower() if text == "q": return None move = parse_move(text) if move in moves: return move if abs(moves[0][2] - moves[0][0]) == 2: print("Invalid move. You have a jump available and must take it.") else: print("Invalid move. Try again.") def count_pieces(board, player): total = 0 for row in board: for square in row: if square.lower() == player: total += 1 return total def main(): print("=== Terminal Checkers ===") print("Black (b) starts at the top, Red (r) at the bottom. Kings are capital letters.") board = new_board() player = "b" while True: print_board(board) print(f"Black pieces: {count_pieces(board, 'b')} Red pieces: {count_pieces(board, 'r')}") moves = all_moves(board, player) if not moves: print(f"{NAMES[player]} has no moves left. {NAMES[opponent(player)]} wins!") break move = ask_move(player, moves) if move is None: print("Game ended.") break jumped, crowned = make_move(board, move) while jumped and not crowned: jumps = piece_moves(board, move[2], move[3])[1] if not jumps: break print_board(board) print("Another jump is available, keep going!") move = ask_move(player, jumps) if move is None: print("Game ended.") return jumped, crowned = make_move(board, move) if crowned: print(f"{NAMES[player]} got a king!") player = opponent(player) if __name__ == "__main__": main()