Author: dhains
Date: Mon Mar 24 10:02:49 2008
New Revision: 25052

URL: http://svn.gna.org/viewcvs/wesnoth?rev=25052&view=rev
Log:
Support for formula scripts (only team formulas for now) added to WML.  Formula 
AI can now be written in a script completely seperate from WML and 'plugged' 
into a WML scenario cfg.  

For an example, the opening formula AI strategy was converted to a script 
(opening.fai) and added to scenario-formula.cfg.  Scripts are still pretty 
rough looking, comments and parsing improvements will come shortly to pretty 
them up.

The embedded Formula AI in WML is still supported as well.

Added:
    trunk/data/opening.fai
Modified:
    trunk/data/scenario-formula.cfg
    trunk/src/formula.cpp
    trunk/src/formula_ai.cpp

Added: trunk/data/opening.fai
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/data/opening.fai?rev=25052&view=auto
==============================================================================
--- trunk/data/opening.fai (added)
+++ trunk/data/opening.fai Mon Mar 24 10:02:49 2008
@@ -1,0 +1,75 @@
+def opening(ai*) 
+       if(turn = 1, [ 
+               recruit('Skeleton Archer', loc(11,21)),
+               recruit('Dark Adept', loc(11,22)),
+               recruit('Dark Adept', loc(10,22)),
+               recruit('Skeleton Archer', loc(9,22)),
+               recruit('Ghost', loc(11,24)),
+               move(loc(11,23), loc(14,22)) ],
+       if(turn = 2, [ 
+               move(loc(11,21),loc(13,17)),
+               if(unit_at(loc(11,22)).total_movement = 6,
+               move(loc(11,22),loc(13,18)),
+               move(loc(11,22),loc(15,19))),
+               move(loc(10,22),loc(7,19)),
+               move(loc(9,22),loc(4,22)),
+               move(loc(11,24),loc(18,24)),
+               move(loc(14,22),loc(11,23)),
+               recruit('Dark Adept', loc(11,21)),
+               recruit('Dark Adept', loc(11,22)) ],
+       if(turn = 3, [
+               move(loc(18,24),loc(20,22)),
+               move(loc(15,19),loc(17,17)),
+               move(loc(4,22),loc(5,18)),
+               recruit('Skeleton Archer', loc(11,21)) ],
+       if(turn = 4, [
+               recruit('Skeleton Archer', loc(11,21)) ],
+        [])))); def rate_position_defensiveness(ai*,src,dst) 
+       units_can_reach(ai.my_moves, dst).size - 
units_can_reach(ai.enemy_moves, dst).size; def 
rate_position_danger(ai*,unit,dst) 
+       (100*sum(map(units_can_reach(ai.enemy_moves, dst), 'enemy', 
max_possible_damage(enemy, unit)*defense_on(unit, dst)))) 
+       / unit.hitpoints; def build_attacks(attack_move) 
+       if(attack_move, 
+               map(attack_move.movements, attack(src, dst, 
attack_move.target)), 
+       []); def targets(ai) 
+       ai.enemy_and_unowned_villages; def distance_to_target(ai,dst) 
+       min(map(targets(ai), distance_between(dst, self))); def 
move_to_targets(ai*) 
+       find(moves, src != my_leader.loc and rate_position_defensiveness(ai, 
src, dst) > 0)
+               where moves = sort(my_moves.moves, distance_to_target(ai, 
a.dst) < distance_to_target(ai, b.dst)); def get_village_captures(ai) 
+       sum(map(ai.enemy_and_unowned_villages, 'village', 
map(units_can_reach(ai.my_moves, village), move(loc, village))), []); def 
get_village_garrisons(ai*) 
+       sum(map(my_villages, 'village', map(units_can_reach(ai.my_moves, 
village), move(loc, village))), []); def uncontended_captures(ai) 
+       filter(get_village_captures(ai), (src != ai.my_leader.loc) and 
(units_can_reach(ai.enemy_moves, dst).empty)); def move_to_keep(ai*) 
+       if(keep_in_range, 
+               move(my_leader.loc, keep_in_range), 
+       null())
+               where keep_in_range = find(unit_moves(my_leader.loc), 'moveto', 
find(keeps, moveto = self)); def village_value(ai*) 400; def 
rate_healing(unit*) 
+       value * ((min(max_hitpoints - hitpoints, 8)*100)/max_hitpoints); def 
rate_village_garrison(ai*, move_from, village) 
+       if(find(enemy_moves, dst = village), 
+               village_value(ai), 
+       0); def rate_village_capture(ai*,src,dst) village_value(ai); def 
+       rate_village_proximity(ai*, unit, dst) 
+       if(distance = 1, 
+               0, 
+       village_value(ai)/(distance/unit.total_movement + 1))
+               where distance = distance_to_nearest_unowned_village(dst); def 
rate_move(ai*,src,dst) 
+       if(is_village(map, dst), 
+               rate_healing(u) + if(find(my_villages, self = dst), 
+                       rate_village_garrison(ai, src, dst), 
+               rate_village_capture(ai, src, dst)), 
+       rate_village_proximity(ai, u, dst)) - (danger*u.value)/2
+               where u = unit_at(src), danger = rate_position_danger(ai, 
unit_at(src), dst); def rate_attack(ai*,attack) 
+       if(attack, 
+               (attack.chance_to_kill*attack.target_value + 
(((attack.avg_damage_inflicted*100)/unit_at(attack.target).max_hitpoints)*attack.target_value)
 + sum(map(attack.movements, rate_move(ai, src, dst))))/attack.movements.size, 
+       null()); def get_best_move(ai*, candidate_moves) 
+       if(rate_attack(ai, best_attack) > 
+               if(best_move, rate_move(ai, best_move.src, 
+                       best_move.dst), 
+               0),
+        build_attacks(best_attack), 
+       [best_move])
+       where best_attack = choose(attacks, rate_attack(ai, self)),
+       best_move = choose(candidate_moves, rate_move(ai, src, dst)); def 
move_leader_to_keep(ai*) 
+       if(dest, 
+               [move(my_leader.loc, dest)], 
+       [])
+               where dest = find(unit_moves(my_leader.loc), 'dst', find(keeps, 
'keep', keep = dst)); if(vars.done_opening != 1, [set_var('done_opening', 1)] + 
opening(self),
+    if(my_leader.loc and find(keeps, self = my_leader.loc) = null(), 
move_leader_to_keep(self), []) + get_best_move(self, filter(my_moves.moves, src 
!= my_leader.loc)) + [ recruit('Skeleton Archer', loc(11,21)), recruit('Dark 
Adept', loc(11,22)) ])

Modified: trunk/data/scenario-formula.cfg
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/data/scenario-formula.cfg?rev=25052&r1=25051&r2=25052&view=diff
==============================================================================
--- trunk/data/scenario-formula.cfg (original)
+++ trunk/data/scenario-formula.cfg Mon Mar 24 10:02:49 2008
@@ -71,208 +71,9 @@
 
         ai_algorithm=formula_ai
         [ai]
-                       [function]
-                               name=make_func
-                               input=""
-                               formula="def adder(x,y) x + y"
-                       [/function]
-            [function]
-                name=opening
-                inputs="ai"
-                formula="
-                       if(ai.turn = 1,
-                     #turn 1
-                     [ recruit('Skeleton Archer', loc(11,21)),
-                       recruit('Dark Adept', loc(11,22)),
-                       recruit('Dark Adept', loc(10,22)),
-                       recruit('Skeleton Archer', loc(9,22)),
-                       recruit('Ghost', loc(11,24)),
-                       move(loc(11,23), loc(14,22)) ],
-                  if(ai.turn = 2,
-                     #turn 2
-                     [ move(loc(11,21),loc(13,17)),
-                       if(unit_at(loc(11,22)).total_movement = 6,
-                           move(loc(11,22),loc(13,18)),
-                           move(loc(11,22),loc(15,19))),
-                       move(loc(10,22),loc(7,19)),
-                       move(loc(9,22),loc(4,22)),
-                       move(loc(11,24),loc(18,24)),
-                       move(loc(14,22),loc(11,23)),
-                       recruit('Dark Adept', loc(11,21)),
-                       recruit('Dark Adept', loc(11,22)) ],
-                  if(ai.turn = 3,
-                     #turn 3
-                     [ move(loc(18,24),loc(20,22)),
-                       move(loc(15,19),loc(17,17)),
-                       move(loc(4,22),loc(5,18)),
-                       recruit('Skeleton Archer', loc(11,21)) ],
-                  if(ai.turn = 4,
-                     #turn 4
-                     [ recruit('Skeleton Archer', loc(11,21)) ],
-                                        []))))"
-            [/function]
-
-                       #this function is not used; is just an example of a 
stub function
-                       #that might be expanded to have a full table of good 
unit matchups
-            [function]
-                name=rate_unit_matchup
-                inputs="unit,against"
-                formula="switch(unit, 'Elvish Fighter',
-                                             switch(against,
-                                                                        
'Elvish Fighter', 0,
-                                                                               
 'Elvish Archer', -5,
-                                                                               
 'Elvish Shaman', 2,
-                                                                               
 'Elvish Scout', 2,
-                                                                               
 'Wose', -2,
-                                                                               
 0),
-                                                                 'Elvish 
Archer',
-                                                                 
switch(against,
-                                                                        
'Elvish Fighter', 5,
-                                                                               
 'Elvish Archer', 0,
-                                                                               
 'Elvish Shaman', 2,
-                                                                               
 'Elvish Scout', 4,
-                                                                               
 'Wose', -8,
-                                                                               
 0),
-                                                                  0)"
-            [/function]
-
-            [function]
-                name=rate_position_defensiveness
-                inputs="ai*,src,dst"
-                formula="units_can_reach(ai.my_moves, dst).size - 
units_can_reach(ai.enemy_moves, dst).size"
-            [/function]
-
-            [function]
-                name=rate_position_danger
-                inputs="ai*,unit,dst"
-                formula="(100*sum(map(units_can_reach(ai.enemy_moves, dst), 
'enemy', max_possible_damage(enemy, unit)*chance_to_hit(unit, dst)))) / 
unit.hitpoints"
-            [/function]
-
-            [function]
-                name=build_attacks
-                inputs=attack_move
-                formula="if(attack_move, map(attack_move.movements, 
attack(src, dst, attack_move.target)), [])"
-            [/function]
-
-            [function]
-                name=targets
-                inputs="ai"
-                formula="ai.enemy_and_unowned_villages"
-            [/function]
-
-            [function]
-                name=distance_to_target
-                inputs="ai,dst"
-                formula="min(map(targets(ai), distance_between(dst, self)))"
-            [/function]
-
-            [function]
-                name=move_to_targets
-                inputs=ai*
-                formula="
-                         find(moves, src != my_leader.loc and 
rate_position_defensiveness(ai, src, dst) > 0)
-                           where moves = sort(my_moves.moves, 
distance_to_target(ai, a.dst) < distance_to_target(ai, b.dst))"
-                #  if(moves, choose(moves, -distance_to_target(ai, dst)), 
null())
-                #    where moves = filter(my_moves.moves, src != my_leader.loc 
and rate_position_defensiveness(ai, src, dst) > 0)
-            [/function]
-
-            [function]
-                name=get_village_captures
-                inputs="ai"
-                formula="
-                         sum(map(ai.enemy_and_unowned_villages, 'village', 
map(units_can_reach(ai.my_moves, village), move(loc, village))), [])"
-            [/function]
-
-            [function]
-                name=get_village_garrisons
-                inputs="ai*"
-                formula="
-                         sum(map(my_villages, 'village', 
map(units_can_reach(ai.my_moves, village), move(loc, village))), [])"
-            [/function]
-
-            [function]
-                name=uncontended_captures
-                inputs=ai
-                formula="filter(get_village_captures(ai), (src != 
ai.my_leader.loc) and (units_can_reach(ai.enemy_moves, dst).empty))"
-            [/function]
-
-            [function]
-                name=move_to_keep
-                inputs="ai*"
-                formula="if(keep_in_range, move(my_leader.loc, keep_in_range), 
null())
-                          where keep_in_range = 
find(unit_moves(my_leader.loc), 'moveto', find(keeps, moveto = self))"
-            [/function]
-
-            [function]
-                name=village_value
-                inputs="ai*"
-                formula="400"
-            [/function]
-
-            [function]
-                name=rate_healing
-                inputs="unit*"
-                formula="value * ((min(max_hitpoints - hitpoints, 
8)*100)/max_hitpoints)"
-            [/function]
-
-            [function]
-                name=rate_village_garrison
-                inputs="ai*,move_from,village"
-                formula="if(find(enemy_moves, dst = village), 
village_value(ai), 0)"
-            [/function]
-
-            [function]
-                name=rate_village_capture
-                inputs="ai*,src,dst"
-                formula="village_value(ai)"
-            [/function]
-
-            [function]
-                name=rate_village_proximity
-                inputs="ai*,unit,dst"
-                precondition="ai and unit and unit.total_movement and dst"
-                formula="if(distance = 1, 0, 
village_value(ai)/(distance/unit.total_movement + 1))
-                                where distance = 
distance_to_nearest_unowned_village(dst)"
-            [/function]
-
-            [function]
-                name=rate_move
-                inputs="ai*,src,dst"
-                formula="if(is_village(map, dst), rate_healing(u) + 
if(find(my_villages, self = dst), rate_village_garrison(ai, src, dst), 
rate_village_capture(ai, src, dst)), rate_village_proximity(ai, u, dst)) - 
(danger*u.value)/2
-                          where u = unit_at(src),
-                                danger = rate_position_danger(ai, 
unit_at(src), dst)"
-            [/function]
-
-            [function]
-                name=rate_attack
-                inputs="ai*,attack"
-                formula="if(attack, (attack.chance_to_kill*attack.target_value 
+ 
(((attack.avg_damage_inflicted*100)/unit_at(attack.target).max_hitpoints)*attack.target_value)
 + sum(map(attack.movements, rate_move(ai, src, dst))))/attack.movements.size, 
