From 7e31c244d7d9ddd8e59bf49d8bb0e59ef02dadeb Mon Sep 17 00:00:00 2001 From: Dave LeCompte Date: Wed, 3 Mar 2021 08:56:30 -0800 Subject: [PATCH] work in progress porting CHECKERS to python did a first pass, porting the logic, refactoring into functions. Found a few bugs in the original code: - when a computer promoted to a king, the original piece would not be removed from the board - humans could move normal pieces like kings I've noticed at least one bug where the computer does not promote to a king when reaching row 0 --- 23 Checkers/python/checkers.py | 358 +++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 23 Checkers/python/checkers.py diff --git a/23 Checkers/python/checkers.py b/23 Checkers/python/checkers.py new file mode 100644 index 00000000..3a1d1236 --- /dev/null +++ b/23 Checkers/python/checkers.py @@ -0,0 +1,358 @@ +""" +CHECKERS + +How about a nice game of checkers? + +Ported by Dave LeCompte +""" + + +PAGE_WIDTH = 64 + +HUMAN_PLAYER = 1 +COMPUTER_PLAYER = -1 +HUMAN_PIECE = 1 +HUMAN_KING = 2 +COMPUTER_PIECE = -1 +COMPUTER_KING = -2 +EMPTY_SPACE = 0 + +INVALID_MOVE = -99 + + +def print_centered(msg): + spaces = " " * ((PAGE_WIDTH - len(msg)) // 2) + print(spaces + msg) + + +def print_header(title): + print_centered(title) + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + print() + print() + print() + + +class Board: + def __init__(self): + self.spaces = [[0 for y in range(8)] for x in range(8)] + for x in range(8): + if (x % 2) == 0: + self.spaces[x][6] = COMPUTER_PIECE + self.spaces[x][2] = HUMAN_PIECE + self.spaces[x][0] = HUMAN_PIECE + else: + self.spaces[x][7] = COMPUTER_PIECE + self.spaces[x][5] = COMPUTER_PIECE + self.spaces[x][1] = HUMAN_PIECE + + def __str__(self): + pieces = { + EMPTY_SPACE: ".", + HUMAN_PIECE: "O", + HUMAN_KING: "O*", + COMPUTER_PIECE: "X", + COMPUTER_KING: "X*", + } + + s = "\n\n\n" + for y in range(7, -1, -1): + for x in range(0, 8): + piece_str = pieces[self.spaces[x][y]] + piece_str += " " * (5 - len(piece_str)) + s += piece_str + s += "\n" + s += "\n\n" + + return s + + +def print_instructions(): + print("THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X,") + print("AND YOU ARE O. THE COMPUTER WILL MOVE FIRST.") + print("SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM.") + print("(0,0) IS THE LOWER LEFT CORNER") + print("(0,7) IS THE UPPER LEFT CORNER") + print("(7,0) IS THE LOWER RIGHT CORNER") + print("(7,7) IS THE UPPER RIGHT CORNER") + print("THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER") + print("JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP.") + print() + print() + print() + + +def get_spaces(): + for x in range(0, 8): + for y in range(0, 8): + yield x, y + + +def get_spaces_with_computer_pieces(board): + for x, y in get_spaces(): + contents = board.spaces[x][y] + if contents < 0: + yield x, y + + +def get_spaces_with_human_pieces(board): + for x, y in get_spaces(): + contents = board.spaces[x][y] + if contents > 0: + yield x, y + + +def pick_computer_move(board): + r = [INVALID_MOVE] * 5 + for x, y in get_spaces_with_computer_pieces(board): + contents = board.spaces[x][y] + if contents == COMPUTER_PIECE: + for dx in (-1, 1): + dy = -1 + sub_650(board, x, y, dx, dy, r) + else: + for dx in (-1, 1): + for dy in (-1, 1): + sub_650(board, x, y, dx, dy, r) + + if r[0] != INVALID_MOVE: + dx = r[3] - r[1] + dy = r[4] - r[2] + if abs(dx) != abs(dy): + print(r) + assert abs(dx) == abs(dy) + return r + + +def sub_650(board, x, y, dx, dy, r): + new_x = x + dx + new_y = y + dy + if not ((0 <= new_x <= 7) and (0 <= new_y <= 7)): + return + + contents = board.spaces[new_x][new_y] + if contents == 0: + sub_910(board, x, y, new_x, new_y, r) + return + if contents < 0: + return + + # check landing space + landing_x = new_x + dx + landing_y = new_y + dy + + # line 790 + if not ((0 <= landing_x <= 7) and (0 <= landing_y <= 7)): + return + if board.spaces[landing_x][landing_y] == 0: + sub_910(board, x, y, landing_x, landing_y, r) + + +def sub_910(board, start_x, start_y, dest_x, dest_y, r): + q = 0 + if dest_y == 0 and board.spaces[start_x][start_y] == COMPUTER_PIECE: + q += 2 + if abs(start_y - dest_y) == 2: + q += 5 + if start_y == 7: + q -= 2 + if dest_x in (0, 7): + q += 1 + for c in (-1, 1): + if (0 <= dest_x + c <= 7) and (1 <= dest_y): + # line 1035 + if board.spaces[dest_x + c][dest_y - 1] < 0: + q += 1 + # line 1040 + elif (0 <= dest_x - c <= 7) and (dest_y + 1 <= 7): + # line 1045 + if ( + (board.spaces[dest_x + c][dest_y - 1] > 0) + and (board.spaces[dest_x - c][dest_y + 1] == EMPTY_SPACE) + or ((dest_x - c == start_x) and (dest_y + 1 == start_y)) + ): + q -= 2 + # line 1080 + + if q > r[0]: + r[0] = q + r[1] = start_x + r[2] = start_y + r[3] = dest_x + r[4] = dest_y + + +def remove_r_pieces(board, r): + remove_pieces(board, r[1], r[2], r[3], r[4]) + + +def remove_pieces(board, start_x, start_y, dest_x, dest_y): + board.spaces[dest_x][dest_y] = board.spaces[start_x][start_y] + board.spaces[start_x][start_y] = EMPTY_SPACE + + if abs(dest_x - start_x) == 2: + mid_x = (start_x + dest_x) // 2 + mid_y = (start_y + dest_y) // 2 + board.spaces[mid_x][mid_y] = EMPTY_SPACE + + +def play_computer_move(board, r): + print(f"FROM {r[1]} {r[2]} TO {r[3]} {r[4]}") + + while True: + if r[4] == 0: + # KING ME + board.spaces[r[3]][r[4]] = COMPUTER_KING + remove_r_pieces(board, r) + return + else: + # line 1250 + board.spaces[r[3]][r[4]] = board.spaces[r[1]][r[2]] + remove_r_pieces(board, r) + + if abs(r[1] - r[3]) != 2: + return + + # line 1340 + x = r[3] + y = r[4] + r[0] = INVALID_MOVE + if board.spaces[x][y] == COMPUTER_PIECE: + for a in (-2, 2): + try_extend(board, r, x, y, a, -2) + else: + assert board.spaces[x][y] == COMPUTER_KING + for a in (-2, 2): + for b in (-2, 2): + try_extend(board, r, x, y, a, b) + if r[0] != INVALID_MOVE: + print(f"TO {r[3]} {r[4]}") + else: + return + + +def try_extend(board, r, x, y, a, b): + # line 1370 + nx = x + a + ny = y + b + if not ((0 <= nx <= 7) and (0 <= ny <= 7)): + return + if (board.spaces[nx][ny] == EMPTY_SPACE) and ( + board.spaces[nx + a // 2][ny + b // 2] > 0 + ): + sub_910(board, x, y, nx, ny, r) + + +def get_human_move(board): + is_king = False + + while True: + print("FROM?") + from_response = input() + x, y = [int(c) for c in from_response.split(",")] + + if board.spaces[x][y] > 0: + break + + is_king = board.spaces[x][y] == HUMAN_KING + + while True: + print("TO?") + to_response = input() + a, b = [int(c) for c in to_response.split(",")] + + if (not is_king) and (b < y): + # CHEATER! Trying to move non-king backwards + continue + if ( + (board.spaces[a][b] == 0) + and (abs(a - x) <= 2) + and (abs(a - x) == abs(b - y)) + ): + break + return x, y, a, b + + +def get_human_extension(board, sx, sy): + is_king = board.spaces[sx][sy] == HUMAN_KING + + while True: + print("+TO?") + to_response = input() + a1, b1 = [int(c) for c in to_response.split(",")] + if a1 < 0: + return False, None + if (not is_king) and (b1 < sy): + # CHEATER! Trying to move non-king backwards + continue + if ( + (board.spaces[a1][b1] == EMPTY_SPACE) + and (abs(a1 - sx) == 2) + and (abs(b1 - sy) == 2) + ): + return True, (sx, sy, a1, b1) + + +def play_human_move(board, start_x, start_y, dest_x, dest_y): + remove_pieces(board, start_x, start_y, dest_x, dest_y) + + if dest_y == 7: + # KING ME + board.spaces[dest_x][dest_y] = HUMAN_KING + + +def print_human_won(): + print() + print("YOU WIN.") + + +def print_computer_won(): + print() + print("I WIN.") + + +def check_pieces(board): + if len(list(get_spaces_with_computer_pieces(board))) == 0: + print_human_won() + return False + if len(list(get_spaces_with_computer_pieces(board))) == 0: + print_computer_won() + return False + return True + + +def play_game(): + board = Board() + + while True: + r = pick_computer_move(board) + if r[0] == INVALID_MOVE: + print_human_won() + return + play_computer_move(board, r) + + print(board) + + if not check_pieces(board): + return + + sx, sy, dx, dy = get_human_move(board) + play_human_move(board, sx, sy, dx, dy) + if abs(dx - sx) == 2: + while True: + extend, move = get_human_extension(board, dx, dy) + if not extend: + break + sx, sy, dx, dy = move + play_human_move(board, sx, sy, dx, dy) + + +def main(): + print_header("CHECKERS") + print_instructions() + + play_game() + + +if __name__ == "__main__": + main()