Merge pull request #173 from tsmaster/port-hexpawn

update port HEXAPAWN to Python
This commit is contained in:
Jeff Atwood
2021-03-02 11:04:53 -08:00
committed by GitHub

View File

@@ -14,8 +14,17 @@ Conversion to MITS BASIC by Steve North
Port to Python by Dave LeCompte
"""
import collections
import random
PAGE_WIDTH = 64
HUMAN_PIECE = 1
EMPTY_SPACE = 0
COMPUTER_PIECE = -1
ComputerMove = collections.namedtuple('ComputerMove', ['x', 'y', 'm1', 'm2'])
def print_centered(msg):
spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
print(spaces + msg)
@@ -70,21 +79,20 @@ def prompt_yes_no(msg):
elif response[0] == "N":
return False
def fnr(x):
score = {1: -3,
2: -2,
3: -1,
4: -6,
5: -5,
6: -4,
7: -9,
8: -8,
9: -7}
def reverse_board_position(x):
assert(x >= 1 and x < 10)
score = {1: 3,
2: 2,
3: 1,
4: 6,
5: 5,
6: 4,
7: 9,
8: 8,
9: 7}
return score[x]
def fnm(y):
return y % 10
def get_b(x, y):
data = [[-1, -1, -1, 1, 0, 0, 0, 1, 1],
[-1, -1, -1, 0, 1, 0, 1, 0, 1],
@@ -106,36 +114,49 @@ def get_b(x, y):
[ 0, -1, 0, 1, -1, 0, 0, 0, 0],
[-1, 0, 0, -1, 1, 0, 0, 0, 0]]
return data[x+1][y+1]
assert(x >= 1 and x < 20)
assert(y >= 1 and y < 10)
return data[x-1][y-1]
m_data = [[24, 25, 36, 0],
[14, 15, 36, 0],
[15, 35, 36, 47],
[36, 58, 59, 0],
[15, 35, 36, 0],
[24, 25, 26, 0],
[26, 57, 58, 0],
[26, 35, 0, 0],
[47, 48, 0, 0],
[35, 36, 0, 0],
[35, 36, 0, 0],
[36, 0, 0, 0],
[47, 58, 0, 0],
[15, 0, 0, 0],
[26, 47, 0, 0],
[47, 58, 0, 0],
[35, 36, 47, 0],
[28, 58, 0, 0],
[15, 47, 0, 0]]
def get_m(x, y):
data = [[24, 25,36,0],
[14,15,36,0],
[15,35,36,47],
[36,58,59,0],
[15,35,36,0],
[24,25,26,0],
[26,57,58,0],
[26,35,0,0],
[47,48,0,0],
[35,36,0,0],
[35,36,0,0],
[36,0,0,0],
[47,58,0,0],
[15,0,0,0],
[26,47,0,0],
[47,58,0,0],
[35,36,47,0],
[28,58,0,0],
[15,47,0,0]]
assert(x >= 1 and x < 20)
assert(y >= 1 and y < 5)
return data[x+1][y+1]
return m_data[x-1][y-1]
def set_m(x, y, value):
m_data[x-1][y-1] = value
def init_board():
return [-1] * 3 + [0] * 3 + [1] * 3
return ([COMPUTER_PIECE] * 3 +
[EMPTY_SPACE] * 3 +
[HUMAN_PIECE] * 3)
def print_board(board):
pieces = "X.O"
piece_dict = {COMPUTER_PIECE: 'X',
EMPTY_SPACE: '.',
HUMAN_PIECE: 'O'}
space = " "*10
print()
@@ -145,7 +166,7 @@ def print_board(board):
line += space
space_number = i * 3 + j
space_contents = board[space_number]
line += pieces[space_contents + 1]
line += piece_dict[space_contents]
print(line)
print()
@@ -157,7 +178,7 @@ def get_coordinates():
m1, m2 = [int(c) for c in response.split(',')]
return m1, m2
except ValueError as ve:
print("ILLEGAL MOVE.")
print_illegal()
def print_illegal():
print("ILLEGAL MOVE.")
@@ -168,78 +189,329 @@ def board_contents(board, space_number):
def set_board(board, space_number, new_value):
board[space_number - 1] = new_value
def is_legal_move(board, m1, m2):
if board_contents(board, m1) != HUMAN_PIECE:
# Start space doesn't contain player's piece
return False
if board_contents(board, m2) == HUMAN_PIECE:
# Destination space contains player's piece (can't capture your own piece)
return False
# line 160
is_capture = (m2-m1 != -3)
if is_capture and board_contents(board, m2) != COMPUTER_PIECE:
# Destination does not contain computer piece
return False
# line 170
if m2 > m1:
# can't move backwards
return False
# line 180
if (not is_capture) and board_contents(board, m2) != EMPTY_SPACE:
# Destination is not open
return False
# line 185
if m2-m1 < -4:
# too far
return False
# line 186
if m1 == 7 and m2 == 3:
# can't jump corner to corner (wrapping around the board)
return False
return True
def player_piece_on_back_row(board):
for space in range(1,4):
if board_contents(board, space) == HUMAN_PIECE:
return True
return False
def computer_piece_on_front_row(board):
for space in range(7, 10):
if board_contents(board, space) == COMPUTER_PIECE:
return True
return False
def all_human_pieces_captured(board):
return len(list(get_human_spaces(board))) == 0
def all_computer_pieces_captured(board):
return len(list(get_computer_spaces(board))) == 0
def human_win(last_computer_move):
print("YOU WIN")
set_m(last_computer_move.x, last_computer_move.y, 0)
global l
l += 1
def computer_win(has_moves):
if has_moves:
msg = "YOU CAN'T MOVE, SO "
else:
msg = ""
msg += "I WIN"
print(msg)
global w
w += 1
def show_scores():
print(f"I HAVE WON {w} AND YOU {l} OUT OF {w+l} GAMES.")
print()
def human_has_move(board):
# line 690
for i in get_human_spaces(board):
if board_contents(board, i-3) == EMPTY_SPACE:
# can move piece forward
return True
elif reverse_board_position(i) == i:
# line 780
# can capture from center
if ((board_contents(board, i-2) == COMPUTER_PIECE) or
(board_contents(board, i-4) == COMPUTER_PIECE)):
return True
else:
continue
elif i < 7:
# Line 760
assert((i == 4) or (i == 6))
# can capture computer piece at 2
if board_contents(board, 2) == COMPUTER_PIECE:
return True
else:
continue
elif board_contents(board, 5) == COMPUTER_PIECE:
assert((i == 7) or (i == 9))
# can capture computer piece at 5
return True
else:
continue
return False
def get_board_spaces():
yield from range(1, 10)
def get_board_spaces_with(board, val):
for i in get_board_spaces():
if board_contents(board, i) == val:
yield i
def get_human_spaces(board):
yield from get_board_spaces_with(board, HUMAN_PIECE)
def get_empty_spaces(board):
yield from get_board_spaces_with(board, EMPTY_SPACE)
def get_computer_spaces(board):
yield from get_board_spaces_with(board, COMPUTER_PIECE)
def has_computer_move(board):
for i in get_computer_spaces(board):
found_move = False
if board_contents(board, i+3) == EMPTY_SPACE:
# can move forward (down)
return True
# line 260
if reverse_board_position(i) == i:
# i is in the middle column
if ((board_contents(board, i + 2) == HUMAN_PIECE) or
(board_contents(board, i + 4) == HUMAN_PIECE)):
return True
else:
# line 270
if i > 3:
# beyond the first row
if board_contents(board, 8) == HUMAN_PIECE:
# can capture on 8
return True
else:
continue
else:
# line 280
if board_contents(board, 5) == HUMAN_PIECE:
# can capture on 5
return True
else:
continue
return False
def get_flipped_table(b_line): # TODO remove table altogether
t = {}
# line 360
for row in range(1, 4):
for column in range(1, 4):
# line 380
flipped_column = 4 - column
# fill out t to represent the data from b flipped left to right
space = (row-1) * 3 + column
flipped_space = (row - 1) * 3 + flipped_column
t[space] = get_b(b_line, flipped_space)
return t
def board_matches_b(b_line, board):
for s in get_board_spaces():
if get_b(b_line, s) != board_contents(board, s):
return False
return True
def board_matches_flipped_b(b_line, board):
flipped_table = get_flipped_table(b_line)
for s in get_board_spaces():
if flipped_table[s] != board_contents(board, s):
return False
return True
def does_b_line_match(b_line, board):
if board_matches_b(b_line, board):
return True, False
elif board_matches_flipped_b(b_line, board):
return True, True
else:
return False, None
def has_any_m_table(x):
for i in range(1,5):
if get_m(x, i) != 0:
return True
return False
def pick_from_m_table(x):
valid_y_list = [y for y in range(1,5) if get_m(x, y) != 0]
assert(len(valid_y_list) > 0)
return random.choice(valid_y_list)
def get_move_for_b_line(b_line, reverse_board):
# line 540
x = b_line
if not has_any_m_table(x):
return None
# line 600
y = pick_from_m_table(x)
# line 610
mxy = get_m(x, y)
m1 = mxy // 10
m2 = mxy % 10
if reverse_board:
m1 = reverse_board_position(m1)
m2 = reverse_board_position(m2)
return ComputerMove(x, y, m1, m2)
def find_b_line_that_matches_board(board):
for b_line in range(1,20):
matches, reverse_board = does_b_line_match(b_line, board)
if matches:
return b_line, reverse_board
# THE TERMINATION OF THIS LOOP IS IMPOSSIBLE
print("ILLEGAL BOARD PATTERN.")
assert(False)
def pick_computer_move(board):
if not has_computer_move(board):
# Line 340
return None
# line 350
b_line, reverse_board = find_b_line_that_matches_board(board)
m = get_move_for_b_line(b_line, reverse_board)
if m == None:
print("I RESIGN")
return None
return m
def play_game():
last_computer_move = None
board = init_board()
while True:
print_board(board)
has_legal_move = False
while not has_legal_move:
m1, m2 = get_coordinates()
if not is_legal_move(board, m1, m2):
print_illegal()
else:
# otherwise, acceptable move
has_legal_move = True
set_board(board, m1, 0)
set_board(board, m2, 1)
# line 205
print_board(board)
if (player_piece_on_back_row(board) or
all_computer_pieces_captured(board)):
human_win(last_computer_move)
return
# line 230
computer_move = pick_computer_move(board)
if computer_move is None:
human_win(last_computer_move)
return
last_computer_move = computer_move
m1, m2 = last_computer_move.m1, last_computer_move.m2
print(f"I MOVE FROM {m1} TO {m2}")
set_board(board, m1, 0)
set_board(board, m2, -1)
# line 640
print_board(board)
if (computer_piece_on_front_row(board) or
all_human_pieces_captured(board)):
computer_win(True)
return
elif not human_has_move(board):
computer_win(False)
return
def main():
print_header("HEXAPAWN")
if prompt_yes_no("INSTRUCTIONS (Y-N)?"):
print_instructions()
global w, l
w = 0
l = 0
x = 0
y = 0
board = init_board()
print_board(board)
while True:
m1, m2 = get_coordinates()
if board_contents(board, m1) != 1:
# Start space doesn't contain player's piece
print_illegal()
continue
if board_contents(board, m2) == 1:
# Destination space contains player's piece (can't capture your own piece)
print_illegal()
continue
# line 160
is_capture = (m2-m1 != -3)
if is_capture and board_contents(board, m2) != -1:
# Destination does not contain computer piece
print_illegal()
continue
# line 170
if m2 > m1:
# can't move backwards
print_illegal()
continue
# line 180
if (not is_capture) and board_contents(board, m2) != 0:
# Destination is not open
print_illegal()
continue
# line 185
if m2-m1 < -4:
# too far
print_illegal()
continue
# line 186
if m1 == 7 and m2 == 3:
# can't jump corner to corner ?!
print_illegal()
continue
# otherwise, acceptable move
break
play_game()
show_scores()
set_board(board, m1, 0)
set_board(board, m2, 1)
# line 205
print_board(board)
if __name__ == "__main__":
main()
"""
210 IF S(1)=1 OR S(2)=1 OR S(3)=1 THEN 820
220 FOR I=1 TO 9
221 IF S(I)=-1 THEN 230
222 NEXT I
223 GOTO 820
230 FOR I=1 TO 9
240 IF S(I)<>-1 THEN 330
250 IF S(I+3)=0 THEN 350