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:
2023-06-29 12:45:50 -04:00
parent f2000deb40
commit 56f1eb564c
31 changed files with 1598 additions and 32 deletions

Binary file not shown.

View File

@@ -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))

159
amazons/board0.py Normal file
View File

@@ -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))

158
amazons/board1.py Normal file
View File

@@ -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))

View File

@@ -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()

View File

@@ -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):

View File

@@ -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.")