New fai recruitment, added formula AI as choosable AI
This commit is contained in:
parent
0aa42b2552
commit
9f71b56c87
4 changed files with 503 additions and 0 deletions
26
data/ai/ais/formula_ai.cfg
Normal file
26
data/ai/ais/formula_ai.cfg
Normal file
|
@ -0,0 +1,26 @@
|
|||
#textdomain wesnoth
|
||||
[ai]
|
||||
id=formula
|
||||
description=Formula AI
|
||||
version=10703
|
||||
|
||||
[aspect]
|
||||
id=recruitment
|
||||
[facet]
|
||||
[value]
|
||||
engine=fai
|
||||
name=side_formulas
|
||||
move="{ai/formula/new_recruitment.fai}"
|
||||
[/value]
|
||||
[/facet]
|
||||
[/aspect]
|
||||
|
||||
[stage]
|
||||
engine=cpp
|
||||
id=fallback
|
||||
name=testing_ai_default::fallback
|
||||
[ai]
|
||||
ai_algorithm=default_ai
|
||||
[/ai]
|
||||
[/stage]
|
||||
[/ai]
|
107
data/ai/formula/lib/map_evaluation.fai
Normal file
107
data/ai/formula/lib/map_evaluation.fai
Normal file
|
@ -0,0 +1,107 @@
|
|||
fai 'map_evaluation.fai' #===== Evaluation how good unit is on a map ===== #
|
||||
|
||||
# filter out important locations #
|
||||
|
||||
# creates a 'map' data type that assigns terrain to location #
|
||||
def location_to_terrain_map(ai*)
|
||||
tomap( map(map.terrain, loc), map.terrain );
|
||||
|
||||
# creates a 'map' data type that assigns location to terrain#
|
||||
def id_to_location_map(ai*)
|
||||
map( tomap( map(map.terrain, id)), find(map.terrain, id=key).loc);
|
||||
|
||||
#this function returns all input hexes and hexes within some radius of them#
|
||||
def get_locations_surroundings(ai*, input_locs, range)
|
||||
sum(
|
||||
map(input_locs,
|
||||
locations_in_radius( self, range)
|
||||
)
|
||||
);
|
||||
|
||||
#this function returns all keeps and hexes within some radius#
|
||||
def get_keeps_with_surroundings(ai*, range)
|
||||
sum(map(keeps, locations_in_radius( self, range)));
|
||||
|
||||
|
||||
|
||||
|
||||
# evaluate average defense #
|
||||
|
||||
#gives map where key is id of terrain, and value is number of hexes of that terrain on a gamemap#
|
||||
def locations_map(ai*)
|
||||
tomap(
|
||||
map(
|
||||
map.terrain,
|
||||
id
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#
|
||||
filters terrain map,
|
||||
min_percent it minimal % of map that terrain has to occupy to be considered in later evaluations
|
||||
#
|
||||
def filtered_locations_map(ai*, min_percent)
|
||||
filter(
|
||||
locations_map(ai) ,
|
||||
value > (map.w * map.h) / ( 100 / min_percent )
|
||||
);
|
||||
|
||||
|
||||
#returns average defense of the unit #
|
||||
def unit_average_defense(ai*, unit, terrain_min_percent )
|
||||
sum(
|
||||
values(
|
||||
map(
|
||||
filtered_locations_map(ai,terrain_min_percent),
|
||||
defense_on(
|
||||
unit,
|
||||
find(map.terrain,id=key).loc
|
||||
) *
|
||||
value
|
||||
)
|
||||
)
|
||||
) /
|
||||
sum(
|
||||
values(
|
||||
filtered_locations_map(
|
||||
ai,
|
||||
terrain_min_percent
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
#returns map of units from units_list and their average defense (more -> better) #
|
||||
|
||||
def map_units_average_defense(ai*, units_list, terrain_min_percent)
|
||||
tomap(
|
||||
map(
|
||||
units_list,
|
||||
id
|
||||
),
|
||||
map(
|
||||
units_list, 'unit',
|
||||
unit_average_defense(
|
||||
ai,
|
||||
unit,
|
||||
terrain_min_percent
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
def units_average_defense(ai*, units_list, terrain_min_percent)
|
||||
sum(
|
||||
map(
|
||||
units_list, 'unit',
|
||||
unit_average_defense(
|
||||
ai,
|
||||
unit,
|
||||
terrain_min_percent
|
||||
)
|
||||
)
|
||||
) /
|
||||
size(
|
||||
units_list
|
||||
);
|
||||
|
||||
faiend
|
47
data/ai/formula/lib/util.fai
Normal file
47
data/ai/formula/lib/util.fai
Normal file
|
@ -0,0 +1,47 @@
|
|||
fai 'util.fai'
|
||||
|
||||
def sumarize_values( input_map )
|
||||
sum(
|
||||
values(input_map)
|
||||
);
|
||||
|
||||
def average( input_list )
|
||||
sum( input_list ) / input_list.size;
|
||||
|
||||
def lowest_value( input_map )
|
||||
choose( input_map, -value ).value;
|
||||
|
||||
def highest_value( input_map )
|
||||
choose( input_map, value ).value;
|
||||
|
||||
#
|
||||
this function takes map with numerical values, and converts it to %:
|
||||
[ 'a' -> 3, 'b' -> 12, 'c' -> 15 ] becomes [ 'a' -> 10, 'b' -> 40, 'c' -> 50 ]
|
||||
#
|
||||
def change_numbers_to_percents( input_map )
|
||||
map( input_map,
|
||||
(value*100) / sum
|
||||
) where sum = sumarize_values(input_map);
|
||||
|
||||
def add_number_to_values( input_map, number )
|
||||
map( input_map,
|
||||
value + number
|
||||
);
|
||||
|
||||
#make sure we have only positive values in a map #
|
||||
def make_positive_only( input_map )
|
||||
if( lowest < 0,
|
||||
add_number_to_values(
|
||||
input_map,
|
||||
-lowest
|
||||
),
|
||||
input_map
|
||||
)
|
||||
where lowest = lowest_value( input_map );
|
||||
|
||||
def sumarize_maps_values( map_A, map_B )
|
||||
map( map_A + map_B,
|
||||
value + map_A[key]
|
||||
);
|
||||
|
||||
faiend
|
323
data/ai/formula/new_recruitment.fai
Normal file
323
data/ai/formula/new_recruitment.fai
Normal file
|
@ -0,0 +1,323 @@
|
|||
fai 'new_recruitment.fai'
|
||||
|
||||
{ai/formula/lib/map_evaluation.fai}
|
||||
{ai/formula/lib/util.fai}
|
||||
|
||||
def get_terrain_types(ai*, locations)
|
||||
map( locations, 'l', fast_map[l].id ) where fast_map = location_to_terrain_map(ai);
|
||||
|
||||
|
||||
def get_villages_on_terrain_map( ai*, locations_map )
|
||||
filter( villages, locations_map[self] );
|
||||
|
||||
#
|
||||
for now important locations are villages only
|
||||
#
|
||||
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 ) );
|
||||
|
||||
def get_important_locations(ai* )
|
||||
keys(
|
||||
map( ai.vars.side_terrain,
|
||||
value >= 1000
|
||||
)
|
||||
);
|
||||
|
||||
def enemy_leaders( ai* )
|
||||
sum(
|
||||
map( enemies, 'enemy',
|
||||
filter( units_of_side[enemy], leader )
|
||||
)
|
||||
);
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
|
||||
def direct_enemies_units( ai* )
|
||||
sum(
|
||||
map( vars.direct_enemies, 'enemy',
|
||||
units_of_side[enemy.side]
|
||||
)
|
||||
);
|
||||
|
||||
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 ]
|
||||
#
|
||||
def important_locations_map(ai*)
|
||||
tomap(get_terrain_types(ai, get_important_locations(ai)));
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
def locally_normalize_to_highest( input_map )
|
||||
map( input_map, (value * 100)/max )
|
||||
where max = highest_value(input_map);
|
||||
|
||||
def locally_normalize_to_lowest( input_map )
|
||||
map( input_map, (value * 100)/min )
|
||||
where min = lowest_value(input_map);
|
||||
|
||||
|
||||
|
||||
# look for who we fight against #
|
||||
def enemy_leaders(ai*)
|
||||
map( enemies, 'enemy_side', find(units_of_side[enemy_side], leader ) );
|
||||
|
||||
|
||||
def distance_to_enemies(ai*)
|
||||
tomap( enemies, map( enemy_leaders(ai), distance_between( my_leader.loc, loc ) ));
|
||||
|
||||
# not used anymore
|
||||
def find_important_opponents(ai*)
|
||||
keys(filter( dist_en, value<avg ))
|
||||
where avg = (average(values(distance_to_enemies(ai)))*11)/10,
|
||||
dist_en = distance_to_enemies(ai);
|
||||
|
||||
|
||||
def current_enemies_units(ai*)
|
||||
sum(map( find_important_opponents(ai), 'enemy_side',
|
||||
units_of_side[ enemy_side ] ));
|
||||
|
||||
def current_enemies_recruits(ai*)
|
||||
sum(map( find_important_opponents(ai), 'enemy_side',
|
||||
recruits_of_side[ enemy_side ] ));
|
||||
#
|
||||
|
||||
def current_enemies(ai*)
|
||||
if( en_units.size > 15, en_units, en_recruis )
|
||||
where en_units = direct_enemies_units(ai),
|
||||
en_recruis = direct_enemies_recruits(ai);
|
||||
|
||||
|
||||
def evaluate_attacker_against_opponents(ai*, unit, enemy_units)
|
||||
sum(
|
||||
map(
|
||||
enemy_units, 'enemy_unit',
|
||||
sum(
|
||||
map(
|
||||
[
|
||||
max_possible_damage_with_retaliation( unit, enemy_unit )
|
||||
],
|
||||
max(
|
||||
[
|
||||
self[0] - self[2],
|
||||
self[1] - self[3]
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
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]
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
def evaluate_recruits_offensive_combat(ai*, recruits, enemy_units)
|
||||
map( recruits, 'recruit',
|
||||
evaluate_attacker_against_opponents( ai, recruit, enemy_units )
|
||||
);
|
||||
|
||||
def evaluate_recruits_defensive_combat(ai*, recruits, enemy_units)
|
||||
map( recruits, 'recruit',
|
||||
evaluate_defender_against_opponents( ai, recruit, enemy_units )
|
||||
);
|
||||
|
||||
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] );
|
||||
|
||||
#
|
||||
[ locally_normalize_to_highest(my_recruits_defense(self)),
|
||||
tomap(map(my_recruits, id),map(my_recruits, hitpoints) ),
|
||||
locally_normalize_to_lowest(my_recruits_movement_cost(self)),
|
||||
tomap(map(my_recruits, id),map(my_recruits, total_movement))]
|
||||
#
|
||||
|
||||
def consider_unit_cost(ai*)
|
||||
make_positive_only(map( cost_map, (value - average(values(cost_map))) * 10))
|
||||
where cost_map = tomap(map(my_recruits, id),map(my_recruits, cost ));
|
||||
|
||||
|
||||
def defense_hp_eval(ai*, recruits_id_map)
|
||||
locally_normalize_to_highest(combine_maps_mul( my_recruits_defense(self), tomap(recruits_id_map, map(my_recruits, hitpoints) ) ));
|
||||
|
||||
def movement_eval(ai*, recruits_id_map)
|
||||
locally_normalize_to_highest(make_positive_only(map(combine_maps_div( my_recruits_movement_cost(self), tomap(recruits_id_map, map(my_recruits, total_movement)) ), -value)));
|
||||
|
||||
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);
|
||||
|
||||
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 )
|
||||
);
|
||||
|
||||
#tmp function #
|
||||
def filter_out( input_map, comparable_map )
|
||||
filter( input_map, comparable_map[key] > comp_val)
|
||||
where comp_val = average(values(comparable_map))/2;
|
||||
|
||||
|
||||
def calculate_recruits(ai*)
|
||||
change_numbers_to_percents(
|
||||
filter_out(
|
||||
combine_maps_sub(
|
||||
combine_maps_add(
|
||||
combine_maps_add(
|
||||
debug_print('A ', defense_hp_eval(ai, recruits_id_map ) ),
|
||||
debug_print('B ', movement_eval(ai, recruits_id_map) )
|
||||
),
|
||||
debug_print('C ', fighting_eval(ai,recruits_id_map) )
|
||||
),
|
||||
combine_maps_add(
|
||||
debug_print('D ', consider_unit_cost(ai) ),
|
||||
debug_print('E ', level_zero_malus(ai) )
|
||||
)
|
||||
), movement_eval(ai, recruits_id_map)
|
||||
)
|
||||
) where recruits_id_map = map(my_recruits, id);
|
||||
|
||||
def unit_chooser(ai*,unit_map)
|
||||
choose(
|
||||
keys(unit_map), 'unit_type',
|
||||
unit_map[unit_type] -
|
||||
(
|
||||
(
|
||||
100 *
|
||||
size(
|
||||
filter(
|
||||
my_units,
|
||||
type = unit_type
|
||||
)
|
||||
)
|
||||
) /
|
||||
size(my_units)
|
||||
)
|
||||
);
|
||||
|
||||
if( vars.side_terrain,
|
||||
if(vars.direct_enemies,
|
||||
if(vars.turn_initialized = turn,
|
||||
|
||||
safe_call( recruit(unit_chooser(self, vars.recruits_map)),
|
||||
end
|
||||
),
|
||||
|
||||
[
|
||||
set_var(debug_print('turn_initialized'), turn) ,
|
||||
set_var(debug_print('recruits_map'), debug_print( 'RECRUITS ', calculate_recruits(self)) )
|
||||
]
|
||||
+
|
||||
if( turn = 5,
|
||||
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, 6, 4
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
faiend
|
Loading…
Add table
Reference in a new issue