mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 04:15:45 -08:00
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
This commit is contained in:
358
23 Checkers/python/checkers.py
Normal file
358
23 Checkers/python/checkers.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user