fai 'new_recruitment.fai' {ai/formula/lib/map_evaluation.fai} {ai/formula/lib/util.fai} {ai/formula/lib/recruitment.fai} # Returns a list of terrain types of the input list locations # def get_terrain_types(ai*, locations) map( locations, 'l', fast_map[l].id ) where fast_map = location_to_terrain_map(ai); # Returns a List of important vilages which are inside locations_map # def get_villages_on_terrain_map( ai*, locations_map ) filter( villages, locations_map[self] ); # Returns location_map with the area around important villages set to 1000 (radius 1) # def mark_important_locations(ai*, location_map ) map( location_map, if( tmp_villages[key], 1000, value ) ) where tmp_villages = tomap( get_locations_surroundings( ai, get_villages_on_terrain_map( ai, location_map ), 1 ) ); # Returns a list of ALL keys(locs) of side_terrain # def get_important_locations(ai* ) keys( map( ai.vars.side_terrain, value >= 1000 ) ); # Returns a list of all enemy leaders # def enemy_leaders( ai* ) sum( map( enemies, 'enemy', filter( units_of_side[enemy], leader ) ) ); # Returns a Map # # Key is a ememy leader # # Value is the summed distace to all important hexes # def find_enemies_part( ai*, locations_map) tomap( enemy_leaders, map( enemy_leaders, 'leader', sum( map( keys(locations_map), 'location', distance_between( location, leader.loc ) ) ) ) ) where enemy_leaders = enemy_leaders( ai ); # Returns a List of those enemy leaders wich are near to the important locations # def find_enemies( ai*, locations_map) filter( keys(enemies_part), 'leader', enemies_part[leader] <= average( values( enemies_part ) ) ) where enemies_part = find_enemies_part( ai, locations_map ); # Returns a List of all units of direct_enemies. # def direct_enemies_units( ai* ) sum( map( vars.direct_enemies, 'enemy', units_of_side[enemy.side] ) ); # Returns a list of all recruits of direct_enemies. # def direct_enemies_recruits( ai* ) sum( map( vars.direct_enemies, 'enemy', recruits_of_side[enemy.side] ) ); # returns map of important locations and number of their occurences on a gamemap # # for example: [ 'forest' -> 10 ] # # all terrains inside side_terrain are considered. # def important_locations_map(ai*) tomap(get_terrain_types(ai, get_important_locations(ai))); # Returns a map. Key is unit-id of my_recruits. Value is sum of all defenses in all important_locs. # def my_recruits_defense(ai*) tomap( map(my_recruits, id), map( my_recruits, 'recruit', sum(values(map( important_locs, defense_on(recruit, id_translator[key]) * value ))) ) ) where important_locs = important_locations_map(ai), id_translator = id_to_location_map(ai); # Returns a map. Key is unit-id of my_recruits. Value is sum of all movent_costs in all important_locs. # def my_recruits_movement_cost(ai*) tomap( map(my_recruits, id), map( my_recruits, 'recruit', sum(values(map( important_locs, movement_cost(recruit, id_translator[key]) * value ))) ) ) where important_locs = important_locations_map(ai), id_translator = id_to_location_map(ai); # Highest Value is set to 100. All others below 100 according to ratio # def locally_normalize_to_highest( input_map ) map( input_map, if(max=0,100,(value * 100)/max) ) where max = highest_value(input_map); # Lowest value is set to 100. All others over 100 according to ratio # def locally_normalize_to_lowest( input_map ) map( input_map, if(min=0,100,(value * 100)/min) ) where min = lowest_value(input_map); # Returns a List of all enemy leaders # # (for some reason this function is defined twice) # def enemy_leaders(ai*) map( enemies, 'enemy_side', find(units_of_side[enemy_side], leader ) ); # UNUSED FUNCTION # # Returns a Map. # # Key is a enemy # # Value is the distance between my leader and enemies leader # def distance_to_enemies(ai*) tomap( enemies, map( enemy_leaders(ai), distance_between( my_leader.loc, loc ) )); # Returns a list of enemy-units # # If number of units on the map is below 5 # # It'll return the recruits # def current_enemies(ai*) if( en_units.size > 5, en_units, en_recruis ) where en_units = direct_enemies_units(ai), en_recruis = direct_enemies_recruits(ai); # Returns a integer. # # Returns the sum of 12 times the maximum attack damage for all enemy_units # def evaluate_attacker_against_opponents(ai*, unit, enemy_units) sum( map( enemy_units, 'enemy_unit', sum( # sum over all 4 elements (which are the same). 3 * 4 = 12. # map( [ max_possible_damage_with_retaliation( unit, enemy_unit ) # gives [ , , , ] # ], max( [ self[0], self[1] ] )*3 # Replaces all 4 elements in the list with 3 times the maximum of self[0] and self[1]. # ) ) ) ); # Returns a integer. # # Returns the sum of 20 times the maximum attack damage that all enemie_units give unit. # # This time the atacker can defend himself. The defender will chose the attack with the best # # damage difference # def evaluate_defender_against_opponents(ai*, unit, enemy_units) sum( map( enemy_units, 'enemy_unit', sum( map( [ max_possible_damage_with_retaliation( enemy_unit, unit ) ], max( [ self[0] - self[2], self[1] - self[3] ] )*5 ) ) ) ); # Returns a List with the attacker damages of all recruits # # The higher the better # def evaluate_recruits_offensive_combat(ai*, recruits, enemy_units) map( recruits, 'recruit', evaluate_attacker_against_opponents( ai, recruit, enemy_units ) ); #Returns a List with the defender damages for all recruits # # The lower the better # def evaluate_recruits_defensive_combat(ai*, recruits, enemy_units) map( recruits, 'recruit', evaluate_defender_against_opponents( ai, recruit, enemy_units ) ); # Some helper functions for map calculations # def combine_maps_mul( map_A, map_B ) map( map_A, value * map_B[key] ); def combine_maps_sub( map_A, map_B ) map( map_A, value - map_B[key] ); def combine_maps_add( map_A, map_B ) map( map_A, value + map_B[key] ); def combine_maps_div( map_A, map_B ) map( map_A, value / map_B[key] ); # Returns a map. # # Key is unit_id # # Value is (costs - avarage costs) * 80 + offset (offset is there so everything is positive) # # High Value means high cost compared to the avarage cost of own units # def consider_unit_cost(ai*) make_positive_only(map( cost_map, (value - average(values(cost_map))) * 80)) where cost_map = tomap(map(my_recruits, id),map(my_recruits, cost )); # Returns a Map. Key is unit_id of my_recruits. # # Value is the summed defense on important terrain multiplied by hp * 15. # # Normalized so the maximum is 100. # def defense_hp_eval(ai*, recruits_id_map) locally_normalize_to_highest( debug_print('combined hp and defense ',combine_maps_mul( my_recruits_defense(self), tomap(recruits_id_map, map( my_recruits, hitpoints*15) ) )) ); # Returns a Map. Key is unit_id of my_recruits. # # Value is how fast a unit can move through the important terrain. # # Normalized so the maximum is 100. # def movement_eval(ai*, recruits_id_map) locally_normalize_to_highest( make_positive_only( map( combine_maps_div( my_recruits_movement_cost( ai ), tomap( recruits_id_map, map( my_recruits, total_movement ) ) ), -value ) ) ); # Returns a Map. # # key is Unit id of my_recruits # # Value is how good is the unit against all other units currently on the map. # # The higher the better # def fighting_eval(ai*, recruits_id_map) make_positive_only( combine_maps_sub( tomap(recruits_id_map, evaluate_recruits_offensive_combat( ai, my_recruits, current_enemies ) ), tomap(recruits_id_map, evaluate_recruits_defensive_combat( ai, my_recruits, current_enemies ) ) ) ) where current_enemies = current_enemies(ai); # Returns a map # # Key is unit_id # # Value is 40 if unit is level 0 # def level_zero_malus(ai*) map( lvl_map, if(value = 0, 40, 0) ) where lvl_map = tomap( map(my_recruits, id), map(my_recruits, level ) ); # UNUSED FUNCTION # def filter_out( input_map, comparable_map ) filter( input_map, comparable_map[key] > comp_val) where comp_val = average(values(comparable_map))/2; # CENTRAL EVALUATION FUNCTION # # calls several subroutines to calculate a final score map of my_recruits # # Normalizes then so the scores add up do 100 (%). # # Note that fighting_eval is multiplied by 5 and gets a massive weight # # Returns a Map with unit_id as key and score in % as value. # def calculate_recruits(ai*) change_numbers_to_percents( combine_maps_sub( combine_maps_add( combine_maps_add( debug_print('defense_hp_eval ', defense_hp_eval(ai, recruits_id_map ) ), debug_print('movement_eval ', movement_eval(ai, recruits_id_map) ) ), debug_print('fighting_eval ', multiply_values( fighting_eval(ai,recruits_id_map), 5 ) ) ), combine_maps_add( debug_print('consider_unit_cost ', consider_unit_cost(ai) ), debug_print('level_zero_malus ', level_zero_malus(ai) ) ) ) ) where recruits_id_map = map(my_recruits, id); #recruits_id_map is a list of all recruit-ids# def do_recruitment( ai* ) if( unit_to_recruit, safe_call( recruit( unit_to_recruit ), end ), end ) where unit_to_recruit = recruit_army(self, vars.recruits_map); # 'MAIN FUNCTION' # if( vars.side_terrain, if(vars.direct_enemies, if(vars.turn_initialized = turn, do_recruitment(self), [ set_var(debug_print('turn_initialized'), turn) , set_var(debug_print('recruits_map'), debug_print( 'RECRUITS ', calculate_recruits(self)) ) ] + if( 1, [ null( sum( [ values(map( vars.side_terrain, if( value > 900, debug_label( key, 'o'), debug_label(key, '.') ) )), map( vars.direct_enemies, debug_label( loc, '*****************☠') ) ] ) ) ], [] ) ), set_var( 'direct_enemies', find_enemies(self, vars.side_terrain ) ) ), set_var('side_terrain', mark_important_locations( self, calculate_map_ownership( recruits_of_side, map(filter(sum(units_of_side), leader), loc), 4, 7, 4 ) ) ) ) faiend