Worker placement in Orleans board game/combinations











up vote
2
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.










share|improve this question









New contributor




TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
























    up vote
    2
    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.










    share|improve this question









    New contributor




    TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






















      up vote
      2
      down vote

      favorite









      up vote
      2
      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.










      share|improve this question









      New contributor




      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      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






      share|improve this question









      New contributor




      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 2 days ago









      Jamal

      30.2k11115226




      30.2k11115226






      New contributor




      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 2 days ago









      TaxpayersMoney

      134




      134




      New contributor




      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      TaxpayersMoney is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          The way you explain it in plain english is similar to a simple, cleaner way to do it in the code :




          I want to develop a list of all the possible action spaces




          With some conditional restriction. This is a complete rewrite.
          Enumeration can be done through recursive algorithm :



          def enumerate_action_space(actions, available_workers):
          accumulator = [] # It is always possible to do nothing
          actions_copy = actions.copy()
          for a in actions.keys():
          # Conditionals go here. The more you restrict your space, the faster the enumeration
          if not set(actions[a].required_workers).issubset(set(available_workers)):
          continue
          # Select the action, then enumerate all possible following actions for that one
          current_action = actions_copy.pop(a)
          workers = set(available_workers) - set(current_action.required_workers)
          l = enumerate_action_space(actions_copy, workers)
          # Collect results
          l2 = [action + [a] for action in l]
          accumulator.extend(l2)
          return accumulator


          actions = {
          'key1': ActionSpace('A',['a']), # First is name, second the worker requirements
          'key2': ActionSpace('B', ['b']),
          'key3': ActionSpace('C', ['a', 'b'])
          }

          print (enumerate_action_space(actions, ['a', 'b'])) # Two workers available
          # -> [, [ActionSpace('A')], [ActionSpace('B'), ActionSpace('A')], [ActionSpace('C')], [ActionSpace('B')]] #


          You can add more conditions to restrict more the action space, based on arbitrary parameters.



          Note that I don't push back elements as I remove them. This is to avoid enumerating both ['A', 'B'] and ['B', 'A'].






          share|improve this answer










          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
            – TaxpayersMoney
            14 hours ago










          • It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
            – Arthur Havlicek
            10 hours ago













          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.










           

          draft saved


          draft discarded


















          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

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote



          accepted










          The way you explain it in plain english is similar to a simple, cleaner way to do it in the code :




          I want to develop a list of all the possible action spaces




          With some conditional restriction. This is a complete rewrite.
          Enumeration can be done through recursive algorithm :



          def enumerate_action_space(actions, available_workers):
          accumulator = [] # It is always possible to do nothing
          actions_copy = actions.copy()
          for a in actions.keys():
          # Conditionals go here. The more you restrict your space, the faster the enumeration
          if not set(actions[a].required_workers).issubset(set(available_workers)):
          continue
          # Select the action, then enumerate all possible following actions for that one
          current_action = actions_copy.pop(a)
          workers = set(available_workers) - set(current_action.required_workers)
          l = enumerate_action_space(actions_copy, workers)
          # Collect results
          l2 = [action + [a] for action in l]
          accumulator.extend(l2)
          return accumulator


          actions = {
          'key1': ActionSpace('A',['a']), # First is name, second the worker requirements
          'key2': ActionSpace('B', ['b']),
          'key3': ActionSpace('C', ['a', 'b'])
          }

          print (enumerate_action_space(actions, ['a', 'b'])) # Two workers available
          # -> [, [ActionSpace('A')], [ActionSpace('B'), ActionSpace('A')], [ActionSpace('C')], [ActionSpace('B')]] #


          You can add more conditions to restrict more the action space, based on arbitrary parameters.



          Note that I don't push back elements as I remove them. This is to avoid enumerating both ['A', 'B'] and ['B', 'A'].






          share|improve this answer










          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
            – TaxpayersMoney
            14 hours ago










          • It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
            – Arthur Havlicek
            10 hours ago

















          up vote
          1
          down vote



          accepted










          The way you explain it in plain english is similar to a simple, cleaner way to do it in the code :




          I want to develop a list of all the possible action spaces




          With some conditional restriction. This is a complete rewrite.
          Enumeration can be done through recursive algorithm :



          def enumerate_action_space(actions, available_workers):
          accumulator = [] # It is always possible to do nothing
          actions_copy = actions.copy()
          for a in actions.keys():
          # Conditionals go here. The more you restrict your space, the faster the enumeration
          if not set(actions[a].required_workers).issubset(set(available_workers)):
          continue
          # Select the action, then enumerate all possible following actions for that one
          current_action = actions_copy.pop(a)
          workers = set(available_workers) - set(current_action.required_workers)
          l = enumerate_action_space(actions_copy, workers)
          # Collect results
          l2 = [action + [a] for action in l]
          accumulator.extend(l2)
          return accumulator


          actions = {
          'key1': ActionSpace('A',['a']), # First is name, second the worker requirements
          'key2': ActionSpace('B', ['b']),
          'key3': ActionSpace('C', ['a', 'b'])
          }

          print (enumerate_action_space(actions, ['a', 'b'])) # Two workers available
          # -> [, [ActionSpace('A')], [ActionSpace('B'), ActionSpace('A')], [ActionSpace('C')], [ActionSpace('B')]] #


          You can add more conditions to restrict more the action space, based on arbitrary parameters.



          Note that I don't push back elements as I remove them. This is to avoid enumerating both ['A', 'B'] and ['B', 'A'].






          share|improve this answer










          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.


















          • That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
            – TaxpayersMoney
            14 hours ago










          • It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
            – Arthur Havlicek
            10 hours ago















          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted






          The way you explain it in plain english is similar to a simple, cleaner way to do it in the code :




          I want to develop a list of all the possible action spaces




          With some conditional restriction. This is a complete rewrite.
          Enumeration can be done through recursive algorithm :



          def enumerate_action_space(actions, available_workers):
          accumulator = [] # It is always possible to do nothing
          actions_copy = actions.copy()
          for a in actions.keys():
          # Conditionals go here. The more you restrict your space, the faster the enumeration
          if not set(actions[a].required_workers).issubset(set(available_workers)):
          continue
          # Select the action, then enumerate all possible following actions for that one
          current_action = actions_copy.pop(a)
          workers = set(available_workers) - set(current_action.required_workers)
          l = enumerate_action_space(actions_copy, workers)
          # Collect results
          l2 = [action + [a] for action in l]
          accumulator.extend(l2)
          return accumulator


          actions = {
          'key1': ActionSpace('A',['a']), # First is name, second the worker requirements
          'key2': ActionSpace('B', ['b']),
          'key3': ActionSpace('C', ['a', 'b'])
          }

          print (enumerate_action_space(actions, ['a', 'b'])) # Two workers available
          # -> [, [ActionSpace('A')], [ActionSpace('B'), ActionSpace('A')], [ActionSpace('C')], [ActionSpace('B')]] #


          You can add more conditions to restrict more the action space, based on arbitrary parameters.



          Note that I don't push back elements as I remove them. This is to avoid enumerating both ['A', 'B'] and ['B', 'A'].






          share|improve this answer










          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          The way you explain it in plain english is similar to a simple, cleaner way to do it in the code :




          I want to develop a list of all the possible action spaces




          With some conditional restriction. This is a complete rewrite.
          Enumeration can be done through recursive algorithm :



          def enumerate_action_space(actions, available_workers):
          accumulator = [] # It is always possible to do nothing
          actions_copy = actions.copy()
          for a in actions.keys():
          # Conditionals go here. The more you restrict your space, the faster the enumeration
          if not set(actions[a].required_workers).issubset(set(available_workers)):
          continue
          # Select the action, then enumerate all possible following actions for that one
          current_action = actions_copy.pop(a)
          workers = set(available_workers) - set(current_action.required_workers)
          l = enumerate_action_space(actions_copy, workers)
          # Collect results
          l2 = [action + [a] for action in l]
          accumulator.extend(l2)
          return accumulator


          actions = {
          'key1': ActionSpace('A',['a']), # First is name, second the worker requirements
          'key2': ActionSpace('B', ['b']),
          'key3': ActionSpace('C', ['a', 'b'])
          }

          print (enumerate_action_space(actions, ['a', 'b'])) # Two workers available
          # -> [, [ActionSpace('A')], [ActionSpace('B'), ActionSpace('A')], [ActionSpace('C')], [ActionSpace('B')]] #


          You can add more conditions to restrict more the action space, based on arbitrary parameters.



          Note that I don't push back elements as I remove them. This is to avoid enumerating both ['A', 'B'] and ['B', 'A'].







          share|improve this answer










          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          share|improve this answer



          share|improve this answer








          edited 10 hours ago





















          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.









          answered 16 hours ago









          Arthur Havlicek

          2713




          2713




          New contributor




          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.





          New contributor





          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          Arthur Havlicek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.












          • That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
            – TaxpayersMoney
            14 hours ago










          • It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
            – Arthur Havlicek
            10 hours ago




















          • That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
            – TaxpayersMoney
            14 hours ago










          • It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
            – Arthur Havlicek
            10 hours ago


















          That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
          – TaxpayersMoney
          14 hours ago




          That is really clever, thank you. I've implemented in my wider code and it works a treat. Shall have to properly get my head around what it's doing. Thanks for taking the time to take a look at my code.
          – TaxpayersMoney
          14 hours ago












          It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
          – Arthur Havlicek
          10 hours ago






          It's not so easy to reverse-engineer recursive code, but this one is hard to write iteratively. It's a tree search, basically, where in the tree, at each level (each recursion level) you consider each possible action (the for loop) to play after the firsts you have already selected (the successive current_actions in the call stack).
          – Arthur Havlicek
          10 hours ago












          TaxpayersMoney is a new contributor. Be nice, and check out our Code of Conduct.










           

          draft saved


          draft discarded


















          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.















           


          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Costa Masnaga

          Fotorealismo

          Sidney Franklin