null())"
-            [/function]
-
-            [function]
-                name=get_best_move
-                inputs="ai*,candidate_moves"
-                formula="if(rate_attack(ai, best_attack) > if(best_move, 
rate_move(ai, best_move.src, best_move.dst), 0),
-                                      build_attacks(best_attack), [best_move])
-                                where best_attack = choose(attacks, 
rate_attack(ai, self)),
-                                              best_move = 
choose(candidate_moves, rate_move(ai, src, dst))"
-            [/function]
-
-            [function]
-                name=move_leader_to_keep
-                inputs="ai*"
-                formula="
-                       if(dest, [move(my_leader.loc, dest)], [])
-                       where dest = find(unit_moves(my_leader.loc), 'dst', 
find(keeps, 'keep', keep = dst))"
-            [/function]
-
-                       # The AI entry point. Currently we just do our 
hard-wired opening moves and then fallback to the
-                       # default C++ AI.
-            move="if(vars.done_opening != 1, [set_var('done_opening', 1)] + 
opening(self),
-                             if(my_leader.loc and find(keeps, self = 
my_leader.loc) = null(), move_leader_to_keep(self), []) +
-                             get_best_move(self, filter(my_moves.moves, src != 
my_leader.loc)) +
-                  [ recruit('Skeleton Archer', loc(11,21)), recruit('Dark 
Adept', loc(11,22)) ]
-                                 )"
+                       [team_formula]
+                               rulebase=opening
+                       [/team_formula]
         [/ai]
     [/side]
 [/test]

Modified: trunk/src/formula.cpp
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/src/formula.cpp?rev=25052&r1=25051&r2=25052&view=diff
==============================================================================
--- trunk/src/formula.cpp (original)
+++ trunk/src/formula.cpp Mon Mar 24 10:02:49 2008
@@ -431,7 +431,12 @@
 
        while((i1-> type != TOKEN_RPARENS) && (i1 != i2)) {
                if(i1->type == TOKEN_IDENTIFIER) {
-                       res->push_back(std::string(i1->begin, i1->end));
+                       if(std::string((i1+1)->begin, (i1+1)->end) == "*") {
+                               res->push_back(std::string(i1->begin, i1->end) 
+ std::string("*"));
+                               ++i1;
+                       } else {
+                               res->push_back(std::string(i1->begin, i1->end));
+                       }
                } else if (i1->type == TOKEN_COMMA) {
                        //do nothing
                } else {

Modified: trunk/src/formula_ai.cpp
URL: 
http://svn.gna.org/viewcvs/wesnoth/trunk/src/formula_ai.cpp?rev=25052&r1=25051&r2=25052&view=diff
==============================================================================
--- trunk/src/formula_ai.cpp (original)
+++ trunk/src/formula_ai.cpp Mon Mar 24 10:02:49 2008
@@ -19,6 +19,7 @@
 #include "formula_callable.hpp"
 #include "formula_function.hpp"
 #include "pathutils.hpp"
+#include "filesystem.hpp"
 
 namespace {
 using namespace game_logic;
@@ -550,6 +551,12 @@
        ai_function_symbol_table function_table(*this);
 
        const config& ai_param = current_team().ai_parameters();
+       config::const_child_itors team_formula = 
ai_param.child_range("team_formula");
+       if(team_formula.first != team_formula.second) {
+               std::string rulebase = (**team_formula.first)["rulebase"];
+               const t_string& formula_string = read_file(std::string("data/") 
+ rulebase + std::string(".fai"));
+               move_formula_ = 
game_logic::formula::create_optional_formula(formula_string, &function_table);
+       } else {
        config::const_child_itors functions = ai_param.child_range("function");
        for(config::const_child_iterator i = functions.first; i != 
functions.second; ++i) {
                const t_string& name = (**i)["name"];
@@ -562,7 +569,7 @@
 
        recruit_formula_ = 
game_logic::formula::create_optional_formula(current_team().ai_parameters()["recruit"],
 &function_table);
        move_formula_ = 
game_logic::formula::create_optional_formula(current_team().ai_parameters()["move"],
 &function_table);
-
+       }
        //execute units formulas first
        std::vector<gamemap::location> formula_unit_loc;
        for(unit_map::const_iterator i = units_.begin(); i != units_.end(); 
++i) {
@@ -574,15 +581,15 @@
 
        for(std::vector<gamemap::location>::const_iterator i = 
formula_unit_loc.begin() ; i != formula_unit_loc.end() ; ++i)
        {
-                       unit_map::const_iterator unit_it = units_.find(*i);
-                       if ( unit_it != units_.end() )
-                       {
-                               game_logic::const_formula_ptr formula(new 
game_logic::formula(unit_it->second.get_formula(), &function_table));
-                               game_logic::map_formula_callable callable(this);
-                               callable.add_ref();
-                               callable.add("me", variant(new 
unit_callable(*unit_it, current_team(), get_info().team_num)));
-                               make_move(formula, callable);
-                       }
+               unit_map::const_iterator unit_it = units_.find(*i);
+               if ( unit_it != units_.end() )
+               {
+                       game_logic::const_formula_ptr formula(new 
game_logic::formula(unit_it->second.get_formula(), &function_table));
+                       game_logic::map_formula_callable callable(this);
+                       callable.add_ref();
+                       callable.add("me", variant(new unit_callable(*unit_it, 
current_team(), get_info().team_num)));
+                       make_move(formula, callable);
+               }
        }
 
        game_logic::map_formula_callable callable(this);


_______________________________________________
Wesnoth-commits mailing list
[email protected]
https://mail.gna.org/listinfo/wesnoth-commits

Reply via email to