def attacked_prob(self, province, nation, force, bias, adjacent_units): ''' How likely the province is to be attacked by an enemy of the nation, with at least the indicated force. '''#''' result = 0 if nation != self.power: entering = False supports = [] for unit in self.friends(province, adjacent_units): order = self.unit_orders.get_order(unit) if order.is_moving(): if order.destination.province.key == province.key: entering = True elif (order.is_supporting() and order.destination.province.key == province.key and order.supported.location.province.key != province.key): supports.append(1 - self.success_prob(order, bias, adjacent_units)) if force == 1: result = entering and 1 or 0 elif len(supports) >= force - 1: # Will my supports work? result = 1 - (bias and min or max)([reduce(mul, sl, 1) for sl in sublists(supports) if len(sl) == force - 1]) if bias: # Allow mutual enemies to help out enemies = self.enemies(province, nation, adjacent_units) # Todo: Consider whether they cooperate, ever if len(enemies) >= force: result = 1 return result
def generate_move_combos(self): adj_provs = defaultdict(list) for unit in self.power.units: for key in unit.location.borders_out: dest = self.map.locs[key] if not any(other.nation == self.power for other in dest.province.units): adj_provs[key[1]].append(MoveOrder(unit, dest)) return [OrderCombo(self, combo) for value in adj_provs.values() for combo in sublists(value) if combo]
def get_children(self, new_orders): # Support functions def valid(order_list): ''' Checks for multiple orders to a single unit, or empty lists. Todo: Check for convoy matching. '''#''' if not order_list: return False keys_seen = [] for order in order_list: key = order.unit.key if key in keys_seen: return False keys_seen.append(key) return True def attackers(province, friendly=self.player.power.key, spaces=self.player.map.spaces): ''' Compiles a list of enemy units that can enter the province.''' bordering = [spaces[prov].units for prov in province.borders_in] return [u for u in sum(bordering, []) if u and u.nation != friendly and u.can_move_to(province)] def occupied(province, friendly=self.player.power.key): ''' Determines whether an enemy is in a space.''' return any(unit.nation != friendly for unit in province.units) # Determine who can still be ordered unordered = [u for u in self.player.power.units if not self.unit_orders.get_order(u)] holding = [order.unit for order in self.unit_orders if order.is_holding()] if not (unordered or holding): return [] # Collect orders related to the new orders sub_orders = [] enter_provs = [] for order in new_orders: if order.is_moving(): if (occupied(order.destination.province) or len(attackers(order.destination.province)) > 0): # Try to support the move sub_orders.extend([ SupportMoveOrder(u, order.unit, order.destination) for u in unordered + holding if u.can_move_to(order.destination.province)]) # Try to enter the vacated space enter_provs.append(order.unit.location.province.key) else: enemies = attackers(order.unit.location.province) if (len(enemies) > 1): # Try to support the stationary unit sub_orders.extend([SupportHoldOrder(u, order.unit) for u in unordered + holding if u.can_move_to(order.unit.location.province)]) # Try to cut support for attacks enter_provs.extend([u.location.province.key for u in enemies]) if (order.is_supporting() and order.supported.location != order.destination and occupied(order.destination.province)): # Try to cut support to the attacked unit enter_provs.extend([u.location.province.key for u in attackers(order.destination.province)]) # Todo: Try to block retreats # Try to enter indicated provinces sub_orders.extend([MoveOrder(u, self.player.map.locs[key]) for u in unordered for key in u.location.borders_out if key[1] in enter_provs]) # Try to convoy to the indicated provinces sub_orders.extend(self.convoys_to(enter_provs, unordered, holding)) # Run all possible combinations of the related orders return [OrderCombo(self, order_list) for order_list in sublists(sub_orders) if valid(order_list)]