At this point I think everything except for amazons-works can be deleted, but I'm hanging onto my mess for a bit before doing the dishes later, just in case.
This commit is contained in:
parent
f2000deb40
commit
56f1eb564c
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,159 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for x, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for y, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction:
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
if self.in_bounds(move_option):
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
addition = move_option.copy()
|
||||
reachables.append(addition) # add it to the list.
|
||||
else:
|
||||
hit_something = True
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
return reachables
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
for amazon in amazons:
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
# Before we burn anything, we need to empty the square we moved from
|
||||
# so we can burn it or squares past it if we want to:
|
||||
self.state[amazon[0]][amazon[1]] = 0
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
# Let's not forget to put the amazon back in its square.
|
||||
# Also, since we emptied a square, we have to reverse the logic:
|
||||
if self.get_active_player_code() == 2:
|
||||
self.state[amazon[0]][amazon[1]] = 3
|
||||
else:
|
||||
self.state[amazon[0]][amazon[1]] = 2
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = move_and_burn[0][0], move_and_burn[0][1]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = move_and_burn[1][0], move_and_burn[1][1]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = move_and_burn[2][0], move_and_burn[2][1]
|
||||
new_state[ai][aj] = 0 # The amazon's start square is emptied.
|
||||
if self.get_active_player_code() == 2: # The move square is filled.
|
||||
new_state[mi][mj] = 3
|
||||
else:
|
||||
new_state[mi][mj] = 2
|
||||
new_state[bi][bj] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
if (x in range(len(self.state[0]))) and (y in range(len(self.state[0]))):
|
||||
if self.state[x][y] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
|
@ -0,0 +1,36 @@
|
|||
from board import *
|
||||
from view import *
|
||||
|
||||
board = Board()
|
||||
|
||||
translator = {
|
||||
'a4':[0,0], 'a3':[1,0], 'a2':[2,0], 'a1':[3,0],
|
||||
'b4':[0,1], 'b3':[1,1], 'b2':[2,1], 'b1':[3,1],
|
||||
'c4':[0,2], 'c3':[1,2], 'c2':[2,2], 'c1':[3,2],
|
||||
'd4':[0,3], 'd3':[1,3], 'd2':[2,3], 'd1':[3,3]
|
||||
}
|
||||
|
||||
def turn():
|
||||
global board
|
||||
move_chosen = False
|
||||
while move_chosen == False:
|
||||
tui(board)
|
||||
print("Valid coordinates look like: \'b2\' or \'c4\'")
|
||||
amazon = translator[(input("The starting coordinates of the amazon of your choice: "))]
|
||||
move = translator[(input("The coordinates of the square you'd like to move to: "))]
|
||||
burn = translator[(input("The coordinates of the square you'd like to set aflame: "))]
|
||||
choice = ((amazon[0],amazon[1]),move,burn)
|
||||
print(board.possible_moves())
|
||||
if choice in board.possible_moves():
|
||||
board.state = board.get_successor_state(choice)
|
||||
move_chosen = True
|
||||
else:
|
||||
print(str(choice)+" is not a valid move.")
|
||||
|
||||
|
||||
def play():
|
||||
while len(board.possible_moves())>0:
|
||||
turn()
|
||||
return ("Player "+str(get_active_player_code()-1)+" loses!")
|
||||
|
||||
play()
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,39 @@
|
|||
from unittest import TestCase
|
||||
from board import Board
|
||||
|
||||
class TestBoard(TestCase):
|
||||
def setUp(self):
|
||||
self.board = Board()
|
||||
self.tiny = Board()
|
||||
self.tiny.state = [[3,0], [0,2]]
|
||||
|
||||
def test_board_size_is_correct(self):
|
||||
self.assertEqual(len(self.board.state), 4)
|
||||
|
||||
def test_in_bounds_works(self):
|
||||
bad = [
|
||||
(-1, 1), (5, 1)
|
||||
]
|
||||
good = [
|
||||
(2, 2), (0, 0), (3, 3)
|
||||
]
|
||||
for square in bad:
|
||||
self.assertFalse(self.board.in_bounds(square))
|
||||
for square in good:
|
||||
self.assertTrue(self.board.in_bounds(square))
|
||||
|
||||
def test_get_initial_state(self):
|
||||
|
||||
def test_get_active_player_code(self):
|
||||
|
||||
def test_get_active_amazons_positions(self):
|
||||
|
||||
def test_possible_moves(self):
|
||||
|
||||
def test_get_successor_state(self, move_and_burn):
|
||||
|
||||
def test_get_possible_successor_states(self):
|
||||
|
||||
def test_is_empty(self, square):
|
||||
|
||||
def test_get_reachable_squares(self, amazon):
|
|
@ -0,0 +1,17 @@
|
|||
from board import *
|
||||
from os import system, name
|
||||
|
||||
def clear():
|
||||
if name == 'nt':
|
||||
_ = system('cls')
|
||||
else:
|
||||
_ = system('clear')
|
||||
|
||||
def tui(board): #This displays the board state to the user
|
||||
clear()
|
||||
print(" a b c d")
|
||||
print("4 "+str(board.state[0][0])+" "+str(board.state[0][1])+" "+str(board.state[0][2])+" "+str(board.state[0][3]))
|
||||
print("3 "+str(board.state[1][0])+" "+str(board.state[1][1])+" "+str(board.state[1][2])+" "+str(board.state[1][3]))
|
||||
print("2 "+str(board.state[2][0])+" "+str(board.state[2][1])+" "+str(board.state[2][2])+" "+str(board.state[2][3]))
|
||||
print("1 "+str(board.state[3][0])+" "+str(board.state[3][1])+" "+str(board.state[3][2])+" "+str(board.state[3][3]))
|
||||
print("It's player "+str(board.get_active_player_code()-1)+"'s turn.")
|
Binary file not shown.
Binary file not shown.
|
@ -67,40 +67,71 @@ class Board:
|
|||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
print('amazon= '+str(origin))
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
print("origin: "+str(origin))
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction...
|
||||
# move in the specified direction:
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print('----------------')
|
||||
print('move_option = '+str(move_option))
|
||||
# move in the specified direction...
|
||||
print('in bounds = '+str(self.in_bounds(move_option)))
|
||||
print('is empty = '+str(self.is_empty(move_option)))
|
||||
if self.in_bounds(move_option) and self.is_empty(move_option):
|
||||
print("move option: "+str(move_option))
|
||||
print("It's in bounds: "+str(self.in_bounds(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
print("It's empty: "+str(self.is_empty(move_option)))
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
reachables.append(move_option) # add it to the list.
|
||||
addition = move_option.copy()
|
||||
print("once again, the move option is:"+str(addition))
|
||||
reachables.append(addition) # add it to the list.
|
||||
print("reaching: "+str(reachables))
|
||||
else:
|
||||
hit_something = True
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print('I hit something = '+str(hit_something))
|
||||
print('ok found em')
|
||||
print("reachables:"+str(reachables))
|
||||
return reachables
|
||||
|
||||
|
||||
# def get_reachable_squares(self, origin):
|
||||
# directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
# reachables = []
|
||||
# for direction in directions: # For each of the 8 directions...
|
||||
# move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
# hit_something = False # We will make this false if we find an obstacle.
|
||||
# while hit_something == False: # Until we hit something....
|
||||
# # move in the specified direction:
|
||||
# move_option[0] += direction[0]
|
||||
# move_option[1] += direction[1]
|
||||
# if self.in_bounds(move_option):
|
||||
# if self.is_empty(move_option):
|
||||
# # if we are still on the board and if the square is empty...
|
||||
# addition = move_option.copy()
|
||||
# reachables.append(addition) # add it to the list.
|
||||
# else:
|
||||
# hit_something = True
|
||||
# else: # If we hit the edge of the board or an obstacle...
|
||||
# hit_something = True
|
||||
# return reachables
|
||||
|
||||
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
print('amazons are '+str(amazons))
|
||||
for amazon in amazons:
|
||||
print('we are on '+str(amazon))
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
print('move options are: '+str(amazon_move_options))
|
||||
# Before we burn anything, we need to empty the square we moved from
|
||||
# so we can burn it or squares past it if we want to:
|
||||
self.state[amazon[0]][amazon[1]] = 0
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
|
@ -110,21 +141,29 @@ class Board:
|
|||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
print('one down')
|
||||
# Let's not forget to put the amazon back in its square.
|
||||
# Also, since we emptied a square, we have to reverse the logic:
|
||||
if self.get_active_player_code() == 2:
|
||||
self.state[amazon[0]][amazon[1]] = 3
|
||||
else:
|
||||
self.state[amazon[0]][amazon[1]] = 2
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = [move_and_burn[0][0]], [move_and_burn[0][1]]
|
||||
ai, aj = move_and_burn[0][0], move_and_burn[0][1]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = [move_and_burn[1][0]], [move_and_burn[1][1]]
|
||||
mi, mj = move_and_burn[1][0], move_and_burn[1][1]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = [move_and_burn[2][0]], [move_and_burn[2][1]]
|
||||
new_state[aj][ai] = 0 # The amazon's start square is emptied.
|
||||
new_state[mj][mi] = self.get_active_player_code() # The move square is filled.
|
||||
new_state[bj][bi] = 1 # The burn square is burned.
|
||||
bi, bj = move_and_burn[2][0], move_and_burn[2][1]
|
||||
new_state[ai][aj] = 0 # The amazon's start square is emptied.
|
||||
if self.get_active_player_code() == 2: # The move square is filled.
|
||||
new_state[mi][mj] = 3
|
||||
else:
|
||||
new_state[mi][mj] = 2
|
||||
new_state[bi][bj] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
|
@ -137,21 +176,19 @@ class Board:
|
|||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
if (x in range(len(self.state[0]))) and (y in range(len(self.state[0]))):
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
print('square = '+str(square))
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
board = Board()
|
||||
|
||||
print(Board.possible_moves(board))
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for y, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for x, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
print('amazon= '+str(origin))
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction...
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print('----------------')
|
||||
print('move_option = '+str(move_option))
|
||||
# move in the specified direction...
|
||||
print('in bounds = '+str(self.in_bounds(move_option)))
|
||||
print('is empty = '+str(self.is_empty(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
reachables.append(move_option) # add it to the list.
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print('I hit something = '+str(hit_something))
|
||||
print('ok found em')
|
||||
return reachables
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
print('amazons are '+str(amazons))
|
||||
for amazon in amazons:
|
||||
print('we are on '+str(amazon))
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
print('move options are: '+str(amazon_move_options))
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
print('one down')
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = [move_and_burn[0][0]], [move_and_burn[0][1]]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = [move_and_burn[1][0]], [move_and_burn[1][1]]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = [move_and_burn[2][0]], [move_and_burn[2][1]]
|
||||
new_state[aj][ai] = 0 # The amazon's start square is emptied.
|
||||
new_state[mj][mi] = self.get_active_player_code() # The move square is filled.
|
||||
new_state[bj][bi] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
print("the square is empty: "+str(self.state[y][x]))
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
print('square = '+str(square))
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
board = Board()
|
||||
|
||||
print(Board.possible_moves(board))
|
|
@ -0,0 +1,158 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for y, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for x, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
print("origin: "+str(origin))
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction:
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print("move option: "+str(move_option))
|
||||
print("It's in bounds: "+str(self.in_bounds(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
print("It's empty: "+str(self.is_empty(move_option)))
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
addition = move_option.copy()
|
||||
print("once again, the move option is:"+str(addition))
|
||||
reachables.append(addition) # add it to the list.
|
||||
print("reaching: "+str(reachables))
|
||||
else:
|
||||
hit_something = True
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print("reachables:"+str(reachables))
|
||||
return reachables
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
for amazon in amazons:
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = [move_and_burn[0][0]], [move_and_burn[0][1]]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = [move_and_burn[1][0]], [move_and_burn[1][1]]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = [move_and_burn[2][0]], [move_and_burn[2][1]]
|
||||
new_state[aj][ai] = 0 # The amazon's start square is emptied.
|
||||
new_state[mj][mi] = self.get_active_player_code() # The move square is filled.
|
||||
new_state[bj][bi] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
if (x in range(len(self.state[0]))) and (y in range(len(self.state[0]))):
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
board = Board()
|
||||
|
||||
print(Board.possible_moves(board))
|
|
@ -1,3 +1,38 @@
|
|||
import board
|
||||
from board import *
|
||||
from view import *
|
||||
|
||||
possible_moves()
|
||||
board = Board()
|
||||
|
||||
translator = {
|
||||
'a4':[0,0], 'a3':[1,0], 'a2':[2,0], 'a1':[3,0],
|
||||
'b4':[0,1], 'b3':[1,1], 'b2':[2,1], 'b1':[3,1],
|
||||
'c4':[0,2], 'c3':[1,2], 'c2':[2,2], 'c1':[3,2],
|
||||
'd4':[0,3], 'd3':[1,3], 'd2':[2,3], 'd1':[3,3]
|
||||
}
|
||||
|
||||
def turn():
|
||||
global board
|
||||
move_chosen = False
|
||||
while move_chosen == False:
|
||||
tui(board)
|
||||
print("Valid coordinates look like: \'b2\' or \'c4\'")
|
||||
amazon = translator[(input("The starting coordinates of the amazon of your choice: "))]
|
||||
move = translator[(input("The coordinates of the square you'd like to move to: "))]
|
||||
burn = translator[(input("The coordinates of the square you'd like to set aflame: "))]
|
||||
choice = ((amazon[0],amazon[1]),move,burn)
|
||||
print(board.possible_moves())
|
||||
if choice in board.possible_moves():
|
||||
print("This is what I'm sending: "+str(choice))
|
||||
board.state = board.get_successor_state(choice)
|
||||
print("board = "+str(board.state))
|
||||
move_chosen = True
|
||||
else:
|
||||
print(str(choice)+" is not a valid move.")
|
||||
|
||||
|
||||
def play():
|
||||
while len(board.possible_moves())>0:
|
||||
turn()
|
||||
return ("Player "+str(get_active_player_code()-1)+" loses!")
|
||||
|
||||
play()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from unittest import TestCase
|
||||
from board import Board
|
||||
|
||||
class TestBoard(TestCase):
|
||||
class TestBoard(TestCase):.
|
||||
def setUp(self):
|
||||
self.board = Board()
|
||||
self.tiny = Board()
|
||||
|
@ -23,10 +23,14 @@ class TestBoard(TestCase):
|
|||
self.assertTrue(self.board.in_bounds(square))
|
||||
|
||||
def test_get_initial_state(self):
|
||||
#unecessary?
|
||||
|
||||
def test_get_active_player_code(self):
|
||||
#unecessary?
|
||||
|
||||
def test_get_active_amazons_positions(self):
|
||||
|
||||
|
||||
|
||||
def test_possible_moves(self):
|
||||
|
||||
|
|
|
@ -1 +1,17 @@
|
|||
import board
|
||||
from board import *
|
||||
from os import system, name
|
||||
|
||||
def clear():
|
||||
if name == 'nt':
|
||||
_ = system('cls')
|
||||
else:
|
||||
_ = system('clear')
|
||||
|
||||
def tui(board): #This displays the board state to the user
|
||||
#clear()
|
||||
print(" a b c d")
|
||||
print("4 "+str(board.state[0][0])+" "+str(board.state[0][1])+" "+str(board.state[0][2])+" "+str(board.state[0][3]))
|
||||
print("3 "+str(board.state[1][0])+" "+str(board.state[1][1])+" "+str(board.state[1][2])+" "+str(board.state[1][3]))
|
||||
print("2 "+str(board.state[2][0])+" "+str(board.state[2][1])+" "+str(board.state[2][2])+" "+str(board.state[2][3]))
|
||||
print("1 "+str(board.state[3][0])+" "+str(board.state[3][1])+" "+str(board.state[3][2])+" "+str(board.state[3][3]))
|
||||
print("It's player "+str(board.get_active_player_code()-1)+"'s turn.")
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,283 @@
|
|||
from random import *
|
||||
from os import system, name
|
||||
|
||||
def clear():
|
||||
if name == 'nt':
|
||||
_ = system('cls')
|
||||
else:
|
||||
_ = system('clear')
|
||||
|
||||
class Square:
|
||||
def __init__(self, location, name):
|
||||
self.location = location #where the square is as an [x,y] pair
|
||||
self.x = location[0]
|
||||
self.y = location[1]
|
||||
self.state = "empty" #behind the scenes data
|
||||
self.display = " " #what the user will see
|
||||
self.name = name #There's gotta be a better way!
|
||||
|
||||
#The names of the squares are according to chess convention,
|
||||
#but the coordinates are according to usual matrix notation.
|
||||
a1 = Square([0,3], "a1")
|
||||
a2 = Square([0,2], "a2")
|
||||
a3 = Square([0,1], "a3")
|
||||
a4 = Square([0,0], "a4")
|
||||
b1 = Square([1,3], "b1")
|
||||
b2 = Square([1,2], "b2")
|
||||
b3 = Square([1,1], "b3")
|
||||
b4 = Square([1,0], "b4")
|
||||
c1 = Square([2,3], "c1")
|
||||
c2 = Square([2,2], "c2")
|
||||
c3 = Square([2,1], "c3")
|
||||
c4 = Square([2,0], "c4")
|
||||
d1 = Square([3,3], "d1")
|
||||
d2 = Square([3,2], "d2")
|
||||
d3 = Square([3,1], "d3")
|
||||
d4 = Square([3,0], "d4")
|
||||
|
||||
board=[a1,a2,a3,a4,b1,b2,b3,b4,c1,c2,c3,c4,d1,d2,d3,d4]
|
||||
|
||||
setup_seed = sample(board, 4) #We pick four squares for initial amazon placement
|
||||
|
||||
setup_seed[0].state = "ba1" #black amazon 1
|
||||
setup_seed[1].state = "ba2" #black amazon 2
|
||||
setup_seed[2].state = "wa1" #white amazon 1
|
||||
setup_seed[3].state = "wa2" #white amazon 2
|
||||
|
||||
turn = "w" #White moves first.
|
||||
turn_counter = 1
|
||||
|
||||
def display(): #This interprets the states of the squares for display
|
||||
global turn, turn_counter, board
|
||||
for box in board:
|
||||
if box.state == "empty":
|
||||
box.display = " "
|
||||
if box.state == "ba1":
|
||||
box.display = "B"
|
||||
if box.state == "ba2":
|
||||
box.display = "B"
|
||||
if box.state == "wa1":
|
||||
box.display = "W"
|
||||
if box.state == "wa2":
|
||||
box.display = "W"
|
||||
if box.state == "fire":
|
||||
box.display = "X"
|
||||
|
||||
def gui(): #This displays the board state to the user
|
||||
global turn, turn_counter, board
|
||||
clear()
|
||||
display()
|
||||
print(" a b c d")
|
||||
print("4 "+a4.display+" "+b4.display+" "+c4.display+" "+d4.display)
|
||||
print("3 "+a3.display+" "+b3.display+" "+c3.display+" "+d3.display)
|
||||
print("2 "+a2.display+" "+b2.display+" "+c2.display+" "+d2.display)
|
||||
print("1 "+a1.display+" "+b1.display+" "+c1.display+" "+d1.display)
|
||||
if turn_counter%2 == 0:
|
||||
print("It's black's turn.")
|
||||
else:
|
||||
print("It's white's turn.")
|
||||
|
||||
def choice_translate(box): #Takes the string inputs of the user and outputs corresponding square
|
||||
global turn, turn_counter, board
|
||||
if box == "a1":
|
||||
return a1
|
||||
if box == "a2":
|
||||
return a2
|
||||
if box == "a3":
|
||||
return a3
|
||||
if box == "a4":
|
||||
return a4
|
||||
if box == "b1":
|
||||
return b1
|
||||
if box == "b2":
|
||||
return b2
|
||||
if box == "b3":
|
||||
return b3
|
||||
if box == "b4":
|
||||
return b4
|
||||
if box == "c1":
|
||||
return c1
|
||||
if box == "c2":
|
||||
return c2
|
||||
if box == "c3":
|
||||
return c3
|
||||
if box == "c4":
|
||||
return c4
|
||||
if box == "d1":
|
||||
return d1
|
||||
if box == "d2":
|
||||
return d2
|
||||
if box == "d3":
|
||||
return d3
|
||||
if box == "d4":
|
||||
return d4
|
||||
else:
|
||||
return False
|
||||
|
||||
def valid_move(start, end): #Checks to see if a mvoe is valid (for both amazons and arrows)
|
||||
global turn, turn_counter, board
|
||||
|
||||
test_set=[] #This will be the set of squares between the start and end of the move.
|
||||
#For reasons unknown, the test set always finds each eligible square twice. How to fix?
|
||||
|
||||
#There are three reasons to immediately reject a move:
|
||||
if end.state != "empty": #If the ending square isn't empty
|
||||
return False
|
||||
if start == end: #If the ending square is the starting square
|
||||
return False
|
||||
#If the start and end aren't in the same row, column, or diagonal.
|
||||
if (start.x != end.x) and (start.y != end.y) and (abs(start.x - end.x) != abs(start.y - end.y)):
|
||||
return False
|
||||
|
||||
#If they are in the same column...
|
||||
if start.x == end.x:
|
||||
if start.y > end.y: #If we are going down....
|
||||
for box in board: #...find the squares...
|
||||
if box.x == start.x: #...in that column...
|
||||
for i in range(1, abs(start.y-end.y)): #... between the start and end...
|
||||
if box.y == start.y-i:
|
||||
test_set.append(box) #... and add it to the list.
|
||||
if start.y < end.y: #If we are going up... etc...
|
||||
for box in board:
|
||||
if box.x == start.x:
|
||||
for i in range(1, abs(start.y-end.y)):
|
||||
if box.y == start.y+i:
|
||||
test_set.append(box)
|
||||
|
||||
#If they are in the same row...
|
||||
if start.y == end.y:
|
||||
if start.x > end.x: #going right....
|
||||
for box in board:
|
||||
if box.y == start.y:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if box.x == start.x-i:
|
||||
test_set.append(box)
|
||||
if start.x < end.x: #going left...
|
||||
for box in board:
|
||||
if box.y == start.y:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if box.x == start.x+i:
|
||||
test_set.append(box)
|
||||
|
||||
#If they are on the same diagonal
|
||||
if abs(start.x - end.x) == abs(start.y - end.y):
|
||||
if (start.x < end.x) and (start.y < end.y): #going up and right
|
||||
for box in board:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if ((box.x == start.x+i) and (box.y == start.y+i)):
|
||||
test_set.append(box)
|
||||
if (start.x > end.x) and (start.y > end.y): #going down and left
|
||||
for box in board:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if ((box.x == start.x-i) and (box.y == start.y-i)):
|
||||
test_set.append(box)
|
||||
if (start.x > end.x) and (start.y < end.y): #going up and left
|
||||
for box in board:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if ((box.x == start.x-i) and (box.y == start.y+i)):
|
||||
test_set.append(box)
|
||||
if (start.x < end.x) and (start.y > end.y): #going down and right
|
||||
for box in board:
|
||||
for i in range(1, abs(start.x-end.x)):
|
||||
if ((box.x == start.x+i) and (box.y == start.y-i)):
|
||||
test_set.append(box)
|
||||
|
||||
for test in test_set: #now make sure all of the squares we've selected are indeed empty
|
||||
if test.state != "empty":
|
||||
return False
|
||||
return True
|
||||
|
||||
def possible_moves(amazon): #outputs a list of squares that can be reached by an amazon
|
||||
moves=[]
|
||||
for box in board:
|
||||
if valid_move(amazon, box):
|
||||
moves.append(box)
|
||||
return(moves)
|
||||
|
||||
def play():
|
||||
global turn, turn_counter, board
|
||||
gui()
|
||||
|
||||
#We check which amazons can legally move
|
||||
movable_amazons=[]
|
||||
for box in board:
|
||||
if box.state == turn+"a1":
|
||||
if len(possible_moves(box)) != 0:
|
||||
movable_amazons.append(box.name)
|
||||
if box.state == turn+"a2":
|
||||
if len(possible_moves(box)) != 0:
|
||||
movable_amazons.append(box.name)
|
||||
|
||||
#If none can move, then the game is over!
|
||||
if len(movable_amazons)==0:
|
||||
if turn_counter%2 == 0:
|
||||
input("White wins!")
|
||||
exit(0)
|
||||
if turn_counter%2 == 1:
|
||||
input("Black wins!")
|
||||
exit(0)
|
||||
|
||||
#Otherwise, the player chooses an amazon to move
|
||||
if len(movable_amazons) == 2: #If both can move they have a choice
|
||||
amazon_choice = input("Choose a square containing one of your amazons, either "+movable_amazons[0]+" or "+movable_amazons[1]+": ")
|
||||
while amazon_choice not in movable_amazons:
|
||||
amazon_choice = input("That's not one of the options. Try again: ")
|
||||
if len(movable_amazons) == 1: #If only one can move, they have no choice
|
||||
amazon_choice = movable_amazons[0]
|
||||
gui()
|
||||
|
||||
#The player chooses a square to move to
|
||||
print("You must move the amazon on "+amazon_choice+". Choose the square you want to move to.")
|
||||
print("Which of the following would you like to move to?")
|
||||
amazon_choice = choice_translate(amazon_choice)
|
||||
choices = [] #We have a list to display to the user...
|
||||
for option in possible_moves(amazon_choice): #...consisting of their possible moves.
|
||||
choices.append(option.name)
|
||||
move_choice = choice_translate(input(choices))
|
||||
|
||||
#The following three while statements only catch exemptions if made in this order.
|
||||
#So if the user first inputs a square correctly, but it's not a possible move,
|
||||
#and then they enter in gibberish, it wigs out. How to fix?
|
||||
while move_choice == False: #choice_translate outputs False when the input isn't a square name
|
||||
move_choice = choice_translate(input("That's not a valid move. Try again: "))
|
||||
while move_choice.name not in choices:
|
||||
move_choice = choice_translate(input("That's not an option. Try again: "))
|
||||
while valid_move(amazon_choice, move_choice) == False:
|
||||
move_choice = choice_translate(input("That amazon can't move there. Try again: "))
|
||||
|
||||
#If all goes well, we make the move by changing the states of the squares.
|
||||
if valid_move(amazon_choice, move_choice):
|
||||
move_choice.state = amazon_choice.state
|
||||
amazon_choice.state = "empty"
|
||||
gui()
|
||||
|
||||
#The player chooses a square to shoot
|
||||
choices = []
|
||||
for option in possible_moves(move_choice):
|
||||
choices.append(option.name)
|
||||
print("Choose the square you want to set aflame: ")
|
||||
burn_choice = choice_translate(input(choices))
|
||||
|
||||
#These three statements have the same problem as those above.
|
||||
while burn_choice == False:
|
||||
burn_choice = choice_translate(input("That's not the name of any square. Try again: "))
|
||||
while burn_choice.state != "empty":
|
||||
burn_choice = choice_translate(input("That's square isn't empty. Try again: "))
|
||||
while valid_move(move_choice, burn_choice) == False:
|
||||
burn_choice = choice_translate(input("You can't shoot there. Try again: "))
|
||||
burn_choice.state = "fire"
|
||||
|
||||
#Bookkeepping for turn taking
|
||||
turn_counter+=1
|
||||
if turn_counter%2 == 0:
|
||||
turn = "b"
|
||||
if turn_counter%2 == 1:
|
||||
turn = "w"
|
||||
|
||||
while True:
|
||||
play()
|
||||
|
||||
|
||||
#Next Steps:
|
||||
#have students change the abilities of the amazons, size of the board, etc.
|
||||
#Add a class to record game state!?!?!?
|
|
@ -0,0 +1,208 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
print("-----------")
|
||||
print("get_active_player_code")
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
print("-----------")
|
||||
print("get_active_amazons_positions")
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for x, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for y, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
print("positions = "+str(positions))
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
print("-----------")
|
||||
print("get_reachable_squares")
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
print("origin: "+str(origin))
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction:
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print("move option: "+str(move_option))
|
||||
print("It's in bounds: "+str(self.in_bounds(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
print("It's empty: "+str(self.is_empty(move_option)))
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
addition = move_option.copy()
|
||||
print("once again, the move option is:"+str(addition))
|
||||
reachables.append(addition) # add it to the list.
|
||||
print("reaching: "+str(reachables))
|
||||
else:
|
||||
hit_something = True
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print("reachables:"+str(reachables))
|
||||
return reachables
|
||||
|
||||
|
||||
# def get_reachable_squares(self, origin):
|
||||
# directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
# reachables = []
|
||||
# for direction in directions: # For each of the 8 directions...
|
||||
# move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
# hit_something = False # We will make this false if we find an obstacle.
|
||||
# while hit_something == False: # Until we hit something....
|
||||
# # move in the specified direction:
|
||||
# move_option[0] += direction[0]
|
||||
# move_option[1] += direction[1]
|
||||
# if self.in_bounds(move_option):
|
||||
# if self.is_empty(move_option):
|
||||
# # if we are still on the board and if the square is empty...
|
||||
# addition = move_option.copy()
|
||||
# reachables.append(addition) # add it to the list.
|
||||
# else:
|
||||
# hit_something = True
|
||||
# else: # If we hit the edge of the board or an obstacle...
|
||||
# hit_something = True
|
||||
# return reachables
|
||||
|
||||
|
||||
|
||||
def possible_moves(self):
|
||||
print("-----------")
|
||||
print("possible_moves")
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
for amazon in amazons:
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
# Before we burn anything, we need to empty the square we moved from
|
||||
# so we can burn it or squares past it if we want to:
|
||||
self.state[amazon[0]][amazon[1]] = 0
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
# Let's not forget to put the amazon back in its square.
|
||||
# Also, since we emptied a square, we have to reverse the logic:
|
||||
if self.get_active_player_code() == 2:
|
||||
self.state[amazon[0]][amazon[1]] = 3
|
||||
else:
|
||||
self.state[amazon[0]][amazon[1]] = 2
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
print("-----------")
|
||||
print("get_successor_state")
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
print("move_and_burn: "+str(move_and_burn))
|
||||
ai, aj = move_and_burn[0][0], move_and_burn[0][1]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = move_and_burn[1][0], move_and_burn[1][1]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = move_and_burn[2][0], move_and_burn[2][1]
|
||||
new_state[ai][aj] = 0 # The amazon's start square is emptied.
|
||||
if self.get_active_player_code() == 2: # The move square is filled.
|
||||
new_state[mi][mj] = 3
|
||||
else:
|
||||
new_state[mi][mj] = 2
|
||||
new_state[bi][bj] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
print("-----------")
|
||||
print("get_possible_successor_states")
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
if (x in range(len(self.state[0]))) and (y in range(len(self.state[0]))):
|
||||
if self.state[x][y] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
|
@ -0,0 +1,159 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for y, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for x, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
print('amazon= '+str(origin))
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction...
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print('----------------')
|
||||
print('move_option = '+str(move_option))
|
||||
# move in the specified direction...
|
||||
print('in bounds = '+str(self.in_bounds(move_option)))
|
||||
print('is empty = '+str(self.is_empty(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
reachables.append(move_option) # add it to the list.
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print('I hit something = '+str(hit_something))
|
||||
print('ok found em')
|
||||
return reachables
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
print('amazons are '+str(amazons))
|
||||
for amazon in amazons:
|
||||
print('we are on '+str(amazon))
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
print('move options are: '+str(amazon_move_options))
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
print('one down')
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = [move_and_burn[0][0]], [move_and_burn[0][1]]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = [move_and_burn[1][0]], [move_and_burn[1][1]]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = [move_and_burn[2][0]], [move_and_burn[2][1]]
|
||||
new_state[aj][ai] = 0 # The amazon's start square is emptied.
|
||||
new_state[mj][mi] = self.get_active_player_code() # The move square is filled.
|
||||
new_state[bj][bi] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
print("the square is empty: "+str(self.state[y][x]))
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
print('square = '+str(square))
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
board = Board()
|
||||
|
||||
print(Board.possible_moves(board))
|
|
@ -0,0 +1,158 @@
|
|||
class Board:
|
||||
# First we need to initialize our object with a 'state.'
|
||||
def __init__(self,):
|
||||
# While at the moment, we have a fixed initial state, in the future
|
||||
# we may want players to be able to choose where the amazons start,
|
||||
# or perhaps include an option for a random start.
|
||||
# In order to build in this future flexibility, we will use another
|
||||
# function to determine go get the initial state whih we can edit later.
|
||||
self.state = self.get_initial_state()
|
||||
# Note also that these functions are of the class, and so need to be
|
||||
# prefixed with a 'self.' so that our script will know we mean the
|
||||
# function that is defined within the our class structure.
|
||||
|
||||
def get_initial_state(self):
|
||||
# For now we will use a 4x4 board and place the amazons in the middle
|
||||
# four squares, diagonlally from eachother for symmetry.
|
||||
# Note that because the board is symettrical, one of the players must
|
||||
# have an advantage... but is it player 1 or player 2?
|
||||
return [[0,0,0,0],[0,2,3,0],[0,3,2,0],[0,0,0,0]]
|
||||
|
||||
def get_active_player_code(self):
|
||||
# It's going to be useful to know whose turn it is at any given time.
|
||||
# Luckily, because a player burns away a square each turn, we can tell
|
||||
# whose turn it is by counting how many open squares are left! They act
|
||||
# like a kind of timer, coutning down to the end of the game.
|
||||
# On turn 1 (initial state), it is player 1's turn and there are 12
|
||||
# open spaces at the start of the turn... so if the number of open
|
||||
# spaces is even, then it's player 1's turn, and if odd then player 2's.
|
||||
free_spaces = 0
|
||||
for row in self.state: #remember the state is a list of four 'rows'...
|
||||
for box in row:
|
||||
if box == 0:
|
||||
free_spaces += 1
|
||||
# The logic above only worked because we had a 4x4 board, but if we had
|
||||
# a 5x5 board, then we would start with 21 open spaces, so the logic is
|
||||
# reversed with player 1 being odd and player 2 even, so...
|
||||
if len(self.state[0])%2 == 0: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (2) # means it's player 1's turn.
|
||||
else:
|
||||
return (3) # And an odd number makes it player 2's.
|
||||
else: # If the length of the rows is even...
|
||||
if free_spaces%2 == 0: # then an even number of free spaces...
|
||||
return (3) # means it's player 2's turn.
|
||||
else:
|
||||
return (2) # And an odd number makes it player 1's.
|
||||
|
||||
def get_active_amazons_positions(self):
|
||||
code = self.get_active_player_code() #First of all, whose turn is it?
|
||||
positions=[] # This will contain the (x,y) for each of the two amazons.
|
||||
for y, row in enumerate(self.state): # 'enumerate' takes a list like:
|
||||
# ['a', 'b', 'c'] and outputs [(0,'a'), (1,'b'), (2,'c')]
|
||||
# So in this case 'y' refers to these numbers (the enumeration)
|
||||
# which correspond to the column we are interested in, and 'row'
|
||||
# refers to the row we are interested in.
|
||||
for x, box in enumerate(row): # This time we hone in on the x coordinate
|
||||
# of each entry in the row.
|
||||
if box == code: # If the actual value at that (x,y) matches whose
|
||||
# turn it is, i.e. they have an amazon there...
|
||||
positions.append((x, y)) # We add that (x,y) pair to the list.
|
||||
return positions
|
||||
|
||||
def get_reachable_squares(self, origin):
|
||||
directions = [[-1,1],[0,1],[1,1],[-1,0],[1,0],[-1,-1],[0,-1],[1,-1]]
|
||||
# From each square on the board, there are eight directions amazons can
|
||||
# move in or shoot in. These represent those eight directions. For example,
|
||||
# (1,1) refers to going to the right once and up once and (-1,-1) means
|
||||
# left and down respectively.
|
||||
reachables = []
|
||||
for direction in directions: # For each of the 8 directions...
|
||||
print("origin: "+str(origin))
|
||||
move_option = [origin[0], origin[1]] # center ourselves on the amazon.
|
||||
hit_something = False # We will make this false if we find an obstacle.
|
||||
while hit_something == False: # Until we hit something....
|
||||
# move in the specified direction:
|
||||
move_option[0] += direction[0]
|
||||
move_option[1] += direction[1]
|
||||
print("move option: "+str(move_option))
|
||||
print("It's in bounds: "+str(self.in_bounds(move_option)))
|
||||
if self.in_bounds(move_option):
|
||||
print("It's empty: "+str(self.is_empty(move_option)))
|
||||
if self.is_empty(move_option):
|
||||
# if we are still on the board and if the square is empty...
|
||||
addition = move_option.copy()
|
||||
print("once again, the move option is:"+str(addition))
|
||||
reachables.append(addition) # add it to the list.
|
||||
print("reaching: "+str(reachables))
|
||||
else:
|
||||
hit_something = True
|
||||
else: # If we hit the edge of the board or an obstacle...
|
||||
hit_something = True
|
||||
print("reachables:"+str(reachables))
|
||||
return reachables
|
||||
|
||||
def possible_moves(self):
|
||||
# Note that a "move" consists of both moving an amazon and shooting.
|
||||
# This means that a move has three values: chosen amazon's starting position,
|
||||
# the amazon's new position, and the position of the burned square.
|
||||
move_options=[]
|
||||
amazons = self.get_active_amazons_positions() # Find the amazons.
|
||||
for amazon in amazons:
|
||||
amazon_move_options = self.get_reachable_squares(amazon)
|
||||
# And where they can go...
|
||||
for move_option in amazon_move_options:
|
||||
#For each move, we see also what squares we can burn:
|
||||
burn_options = self.get_reachable_squares(move_option)
|
||||
for burn_option in burn_options:
|
||||
# Now that we have an amazon, each square it can go to, and
|
||||
# each square it can burn from each move, we have a (potentially large)
|
||||
# list of triples. Let's add them to the list and move to the
|
||||
# next amazon.
|
||||
move_options.append((amazon, move_option, burn_option))
|
||||
return move_options
|
||||
|
||||
def get_successor_state(self, move_and_burn):
|
||||
# We need to update the board based on the move, so let's grab it:
|
||||
new_state = self.state.copy()
|
||||
# These are the (x,y) coordinates of the chosen amazon's start.
|
||||
ai, aj = [move_and_burn[0][0]], [move_and_burn[0][1]]
|
||||
# These are the (x,y) coordinates of the chosen amazon's move.
|
||||
mi, mj = [move_and_burn[1][0]], [move_and_burn[1][1]]
|
||||
# These are the (x,y) corrdinates of the chosen burn square.
|
||||
bi, bj = [move_and_burn[2][0]], [move_and_burn[2][1]]
|
||||
new_state[aj][ai] = 0 # The amazon's start square is emptied.
|
||||
new_state[mj][mi] = self.get_active_player_code() # The move square is filled.
|
||||
new_state[bj][bi] = 1 # The burn square is burned.
|
||||
return (new_state)
|
||||
|
||||
def get_possible_successor_states(self):
|
||||
# This just uses the other function and applies is to every possible move.
|
||||
return [self.get_successor_state(move) for move in self.possible_moves()]
|
||||
|
||||
def is_empty(self, square):
|
||||
# Here's the function referenced in "get_reachable_squares."
|
||||
# Recall that the input is the (x,y) position of an amazon.
|
||||
x, y = square # We isolate the x and y.
|
||||
# Don't forget that while we say (x,y), the indices are refernced as [y][x]
|
||||
# since the rows are above and below eachother (y) and columns adjacent (x):
|
||||
if (x in range(len(self.state[0]))) and (y in range(len(self.state[0]))):
|
||||
if self.state[y][x] == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def in_bounds(self, square):
|
||||
# Here's the other function refernced in "get_reachable_squares."
|
||||
x, y = square
|
||||
# We need to make sure all (x,y) values are between 0 and the length of a row.
|
||||
if (x < 0) or (y < 0) or (x > len(self.state[0])) or (y > len(self.state[0])):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
board = Board()
|
||||
|
||||
print(Board.possible_moves(board))
|
|
@ -0,0 +1,40 @@
|
|||
from board import *
|
||||
from view import *
|
||||
|
||||
board = Board()
|
||||
|
||||
translator = {
|
||||
'a4':[0,0], 'a3':[1,0], 'a2':[2,0], 'a1':[3,0],
|
||||
'b4':[0,1], 'b3':[1,1], 'b2':[2,1], 'b1':[3,1],
|
||||
'c4':[0,2], 'c3':[1,2], 'c2':[2,2], 'c1':[3,2],
|
||||
'd4':[0,3], 'd3':[1,3], 'd2':[2,3], 'd1':[3,3]
|
||||
}
|
||||
|
||||
def turn():
|
||||
print("-----------")
|
||||
print("turn")
|
||||
global board
|
||||
move_chosen = False
|
||||
while move_chosen == False:
|
||||
tui(board)
|
||||
print("Valid coordinates look like: \'b2\' or \'c4\'")
|
||||
amazon = translator[(input("The starting coordinates of the amazon of your choice: "))]
|
||||
move = translator[(input("The coordinates of the square you'd like to move to: "))]
|
||||
burn = translator[(input("The coordinates of the square you'd like to set aflame: "))]
|
||||
choice = ((amazon[0],amazon[1]),move,burn)
|
||||
print(board.possible_moves())
|
||||
if choice in board.possible_moves():
|
||||
print("This is what I'm sending: "+str(choice))
|
||||
board.state = board.get_successor_state(choice)
|
||||
print("board = "+str(board.state))
|
||||
move_chosen = True
|
||||
else:
|
||||
print(str(choice)+" is not a valid move.")
|
||||
|
||||
|
||||
def play():
|
||||
while len(board.possible_moves())>0:
|
||||
turn()
|
||||
return ("Player "+str(get_active_player_code()-1)+" loses!")
|
||||
|
||||
play()
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,39 @@
|
|||
from unittest import TestCase
|
||||
from board import Board
|
||||
|
||||
class TestBoard(TestCase):
|
||||
def setUp(self):
|
||||
self.board = Board()
|
||||
self.tiny = Board()
|
||||
self.tiny.state = [[3,0], [0,2]]
|
||||
|
||||
def test_board_size_is_correct(self):
|
||||
self.assertEqual(len(self.board.state), 4)
|
||||
|
||||
def test_in_bounds_works(self):
|
||||
bad = [
|
||||
(-1, 1), (5, 1)
|
||||
]
|
||||
good = [
|
||||
(2, 2), (0, 0), (3, 3)
|
||||
]
|
||||
for square in bad:
|
||||
self.assertFalse(self.board.in_bounds(square))
|
||||
for square in good:
|
||||
self.assertTrue(self.board.in_bounds(square))
|
||||
|
||||
def test_get_initial_state(self):
|
||||
|
||||
def test_get_active_player_code(self):
|
||||
|
||||
def test_get_active_amazons_positions(self):
|
||||
|
||||
def test_possible_moves(self):
|
||||
|
||||
def test_get_successor_state(self, move_and_burn):
|
||||
|
||||
def test_get_possible_successor_states(self):
|
||||
|
||||
def test_is_empty(self, square):
|
||||
|
||||
def test_get_reachable_squares(self, amazon):
|
|
@ -0,0 +1,19 @@
|
|||
from board import *
|
||||
from os import system, name
|
||||
|
||||
def clear():
|
||||
if name == 'nt':
|
||||
_ = system('cls')
|
||||
else:
|
||||
_ = system('clear')
|
||||
|
||||
def tui(board): #This displays the board state to the user
|
||||
#clear()
|
||||
print("-----------")
|
||||
print("tui")
|
||||
print(" a b c d")
|
||||
print("4 "+str(board.state[0][0])+" "+str(board.state[0][1])+" "+str(board.state[0][2])+" "+str(board.state[0][3]))
|
||||
print("3 "+str(board.state[1][0])+" "+str(board.state[1][1])+" "+str(board.state[1][2])+" "+str(board.state[1][3]))
|
||||
print("2 "+str(board.state[2][0])+" "+str(board.state[2][1])+" "+str(board.state[2][2])+" "+str(board.state[2][3]))
|
||||
print("1 "+str(board.state[3][0])+" "+str(board.state[3][1])+" "+str(board.state[3][2])+" "+str(board.state[3][3]))
|
||||
print("It's player "+str(board.get_active_player_code()-1)+"'s turn.")
|
Loading…
Reference in New Issue