Worker placement in Orleans board game/combinations
up vote
1
down vote
favorite
I am trying to program the board game Orleans. I am planning on making a computer player that plays randomly at first. The part of the game that I'm struggling to code elegantly at the moment has to do with the planning of worker placement. I'll try to explain it in a way that doesn't require familiarity with the game. A player can have workers of 6 different types (and 1 wildcard type), and each action space requires some combination of those 6 types to activate. Each action space requires 1-3 workers of different types. I want to develop a list of all the possible action spaces that can be triggered given a player's current collection of workers, and the requirements of each action space. This is with a view to then randomly select from these later.
I have a Player
class, which has a dictionary that stores the action spaces a player has access to, and a list of workers that the player has access to. The list of actions can expand as the game progresses, and the number and type of workers can also change.
class Player:
def __init__(self):
self.workers = ["boatman", "farmer", "craftsman", "trader"]
self.action_spaces = {'farmhouse': ActionSpace(["boatman", "craftsman"]),
'village': ActionSpace(["boatman", "craftsman"]),
'university': ActionSpace(["farmer", "craftsman", "trader"]),
'castle': ActionSpace(["boatman", "farmer", "trader"]),
'monastery': ActionSpace(["scholar", "trader"]),
'wagon': ActionSpace(["farmer", "trader", "knight"]),
'ship': ActionSpace(["boatman", "farmer", "knight"]),
'guildhall': ActionSpace(["farmer", "craftsman", "knight"]),
'scriptorium': ActionSpace(["knight", "scholar"]),
'townhall': ActionSpace()}
And an action space class that codes the requirements and current workers placed on each action space.
class ActionSpace:
def __init__(self, requirements):
self.requirements = requirements
self.placed_workers =
Then I have some very tortuous code that uses that information to produce a list of the possible combinations of action spaces that can be triggered with the player's workers.
def plan_worker_placement(player):
# loop through all action spaces that a player owns and make a list of tuples recording the requirements for each action space. I convert it to a set because I will find the intersection with this set and another later
reqs_of_available_spaces = set([tuple(v.requirements) for k, v in player.action_spaces.items()])
# get a list of workers
workers = [w for w in player.workers]
combos_of_workers =
# get all possible combinations of workers for groups of size 1-3 workers
for r in range(1, 4):
combos_of_workers.extend(set(combinations(workers, r)))
# narrow down list to those action spaces that are playable given the player's workers
reqs_of_playable_spaces = reqs_of_available_spaces.intersection(combos_of_workers)
# convert back to list of lists
reqs_of_playable_spaces = [list(c) for c in reqs_of_playable_spaces]
combos_of_reqs_of_playable_spaces =
# can activate 1 or more action space
for r in range(1, len(reqs_of_playable_spaces) + 1):
combos_of_reqs_of_playable_spaces.extend(combinations(reqs_of_playable_spaces, r))
combos_of_reqs_of_playable_spaces = [list(c) for c in combos_of_reqs_of_playable_spaces]
valid_combos_of_reqs_of_playable_spaces =
for combo in combos_of_reqs_of_playable_spaces:
# flatten out the list
flat_combo = [item for sublist in combo for item in sublist]
# check whether player has enough workers of each type to play that combination of action spaces
if check_valid(flat_combo, followers):
# if the player has enough workers add that combo of action spaces to the list of possibles
valid_combos_of_reqs_of_playable_spaces.append(combo)
valid_combos_of_reqs_of_playable_spaces = [tuple(c) for c in valid_combos_of_reqs_of_playable_spaces]
return convert_reqs_to_spaces(valid_combos_of_reqs_of_playable_spaces)
# function to convert from a list of requirements back to the name of the action space
def convert_reqs_to_spaces(reqs):
converter = {tuple(sorted(["boatman", "craftsman"])): 'village',
tuple(sorted(["boatman", "farmer", "trader"])): 'castle',
tuple(sorted(["farmer", "trader", "knight"])): 'wagon',
tuple(sorted(["boatman", "farmer", "knight"])): 'ship',
tuple(sorted(["farmer", "craftsman", "knight"])): 'guildhall',
tuple(sorted(["knight", "scholar"])): 'scriptorium',
tuple(sorted(["scholar", "trader"])): 'monastery',
tuple(sorted(["farmer", "craftsman", "trader"])): 'university',
tuple(sorted(["boatman", "craftsman"])): 'farmhouse'}
spaces =
for s in reqs:
spaces.append([converter[tuple(sorted(req))] for req in s])
return spaces
# function to check to whether a player has enough of each type of worker to activate a given set of action space requirements
def check_valid(flat, followers):
c1, c2 = Counter(flat), Counter(followers)
for k, n in c1.items():
if n > c2[k]:
return False
return True
I don't like the way I am having to convert from lists to sets and back to lists again. It seems there should be a much cleaner way of doing things.
python python-3.x game combinatorics
New contributor
add a comment |
up vote
1
down vote
favorite
I am trying to program the board game Orleans. I am planning on making a computer player that plays randomly at first. The part of the game that I'm struggling to code elegantly at the moment has to do with the planning of worker placement. I'll try to explain it in a way that doesn't require familiarity with the game. A player can have workers of 6 different types (and 1 wildcard type), and each action space requires some combination of those 6 types to activate. Each action space requires 1-3 workers of different types. I want to develop a list of all the possible action spaces that can be triggered given a player's current collection of workers, and the requirements of each action space. This is with a view to then randomly select from these later.
I have a Player
class, which has a dictionary that stores the action spaces a player has access to, and a list of workers that the player has access to. The list of actions can expand as the game progresses, and the number and type of workers can also change.
class Player:
def __init__(self):
self.workers = ["boatman", "farmer", "craftsman", "trader"]
self.action_spaces = {'farmhouse': ActionSpace(["boatman", "craftsman"]),
'village': ActionSpace(["boatman", "craftsman"]),
'university': ActionSpace(["farmer", "craftsman", "trader"]),
'castle': ActionSpace(["boatman", "farmer", "trader"]),
'monastery': ActionSpace(["scholar", "trader"]),
'wagon': ActionSpace(["farmer", "trader", "knight"]),
'ship': ActionSpace(["boatman", "farmer", "knight"]),
'guildhall': ActionSpace(["farmer", "craftsman", "knight"]),
'scriptorium': ActionSpace(["knight", "scholar"]),
'townhall': ActionSpace()}
And an action space class that codes the requirements and current workers placed on each action space.
class ActionSpace:
def __init__(self, requirements):
self.requirements = requirements
self.placed_workers =
Then I have some very tortuous code that uses that information to produce a list of the possible combinations of action spaces that can be triggered with the player's workers.
def plan_worker_placement(player):
# loop through all action spaces that a player owns and make a list of tuples recording the requirements for each action space. I convert it to a set because I will find the intersection with this set and another later
reqs_of_available_spaces = set([tuple(v.requirements) for k, v in player.action_spaces.items()])
# get a list of workers
workers = [w for w in player.workers]
combos_of_workers =
# get all possible combinations of workers for groups of size 1-3 workers
for r in range(1, 4):
combos_of_workers.extend(set(combinations(workers, r)))
# narrow down list to those action spaces that are playable given the player's workers
reqs_of_playable_spaces = reqs_of_available_spaces.intersection(combos_of_workers)
# convert back to list of lists
reqs_of_playable_spaces = [list(c) for c in reqs_of_playable_spaces]
combos_of_reqs_of_playable_spaces =
# can activate 1 or more action space
for r in range(1, len(reqs_of_playable_spaces) + 1):
combos_of_reqs_of_playable_spaces.extend(combinations(reqs_of_playable_spaces, r))
combos_of_reqs_of_playable_spaces = [list(c) for c in combos_of_reqs_of_playable_spaces]
valid_combos_of_reqs_of_playable_spaces =
for combo in combos_of_reqs_of_playable_spaces:
# flatten out the list
flat_combo = [item for sublist in combo for item in sublist]
# check whether player has enough workers of each type to play that combination of action spaces
if check_valid(flat_combo, followers):
# if the player has enough workers add that combo of action spaces to the list of possibles
valid_combos_of_reqs_of_playable_spaces.append(combo)
valid_combos_of_reqs_of_playable_spaces = [tuple(c) for c in valid_combos_of_reqs_of_playable_spaces]
return convert_reqs_to_spaces(valid_combos_of_reqs_of_playable_spaces)
# function to convert from a list of requirements back to the name of the action space
def convert_reqs_to_spaces(reqs):
converter = {tuple(sorted(["boatman", "craftsman"])): 'village',
tuple(sorted(["boatman", "farmer", "trader"])): 'castle',
tuple(sorted(["farmer", "trader", "knight"])): 'wagon',
tuple(sorted(["boatman", "farmer", "knight"])): 'ship',
tuple(sorted(["farmer", "craftsman", "knight"])): 'guildhall',
tuple(sorted(["knight", "scholar"])): 'scriptorium',
tuple(sorted(["scholar", "trader"])): 'monastery',
tuple(sorted(["farmer", "craftsman", "trader"])): 'university',
tuple(sorted(["boatman", "craftsman"])): 'farmhouse'}
spaces =
for s in reqs:
spaces.append([converter[tuple(sorted(req))] for req in s])
return spaces
# function to check to whether a player has enough of each type of worker to activate a given set of action space requirements
def check_valid(flat, followers):
c1, c2 = Counter(flat), Counter(followers)
for k, n in c1.items():
if n > c2[k]:
return False
return True
I don't like the way I am having to convert from lists to sets and back to lists again. It seems there should be a much cleaner way of doing things.
python python-3.x game combinatorics
New contributor
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I am trying to program the board game Orleans. I am planning on making a computer player that plays randomly at first. The part of the game that I'm struggling to code elegantly at the moment has to do with the planning of worker placement. I'll try to explain it in a way that doesn't require familiarity with the game. A player can have workers of 6 different types (and 1 wildcard type), and each action space requires some combination of those 6 types to activate. Each action space requires 1-3 workers of different types. I want to develop a list of all the possible action spaces that can be triggered given a player's current collection of workers, and the requirements of each action space. This is with a view to then randomly select from these later.
I have a Player
class, which has a dictionary that stores the action spaces a player has access to, and a list of workers that the player has access to. The list of actions can expand as the game progresses, and the number and type of workers can also change.
class Player:
def __init__(self):
self.workers = ["boatman", "farmer", "craftsman", "trader"]
self.action_spaces = {'farmhouse': ActionSpace(["boatman", "craftsman"]),
'village': ActionSpace(["boatman", "craftsman"]),
'university': ActionSpace(["farmer", "craftsman", "trader"]),
'castle': ActionSpace(["boatman", "farmer", "trader"]),
'monastery': ActionSpace(["scholar", "trader"]),
'wagon': ActionSpace(["farmer", "trader", "knight"]),
'ship': ActionSpace(["boatman", "farmer", "knight"]),
'guildhall': ActionSpace(["farmer", "craftsman", "knight"]),
'scriptorium': ActionSpace(["knight", "scholar"]),
'townhall': ActionSpace()}
And an action space class that codes the requirements and current workers placed on each action space.
class ActionSpace:
def __init__(self, requirements):
self.requirements = requirements
self.placed_workers =
Then I have some very tortuous code that uses that information to produce a list of the possible combinations of action spaces that can be triggered with the player's workers.
def plan_worker_placement(player):
# loop through all action spaces that a player owns and make a list of tuples recording the requirements for each action space. I convert it to a set because I will find the intersection with this set and another later
reqs_of_available_spaces = set([tuple(v.requirements) for k, v in player.action_spaces.items()])
# get a list of workers
workers = [w for w in player.workers]
combos_of_workers =
# get all possible combinations of workers for groups of size 1-3 workers
for r in range(1, 4):
combos_of_workers.extend(set(combinations(workers, r)))
# narrow down list to those action spaces that are playable given the player's workers
reqs_of_playable_spaces = reqs_of_available_spaces.intersection(combos_of_workers)
# convert back to list of lists
reqs_of_playable_spaces = [list(c) for c in reqs_of_playable_spaces]
combos_of_reqs_of_playable_spaces =
# can activate 1 or more action space
for r in range(1, len(reqs_of_playable_spaces) + 1):
combos_of_reqs_of_playable_spaces.extend(combinations(reqs_of_playable_spaces, r))
combos_of_reqs_of_playable_spaces = [list(c) for c in combos_of_reqs_of_playable_spaces]
valid_combos_of_reqs_of_playable_spaces =
for combo in combos_of_reqs_of_playable_spaces:
# flatten out the list
flat_combo = [item for sublist in combo for item in sublist]
# check whether player has enough workers of each type to play that combination of action spaces
if check_valid(flat_combo, followers):
# if the player has enough workers add that combo of action spaces to the list of possibles
valid_combos_of_reqs_of_playable_spaces.append(combo)
valid_combos_of_reqs_of_playable_spaces = [tuple(c) for c in valid_combos_of_reqs_of_playable_spaces]
return convert_reqs_to_spaces(valid_combos_of_reqs_of_playable_spaces)
# function to convert from a list of requirements back to the name of the action space
def convert_reqs_to_spaces(reqs):
converter = {tuple(sorted(["boatman", "craftsman"])): 'village',
tuple(sorted(["boatman", "farmer", "trader"])): 'castle',
tuple(sorted(["farmer", "trader", "knight"])): 'wagon',
tuple(sorted(["boatman", "farmer", "knight"])): 'ship',
tuple(sorted(["farmer", "craftsman", "knight"])): 'guildhall',
tuple(sorted(["knight", "scholar"])): 'scriptorium',
tuple(sorted(["scholar", "trader"])): 'monastery',
tuple(sorted(["farmer", "craftsman", "trader"])): 'university',
tuple(sorted(["boatman", "craftsman"])): 'farmhouse'}
spaces =
for s in reqs:
spaces.append([converter[tuple(sorted(req))] for req in s])
return spaces
# function to check to whether a player has enough of each type of worker to activate a given set of action space requirements
def check_valid(flat, followers):
c1, c2 = Counter(flat), Counter(followers)
for k, n in c1.items():
if n > c2[k]:
return False
return True
I don't like the way I am having to convert from lists to sets and back to lists again. It seems there should be a much cleaner way of doing things.
python python-3.x game combinatorics
New contributor
I am trying to program the board game Orleans. I am planning on making a computer player that plays randomly at first. The part of the game that I'm struggling to code elegantly at the moment has to do with the planning of worker placement. I'll try to explain it in a way that doesn't require familiarity with the game. A player can have workers of 6 different types (and 1 wildcard type), and each action space requires some combination of those 6 types to activate. Each action space requires 1-3 workers of different types. I want to develop a list of all the possible action spaces that can be triggered given a player's current collection of workers, and the requirements of each action space. This is with a view to then randomly select from these later.
I have a Player
class, which has a dictionary that stores the action spaces a player has access to, and a list of workers that the player has access to. The list of actions can expand as the game progresses, and the number and type of workers can also change.
class Player:
def __init__(self):
self.workers = ["boatman", "farmer", "craftsman", "trader"]
self.action_spaces = {'farmhouse': ActionSpace(["boatman", "craftsman"]),
'village': ActionSpace(["boatman", "craftsman"]),
'university': ActionSpace(["farmer", "craftsman", "trader"]),
'castle': ActionSpace(["boatman", "farmer", "trader"]),
'monastery': ActionSpace(["scholar", "trader"]),
'wagon': ActionSpace(["farmer", "trader", "knight"]),
'ship': ActionSpace(["boatman", "farmer", "knight"]),
'guildhall': ActionSpace(["farmer", "craftsman", "knight"]),
'scriptorium': ActionSpace(["knight", "scholar"]),
'townhall': ActionSpace()}
And an action space class that codes the requirements and current workers placed on each action space.
class ActionSpace:
def __init__(self, requirements):
self.requirements = requirements
self.placed_workers =
Then I have some very tortuous code that uses that information to produce a list of the possible combinations of action spaces that can be triggered with the player's workers.
def plan_worker_placement(player):
# loop through all action spaces that a player owns and make a list of tuples recording the requirements for each action space. I convert it to a set because I will find the intersection with this set and another later
reqs_of_available_spaces = set([tuple(v.requirements) for k, v in player.action_spaces.items()])
# get a list of workers
workers = [w for w in player.workers]
combos_of_workers =
# get all possible combinations of workers for groups of size 1-3 workers
for r in range(1, 4):
combos_of_workers.extend(set(combinations(workers, r)))
# narrow down list to those action spaces that are playable given the player's workers
reqs_of_playable_spaces = reqs_of_available_spaces.intersection(combos_of_workers)
# convert back to list of lists
reqs_of_playable_spaces = [list(c) for c in reqs_of_playable_spaces]
combos_of_reqs_of_playable_spaces =
# can activate 1 or more action space
for r in range(1, len(reqs_of_playable_spaces) + 1):
combos_of_reqs_of_playable_spaces.extend(combinations(reqs_of_playable_spaces, r))
combos_of_reqs_of_playable_spaces = [list(c) for c in combos_of_reqs_of_playable_spaces]
valid_combos_of_reqs_of_playable_spaces =
for combo in combos_of_reqs_of_playable_spaces:
# flatten out the list
flat_combo = [item for sublist in combo for item in sublist]
# check whether player has enough workers of each type to play that combination of action spaces
if check_valid(flat_combo, followers):
# if the player has enough workers add that combo of action spaces to the list of possibles
valid_combos_of_reqs_of_playable_spaces.append(combo)
valid_combos_of_reqs_of_playable_spaces = [tuple(c) for c in valid_combos_of_reqs_of_playable_spaces]
return convert_reqs_to_spaces(valid_combos_of_reqs_of_playable_spaces)
# function to convert from a list of requirements back to the name of the action space
def convert_reqs_to_spaces(reqs):
converter = {tuple(sorted(["boatman", "craftsman"])): 'village',
tuple(sorted(["boatman", "farmer", "trader"])): 'castle',
tuple(sorted(["farmer", "trader", "knight"])): 'wagon',
tuple(sorted(["boatman", "farmer", "knight"])): 'ship',
tuple(sorted(["farmer", "craftsman", "knight"])): 'guildhall',
tuple(sorted(["knight", "scholar"])): 'scriptorium',
tuple(sorted(["scholar", "trader"])): 'monastery',
tuple(sorted(["farmer", "craftsman", "trader"])): 'university',
tuple(sorted(["boatman", "craftsman"])): 'farmhouse'}
spaces =
for s in reqs:
spaces.append([converter[tuple(sorted(req))] for req in s])
return spaces
# function to check to whether a player has enough of each type of worker to activate a given set of action space requirements
def check_valid(flat, followers):
c1, c2 = Counter(flat), Counter(followers)
for k, n in c1.items():
if n > c2[k]:
return False
return True
I don't like the way I am having to convert from lists to sets and back to lists again. It seems there should be a much cleaner way of doing things.
python python-3.x game combinatorics
python python-3.x game combinatorics
New contributor
New contributor
edited yesterday
Jamal♦
30.2k11115226
30.2k11115226
New contributor
asked yesterday
TaxpayersMoney
63
63
New contributor
New contributor
add a comment |
add a comment |
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.
TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.
TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.
TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207682%2fworker-placement-in-orleans-board-game-combinations%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown