def get_intel_options(G, card, *targets): opts = xset() # print('intel') if card.intelligence == 'Coup': for target in targets: topts = xset(G.objects.table[iid].nation for iid in G.players[target].influence) if len(topts): opts.add((target, topts)) elif card.intelligence == 'Agent': for target in targets: topts = xset(unit.tile for unit in G.players[target].units.values()) if len(topts): opts.add((target, topts)) elif card.intelligence == 'Spy_Ring': opts.update(target for target in targets if len(G.players[target].hand)) elif card.intelligence == 'Sabotage': opts.update(target for target in targets if G.players[target].tracks.IND > 0) elif card.intelligence == 'Code_Break': opts.update(target for target in targets if len(G.players[target].hand)) elif card.intelligence == 'Mole': opts.update(target for target in targets if len(G.players[target].secret_vault)) else: raise Exception('Unknown intelligence card type: {}'.format(card.intelligence)) return opts
def check_revealable(G, player): options = xset() faction = G.players[player] if len(faction.secret_vault): # options.add(('reveal', xset(faction.secret_vault))) options.add((xset(faction.secret_vault), )) return options
def encode_government_actions(G): code = adict() active_player = G.game.turn_order[G.temp.active_idx] faction = G.players[active_player] hand = adict({ID: G.objects.table[ID] for ID in faction.hand}) action_cards = adict( (k, v) for k, v in hand.items() if v.obj_type == 'action_card') invest_cards = adict( (k, v) for k, v in hand.items() if v.obj_type == 'investment_card') options = xset() # pass options.add(('pass', )) # diplomacy options for ID, card in action_cards.items(): if 'top' in card: lopts = xset(nation for nation in [card.top, card.bottom] if nation in G.diplomacy.neutrals) if len(lopts): options.add((ID, lopts)) elif 'wildcard' in card: wopts = check_wildcard(G, active_player, card) if len(wopts): options.add((ID, wopts)) else: raise Exception('Unknown action card properties: {}'.format( card.keys())) # factory upgrade options # print([(i, c.value) for i, c in invest_cards.items()]) total_val = sum(c.value for c in invest_cards.values()) if G.temp.past_upgrades[ active_player] < 2 and total_val >= faction.stats.factory_cost: options.add(('factory_upgrade', )) # for combo in factory_upgrade_combos(invest_cards, faction.stats.factory_cost): # options.add(('factory_upgrade',) + combo) # tech options options.update(check_techs(G, active_player, invest_cards)) # espionage options options.update(check_intelligence(G, active_player, invest_cards)) # reveal techs from secret vault options.update(check_revealable(G, active_player)) # removable inf rmb = removable_nations(G, active_player) if len(rmb): options.add(('remove', rmb)) code[active_player] = options return code
def check_declarations(G, player): options = xset() # wars options.add( (xset(name for name, war in G.players[player].stats.at_war_with.items() if not war), )) # neutral options.add((xset(G.diplomacy.neutrals.keys()), )) return options
def encode_intel_response(G, intel_card, player, target): code = adict() options = xset() options.add(('accept', )) msg = None #'{} plays {} and is targetting you'.format(player, intel_card.intelligence) for cid in G.players[target].hand: card = G.objects.table[cid] if 'intelligence' in card and card.intelligence == 'Double_Agent': opts = get_intel_options(G, intel_card, player) if len(opts): # msg = 'You may play your Double Agent to reverse the effect of {} played by {}'.format(intel_card.intelligence, player) msg = 'You may play your Double Agent to reverse the effect' options.add((cid, opts)) break G.logger.write('{} played {} targetting {}, waiting for a response'.format( player, intel_card.intelligence, target)) if msg is not None: G.logger.write(msg, player=target) code[target] = options # code[player] = xset(('cancel',)) # original player can cancel return code
def convert_from_saveable(data): if data is None: return None if isinstance(data, (str, int, float)): try: return int(data) except: pass return data if isinstance(data, dict): if len(data) == 1: key, val = next(iter(data.items())) if 'set' == key: return tset(convert_from_saveable(el) for el in val) if 'xset' == key: return xset(convert_from_saveable(el) for el in val) if 'tuple' == key: return tuple(convert_from_saveable(el) for el in val) if '_object_' == key[:8]: typ = eval(key[8:]) obj = typ() obj.load_state(val) return obj if '_id' in data: return idict({convert_from_saveable(k): convert_from_saveable(v) for k, v in data.items()}) return tdict({convert_from_saveable(k): convert_from_saveable(v) for k, v in data.items()}) if isinstance(data, list): return tlist(convert_from_saveable(el) for el in data) try: return data.save_state() except AttributeError: raise Exception('Cannot save data of type: {}'.format(type(data)))
def powers_present(G, tile): powers = xset() for uid in tile.units: unit = G.objects.table[uid] owner = G.nations.designations[unit.nationality] powers.add(owner) return powers
def encode_movement(G): player = G.temp.order[G.temp.active_idx] faction = G.players[player] cmd = G.temp.commands[player] assert cmd.value > 0, 'No more commands - shouldve moved on to next player' options = xset() options.add(('pass', )) if len(G.temp.has_moved) == 0 and not ( 'emergency' in cmd): # no units have been moved yet -> can make declarations options.update(check_declarations(G, player)) for uid, unit in faction.units.items(): if uid in G.temp.has_moved: continue locs = travel_options(G, unit) if len(locs): options.add((unit._id, locs)) # reveal techs in secret vault options.update(check_revealable(G, player)) code = adict() code[player] = options return code
def increment_influence(G, player, nation): if nation not in G.diplomacy.influence: inf = idict() inf.value = 1 inf.nation = nation inf.faction = player inf.obj_type = 'influence' inf.visible = xset(G.players.keys()) G.players[player].influence.add(inf._id) G.diplomacy.influence[nation] = inf G.objects.table[inf._id] = inf G.objects.created[inf._id] = inf return inf = G.diplomacy.influence[nation] if player != inf.faction and inf.value == 1: del G.diplomacy.influence[nation] G.players[inf.faction].influence.remove(inf._id) del G.objects.table[inf._id] G.objects.removed[inf._id] = inf return delta = (-1) ** (player != inf.faction) inf.value += delta G.objects.updated[inf._id] = inf
def get_removable_nations(G, player): nations = xset() for rival in G.players[player].stats.rivals: for iID in G.players[rival].influence: nations.add(G.objects.table[iID].nation) return nations
def encode_options_for_next_battle(G, player): #player = G.temp.order[G.temp.active_idx] code = adict() options = xset() for b in G.temp.combat.battles_remaining: options.add((b,)) #print('* * select next battles_to_fight', options) code[player] = options return code
def check_declarations(G, player): options = xset() # wars options.add( (xset(name for name, war in G.players[player].stats.at_war_with.items() if not war and not name in G.temp.threats), )) # neutral nations = xset(nat for nat, info in G.nations.status.items() if not info.is_armed and not nat in G.temp.threats) # nations -= G.players[player].diplomacy.violations #print('check_declarations nations ', nations) options.add((nations, )) return options
def get_adjacent_nations(G, *players): nations = xset() for player in players: for tilename in G.players[player].territory: for neighbor in G.tiles[tilename].borders.keys(): tile = G.tiles[neighbor] if 'alligence' in tile: nations.add(tile.alligence) return nations
def removable_nations(G, player): nations = xset() for iid in G.players[player].influence: inf = G.objects.table[iid] nations.add(inf.nation) return nations
def encode_battles_to_select(G, player): #player = G.temp.order[G.temp.active_idx] code = adict() options = xset() options.add(('pass',)) for b in G.temp.combat.battles_to_select: options.add((b,)) #print('* * vor code[player]=options', options) code[player] = options return code
def check_wildcard(G, player, card): name = card.wildcard info = G.cards.info.action[name] options = xset() if 'adjacent' in info: # either guarantee or intimidation if info.from_self: # intimidation options.update(get_adjacent_nations(G, player)) else: # guarantee options.update( get_adjacent_nations(G, *G.players[player].stats.rivals)) options.discard('USA') for name in G.players: options -= G.nations.groups[name] elif 'options' in info: # contains 'options' if 'from_self' in info: # personalized options if info.from_self: # TtB, ET, BF, BA, V options.update(info.options[player]) else: # F&L for rival in G.players[player].stats.rivals: options.update(info.options[rival]) else: # isolationism options.update(info.options) elif name == 'Foreign_Aid': options.update(G.diplomacy.minors) options.update(G.diplomacy.majors) else: raise Exception('Unknown wildcard type: {}, {}'.format( name, list(info.keys()))) options = options.intersection(G.diplomacy.neutrals) # filter out options depending on add/remove if 'add' in info and 'remove' in info: return options assert 'add' in info or 'remove' in info, 'Cant do anything, card info is missing something for {}'.format( name) removable = get_removable_nations(G, player) if 'remove' not in info: options -= removable if 'add' not in info: options *= removable return options
def choose_2_from(all): options = xset() while len(all): all = all.copy() x = all.pop() if len(all) > 1: options.add((x, all)) else: options.add((x, all.pop())) return options
def check_intelligence(G, player, cards): options = xset() for cid, card in cards.items(): if 'intelligence' not in card or card.intelligence == 'Double_Agent': continue opts = get_intel_options(G, card, *G.players[player].stats.rivals) if len(opts): options.add((cid, opts)) return options
def encode_command_card_phase(G): code = adict() player = G.temp.active_players[G.temp.active_idx] options = xset() options.add('pass') options.update(G.players[player].hand) # options.update(cid for cid in G.players[player].hand if 'action' in G.objects.table[cid].obj_type) code[player] = (options, ) return code
def encode_factory_upgrade_actions(G): code = adict() active_player = G.game.turn_order[G.temp.active_idx] faction = G.players[active_player] options = xset(ID for ID in faction.hand if G.objects.table[ID].obj_type == 'investment_card') # options -= G.temp.factory_upgrade.selects # can unselect cards options.add('cancel') code[active_player] = (options,) return code
def supply_phase(G, player, action): for pl in G.players: faction = G.players[pl] goals = xset(faction.cities.SubCapitals) goals.add(faction.cities.MainCapital) if not faction.stats.at_war: G.logger.write( '{} is at peace, so all units are supplied'.format(pl)) continue u_to_remove = [] for uid, unit in faction.units.items(): if unit.type == 'Fortress' or G.units.rules[unit.type].type != 'G': continue supplied = find_path(G, unit.tile, goals=goals, player=pl, isUnitLoc=True) tile = G.tiles[unit.tile] if not supplied: unit.cv -= 1 if unit.cv == 0: u_to_remove.append(unit) msg = 'was eliminated' #remove_unit(G, unit) # TODO: check for forced retreats of ANS else: msg = 'lost 1 cv' G.objects.updated[uid] = unit G.logger.write( '{} unit in {} {} due to lack of supplies'.format( pl, unit.tile, msg)) elif 'unsupplied' in tile and pl in tile.unsupplied: tile.unsupplied.remove(pl) if len(tile.unsupplied) == 0: del tile.unsupplied G.objects.updated[tile._id] = tile for unit in u_to_remove: remove_unit(G, unit) raise PhaseComplete
def format_out_message(player): if player not in WAITING_OBJS: return REPEATS[player] out = WAITING_OBJS[player] del WAITING_OBJS[player] if player in WAITING_ACTIONS: out.actions = WAITING_ACTIONS[player] else: out.waiting_for = xset(WAITING_ACTIONS.keys()) out.log = G.logger.pull(player) REPEATS[player] = out return out
def load_unit_rules(G, unit_rules_path='config/units.yml', unit_count_path='config/unit_count.yml'): unit_rules = load(unit_rules_path) unit_count = load(unit_count_path) G.units = adict() G.units.rules = unit_rules G.units.placeable = xset(name for name, rules in unit_rules.items() if 'not_placeable' not in rules) G.units.priorities = [ n for n, _ in sorted(unit_rules.items(), key=lambda x: x[1].priority) ] G.units.reserves = unit_count
def placeable_units(G, player, nationality, tile_options): # Groups: in land, no fortress, not supplied, reserves = xset(ut for ut in G.units.placeable if ut in G.units.reserves[nationality] and G.units.reserves[nationality][ut] > 0) base = adict({ 'unsupplied': xset('Fortress'), (False, False): reserves, (True, False): xset(ut for ut in reserves if ut != 'Fortress'), (False, True): xset(ut for ut in reserves if G.units.rules[ut].type not in {'N', 'S'}), # in_land_no_fort = xset(ut for ut in in_land if ut in no_fortress), }) base[True, True] = base[True, False].intersection(base[False, True]) # make sure territory is undisputed options = adict() for tilename in tile_options: tile = G.tiles[tilename] if 'disputed' in tile: continue has_fortress = contains_fortress(G, tile) in_land = tile.type == 'Land' # not including coast unsupplied = 'unsupplied' in tile and player in tile.unsupplied cond = has_fortress, in_land if unsupplied: cond = 'unsupplied' if unsupplied and has_fortress: continue # add new options based on cond if cond not in options: options[cond] = xset(), base[cond] options[cond][0].add(tilename) return xset(options.values())
def format_out_message(player): if player not in WAITING_OBJS: return REPEATS[player] out = WAITING_OBJS[player] del WAITING_OBJS[player] if player in WAITING_ACTIONS: out.actions = WAITING_ACTIONS[player] else: #@playerChange out.waiting_for = xset(WAITING_ACTIONS.keys()) out.log = G.logger.pull(player) if 'temp' in G: out.temp = G.temp #@@@ added this to test extra battle info!!!!!!!!!! else: out.temp = adict() REPEATS[player] = out return out
def encode_post_gov_actions(G): code = adict() for player, is_active in G.temp.move_to_post.items(): faction = G.players[player] handsize = len(faction.hand) if not is_active and handsize <= faction.stats.handlimit: continue options = xset() if handsize > faction.stats.handlimit: for cid in faction.hand: options.add((cid, )) # options.update(faction.hand.copy()) # # options = xset((options,)) if is_active: options.add(('accept', )) # reveal techs from secret vault options.update(check_revealable(G, player)) # removable nations options.add((removable_nations(G, player), )) code[player] = options for player in G.players: if player not in code and player in G.temp.move_to_post: del G.temp.move_to_post[player] return code
def encode_setup_actions(G): code = adict() for faction, nationality, tilenames in seq_iterate(G.temp.setup, [None, 'cadres', None], end=True): # if player is not None and faction != player: # continue options = placeable_units(G, faction, nationality, tilenames) if len(options) == 0: continue if faction not in code: code[faction] = xset() code[faction].add((nationality, options)) if len(code) == 0: raise PhaseComplete return code
def add_battles_to_reveal(G, player): #turn all units to be visible on each tile in G.temp.battles_to_reveal #moves revealed battles from battles_to_reveal to battles_to_fight #print('battles are revealed...') c = G.temp.combat for tile in c.battles_to_reveal: # atk = c.battles_to_reveal[tile] #print('...', tile, type(tile)) units = G.tiles[tile].units c.battles[tile] = adict() c.battles_remaining.append(tile) b = c.battles[tile] b.tilename = tile b.tile = G.tiles[tile] b.isSeaBattle = b.tile.type in ['Sea', 'Ocean'] #find owners of units on that tile #TODO: replace by powers_present!!! owners = [k for k in powers_present(G, b.tile)] if len(owners) != 2: #TODO implement 3 way battles #if this is the defender, needs to choose who to attack #for now assume that there must be exactly 2 parties in a combat c.ERROR = 'NOT SUPPORTED: COMBAT WITH POWERS != 2 ' + b.tilename pass b.owner = b.tile.owner if 'owner' in tile else None uidsList = [k for k in units] if not b.owner: uid = uidsList[0] b.owner = find_unit_owner(G, G.objects.table[uid]) b.intruder = owners[1] if b.owner == owners[0] else owners[0] assert G.temp.attacker == player, 'ATTACKER != PLAYER!!!!!!' b.attacker = G.temp.attacker #GANZ SICHER RICHTIG b.defender = owners[1] if b.attacker == owners[0] else owners[0] b.units = [] for id in units: unit = G.objects.table[id] unit.visible = xset(G.players.keys()) G.objects.updated[id] = unit attacker = b.attacker defender = b.defender uowner = find_unit_owner(G, unit) uopponent = defender if uowner == attacker else attacker utype = unit.type ugroup = G.units.rules[utype].type upriority = G.units.rules[utype].priority udamage = G.units.rules[utype] uid = id u_battle_group = None if b.isSeaBattle and b.tilename in G.temp.battle_groups and uid in G.temp.battle_groups[b.tilename]: u_battle_group = G.temp.battle_groups[b.tilename][uid] uff = False prec_bomb = False air_def_radar = False sonar = False u = adict() u.unit = unit u.owner = uowner u.group = ugroup u.priority = upriority u.rules = udamage u.id = uid u.battle_group = u_battle_group #modifying cards uff = hasFirstFire(G, uowner, utype) if uowner in G.players: tech = G.players[player].technologies #determine IND damage of AirForce: if utype == 'AirForce' and 'Jets' in tech: prec_bomb = True # air defense radar: #Air Forces in Friendly Territory (1.14) Fire doubledice (two dice/CV) at Enemy Air if uowner == b.owner and utype == 'AirForce' and 'Air_Defense_Radar' in tech: air_def_radar = True #sonar if utype == 'Fleet' and 'Sonar' in tech: sonar = True u.ff = uff u.sonar = sonar u.air_def_radar = air_def_radar u.prec_bomb = prec_bomb u.type = unit.type #if owner of this unit is defender he goes first by default #u.turn 0: this unit goes before same type of opponent, otherwise 1 u.turn = 0 opp_has_ff = hasFirstFire(G, uopponent, utype) uHasFF = False if uowner == 'Minor': if not opp_has_ff: uHasFF = True elif uopponent == 'Minor': if u.ff: uHasFF = True elif uowner == attacker and G.players[uowner].stats.DoW[uopponent] and not opp_has_ff: uHasFF = True elif uowner == attacker and u.ff and not opp_has_ff: uHasFF = True elif uowner == defender and (u.ff or (not opp_has_ff and not G.players[uopponent].stats.DoW[uowner])): uHasFF = True u.turn = 0 if uHasFF else 1 b.units.append(u) unitsSorted = sorted(b.units, key=lambda u: u.battle_group if u.battle_group else 'zzz') if b.isSeaBattle else b.units b.fire_order = sorted(unitsSorted, key=lambda u: u.priority * 10 + u.turn) c.battles_to_reveal.clear()
def get_enemies(G, player): enemies = xset(['Minor', 'Major']) wars = G.players[player].stats.at_war_with enemies.update([p for p in wars if wars[p]]) return enemies
def governmnet_phase(G, player, action): # play cards if action is None: # prep influence G.logger.write('Beginning Government Phase') if 'temp' in G: del G.temp G.temp = tdict() G.temp.diplomacy = tdict() G.temp.diplomacy_cards = tset() G.temp.intel = tdict() G.temp.past_upgrades = tdict() for player in G.players: G.temp.past_upgrades[player] = 0 G.temp.passes = 0 G.temp.active_idx = 0 return encode_government_actions(G) if 'move_to_post' in G.temp: # after phase has ended and only clean up is necessary return government_post_phase(G, player, action) # TODO: make sure cards that should now be visible stay visible if player in G.temp.intel: # hide any temporarily visible objects from intel cards for ID, obj in G.temp.intel[player].items(): obj.visible.discard(player) G.objects.updated[ID] = obj del G.temp.intel[player] if 'hack' in G.temp: if player == G.temp.hack.source: raise NotImplementedError assert action == ( 'cancel', ), 'Misunderstood action: {}'.format(action) G.players[player].hand.add( G.temp.hack.card._id ) # make sure card is back in sources's ownership del G.temp.hack return encode_government_actions(G) else: actions = resolve_intel(G, player, action) if actions is not None: return actions elif 'mole' in G.temp: if action != ('accept', ): _, tech, _ = action G.logger.write('{} uses their mole to achieve {}'.format( player, tech), player=G.temp.mole) achieve_tech(G, player, *action) del G.temp.mole elif 'factory_upgrade' in G.temp: if action == ('cancel', ): G.logger.write('Cancelled factory upgrade', player=player) del G.temp.factory_upgrade return encode_government_actions(G) ID, = action val = G.objects.table[ID].value if ID in G.temp.factory_upgrade.selects: val = -val G.temp.factory_upgrade.selects.discard(ID) G.logger.write('Unselected {}'.format(ID), end='', player=player) else: G.temp.factory_upgrade.selects.add(ID) G.logger.write('Selected {}'.format(ID), end='', player=player) G.temp.factory_upgrade.value += val G.logger.write(' (value so far: {}/{})'.format( G.temp.factory_upgrade.value, G.players[player].stats.factory_cost), player=player) # print(G.temp.factory_upgrade.value) if G.temp.factory_upgrade.value < G.players[player].stats.factory_cost: return encode_factory_upgrade_actions(G) # factory upgrade complete G.players[player].tracks.IND += 1 G.temp.past_upgrades[player] += 1 G.players[player].hand -= G.temp.factory_upgrade.selects G.logger.write( '{} upgrades their IND to {} with factory card values of: {}'. format( player, G.players[player].tracks.IND, ', '.join( str(G.objects.table[ID].value) for ID in G.temp.factory_upgrade.selects))) discard_cards(G, *G.temp.factory_upgrade.selects) del G.temp.factory_upgrade G.temp.passes = 0 elif action == ('pass', ): G.logger.write('{} passes'.format(player)) G.temp.passes += 1 if G.temp.passes == len(G.players): G.logger.write( 'All players have passed consecutively - moving on to Government resolution' ) G.temp.move_to_post = tdict() # for handsize limit options for name, faction in G.players.items(): handsize = len(faction.hand) diff = handsize - faction.stats.handlimit if diff > 0: G.logger.write('{} must discard {} cards'.format( name, diff)) G.temp.move_to_post[name] = True return government_post_phase(G) else: # execute action head, *tail = action if head == 'factory_upgrade': G.temp.factory_upgrade = tdict() G.temp.factory_upgrade.value = 0 G.temp.factory_upgrade.selects = tset() G.logger.write('Select the cards to use for the factory upgrade', player=player) return encode_factory_upgrade_actions(G) elif head in G.players[ player].secret_vault: # reveal tech from secret vault reveal_tech(G, player, head) return encode_government_actions(G) elif head == 'remove': nation, = tail decrement_influence(G, nation) G.logger.write('{} removes one of their influence from {}'.format( player, nation)) return encode_government_actions(G) elif head in {'open', 'secret'}: G.temp.passes = 0 achieve_tech(G, player, head, *tail) else: G.temp.passes = 0 card = G.objects.table[head] if 'wildcard' in card: nation, = tail increment_influence(G, player, nation) extra = '' if card.wildcard == 'Foreign_Aid': # pay IND G.players[player].tracks.IND -= 1 extra = ' (decreasing IND to {})'.format( G.players[player].tracks.IND) G.logger.write( '{} plays {} adding/removing influence in {}{}'.format( player, card.wildcard, nation, extra)) discard_cards(G, head) elif 'intelligence' in card: discard_cards(G, head) return play_intel(G, player, card, *tail) else: nation, = tail play_diplomacy(G, player, nation) G.temp.diplomacy_cards.add(head) card.visible = xset(G.players.keys()) # visible to everyone G.players[card.owner].hand.discard(card._id) del card.owner G.objects.updated[head] = card # discard_cards(G, head) G.temp.active_idx += 1 G.temp.active_idx %= len(G.players) return encode_government_actions(G)