merged in formula AI branch
This commit is contained in:
commit
ba2a4c2ad4
39 changed files with 4650 additions and 157 deletions
|
@ -2,6 +2,11 @@
|
|||
# Warning:
|
||||
# any hotkey that uses shift should not trigger any animation(s) in game since shift toggles accelerated mode
|
||||
|
||||
[hotkey]
|
||||
command=aiformula
|
||||
key=f
|
||||
[/hotkey]
|
||||
|
||||
#ifdef APPLE
|
||||
[hotkey]
|
||||
command=accelerated
|
||||
|
|
|
@ -41,11 +41,9 @@ noinst_LIBRARIES = libwesnoth-core.a libwesnoth.a
|
|||
|
||||
pkgdatadir=$(datadir)/@DATADIR@
|
||||
|
||||
INTERNALLIBS = -lwesnoth-core
|
||||
|
||||
THELIBS = -L. $(SDL_IMAGE_LIBS) $(SDL_MIXER_LIBS) $(SDL_NET_LIBS) \
|
||||
THELIBS = -L. -lwesnoth-core $(SDL_IMAGE_LIBS) $(SDL_MIXER_LIBS) $(SDL_NET_LIBS) \
|
||||
$(SDL_TTF_LIBS) $(SDL_LIBS) $(PYTHON_LIBS) $(FREETYPE_LIBS) $(LIBINTL) \
|
||||
$(BOOST_IOSTREAMS_LIBS)
|
||||
$(BOOST_IOSTREAMS_LIBS) $(BOOST_REGEX_LIBS)
|
||||
|
||||
wesnoth_source = \
|
||||
about.cpp \
|
||||
|
@ -58,9 +56,14 @@ wesnoth_source = \
|
|||
ai_village.cpp \
|
||||
animated_game.cpp \
|
||||
attack_prediction.cpp \
|
||||
callable_objects.cpp \
|
||||
config_adapter.cpp \
|
||||
dialogs.cpp \
|
||||
floating_textbox.cpp \
|
||||
formula.cpp \
|
||||
formula_ai.cpp \
|
||||
formula_function.cpp \
|
||||
formula_tokenizer.cpp \
|
||||
game_display.cpp \
|
||||
game_events.cpp \
|
||||
game_preferences.cpp \
|
||||
|
@ -107,8 +110,9 @@ wesnoth_source = \
|
|||
unit_types.cpp \
|
||||
upload_log.cpp \
|
||||
variable.cpp \
|
||||
variant.cpp \
|
||||
widgets/combo.cpp \
|
||||
widgets/scrollpane.cpp
|
||||
widgets/scrollpane.cpp
|
||||
|
||||
#############################################################################
|
||||
# Wesnoth #
|
||||
|
@ -118,7 +122,7 @@ wesnoth_SOURCES = \
|
|||
game.cpp \
|
||||
$(wesnoth_source)
|
||||
|
||||
wesnoth_LDADD = $(INTERNALLIBS) libwesnoth.a $(THELIBS)
|
||||
wesnoth_LDADD = $(THELIBS) libwesnoth.a
|
||||
wesnoth_DEPENDENCIES=libwesnoth-core.a libwesnoth.a
|
||||
|
||||
#############################################################################
|
||||
|
@ -139,7 +143,7 @@ wesnoth_editor_SOURCES = \
|
|||
generic_event.cpp \
|
||||
tooltips.cpp
|
||||
|
||||
wesnoth_editor_LDADD = $(INTERNALLIBS) libwesnoth.a $(THELIBS)
|
||||
wesnoth_editor_LDADD = $(THELIBS) libwesnoth.a
|
||||
wesnoth_editor_DEPENDENCIES=libwesnoth-core.a libwesnoth.a
|
||||
|
||||
#############################################################################
|
||||
|
@ -207,10 +211,9 @@ cutter_DEPENDENCIES=libwesnoth-core.a
|
|||
test_SOURCES = \
|
||||
tests/main.cpp \
|
||||
tests/test_util.cpp \
|
||||
tests/test_network_worker.cpp \
|
||||
$(wesnoth_source)
|
||||
|
||||
test_LDADD = $(INTERNALLIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) libwesnoth.a $(THELIBS)
|
||||
test_LDADD = $(THELIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) libwesnoth.a
|
||||
test_DEPENDENCIES=libwesnoth-core.a libwesnoth.a
|
||||
|
||||
#############################################################################
|
||||
|
@ -480,10 +483,6 @@ if PREFSDIR
|
|||
CXXFLAGS += -DPREFERENCES_DIR=\"$(prefsdir)\"
|
||||
endif
|
||||
|
||||
##if HAVE_BUILTIN_EXPECT
|
||||
## CXXFLAGS += -DHAVE_BUILTIN_EXPECT
|
||||
##endif
|
||||
|
||||
if BOOST_TEST_DYN_LINK
|
||||
CXXFLAGS += -DBOOST_TEST_DYN_LINK
|
||||
endif
|
||||
|
@ -502,7 +501,7 @@ if STATIC
|
|||
LDFLAGS += -all-static
|
||||
endif
|
||||
|
||||
LDFLAGS += $(BOOST_IOSTREAMS_LDFLAGS)
|
||||
LDFLAGS += $(BOOST_IOSTREAMS_LDFLAGS) $(BOOST_REGEX_LDFLAGS)
|
||||
|
||||
#if STATIC
|
||||
#LIBS += -static -L/usr/lib -Wl,-rpath,/usr/lib -L/usr/X11R6/lib -lSDL_image \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -202,7 +202,7 @@ std::string recruit_unit(const gamemap& map, const int side, unit_map& units,
|
|||
|
||||
config cfg_unit1;
|
||||
new_unit.write(cfg_unit1);
|
||||
DBG_NG << cfg_unit1;
|
||||
DBG_NG << cfg_unit1.debug();
|
||||
if (!game_config::ignore_replay_errors) {
|
||||
throw replay::error("OOS while recruiting.");
|
||||
}
|
||||
|
@ -236,10 +236,10 @@ gamemap::location under_leadership(const unit_map& units,
|
|||
battle_context::battle_context(const gamemap& map, const std::vector<team>& teams, const unit_map& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc, const gamemap::location& defender_loc,
|
||||
int attacker_weapon, int defender_weapon, double aggression, const combatant *prev_def)
|
||||
int attacker_weapon, int defender_weapon, double aggression, const combatant *prev_def, const unit* attacker_ptr)
|
||||
: attacker_stats_(NULL), defender_stats_(NULL), attacker_combatant_(NULL), defender_combatant_(NULL)
|
||||
{
|
||||
const unit& attacker = units.find(attacker_loc)->second;
|
||||
const unit& attacker = attacker_ptr ? *attacker_ptr : units.find(attacker_loc)->second;
|
||||
const unit& defender = units.find(defender_loc)->second;
|
||||
const double harm_weight = 1.0 - aggression;
|
||||
|
||||
|
@ -736,7 +736,7 @@ void attack::fire_event(const std::string& n)
|
|||
dat.add_child("first");
|
||||
dat.add_child("second");
|
||||
if(a_ != units_.end()) {
|
||||
(*(dat.child("first")))["weapon"]=a_stats_->weapon?a_stats_->weapon->id():"none";
|
||||
(*(dat.child("first")))["weapon"]=a_stats_->weapon->id();
|
||||
|
||||
}
|
||||
if(d_ != units_.end()) {
|
||||
|
@ -769,9 +769,7 @@ void attack::fire_event(const std::string& n)
|
|||
dat.add_child("second");
|
||||
(*(dat.child("first")))["weapon"]=a_stats_->weapon->id();
|
||||
(*(dat.child("second")))["weapon"]=d_stats_->weapon != NULL ? d_stats_->weapon->id() : "none";
|
||||
DELAY_END_LEVEL(delayed_exception, game_events::fire(n,
|
||||
game_events::entity_location(attacker_,a_id_),
|
||||
game_events::entity_location(defender_,d_id_),dat));
|
||||
DELAY_END_LEVEL(delayed_exception, game_events::fire(n,attacker_,defender_,dat));
|
||||
|
||||
// The event could have killed either the attacker or
|
||||
// defender, so we have to make sure they still exist
|
||||
|
@ -802,7 +800,7 @@ void attack::refresh_bc()
|
|||
if(a_ == units_.end() || d_ == units_.end()) {
|
||||
return;
|
||||
}
|
||||
*bc_ = battle_context(map_, teams_, units_, state_, info_, attacker_, defender_, attack_with_, defend_with_);
|
||||
*bc_ = battle_context(map_, teams_, units_, state_, info_, attacker_, defender_, attack_with_, defend_with_);
|
||||
a_stats_ = &bc_->get_attacker_stats();
|
||||
d_stats_ = &bc_->get_defender_stats();
|
||||
attacker_cth_ = a_stats_->chance_to_hit;
|
||||
|
@ -1046,7 +1044,7 @@ attack::attack(game_display& gui, const gamemap& map,
|
|||
if(amount_drained > 0) {
|
||||
char buf[50];
|
||||
snprintf(buf,sizeof(buf),"%d",amount_drained);
|
||||
if (update_display_ && preferences::show_combat()){
|
||||
if (update_display_){
|
||||
gui_.float_label(a_->first,buf,0,255,0);
|
||||
}
|
||||
a_->second.heal(amount_drained);
|
||||
|
@ -1322,11 +1320,7 @@ attack::attack(game_display& gui, const gamemap& map,
|
|||
fire_event("attack_end");
|
||||
DELAY_END_LEVEL(delayed_exception, game_events::fire("die",death_loc,defender_loc));
|
||||
|
||||
// Don't try to call refresh_bc() here the attacker or defender might have
|
||||
// been replaced by another unit, which might have a lower number of weapons.
|
||||
// In that case refresh_bc() will terminate with an invalid selected weapon.
|
||||
a_ = units_.find(attacker_);
|
||||
d_ = units_.find(defender_);
|
||||
refresh_bc();
|
||||
|
||||
if(a_ == units_.end() || !death_loc.matches_unit(a_->second)) {
|
||||
// WML has invalidated the dying unit, abort
|
||||
|
@ -1847,44 +1841,23 @@ bool clear_shroud_loc(const gamemap& map, team& tm,
|
|||
|
||||
// We clear one past the edge of the board, so that the half-hexes
|
||||
// at the edge can also be cleared of fog/shroud.
|
||||
if (on_board_loc || map.on_board(adj[i], true)) {
|
||||
if (on_board_loc || map.on_board(adj[i])) {
|
||||
// Both functions should be executed so don't use || which
|
||||
// uses short-cut evaluation.
|
||||
const bool res = tm.clear_shroud(adj[i]) | tm.clear_fog(adj[i]);
|
||||
|
||||
if(res) {
|
||||
result = true;
|
||||
|
||||
// If we're near the corner it might be the corner also needs to be cleared
|
||||
// this always happens at the lower left corner and depending on the with
|
||||
// at the upper or lower right corner.
|
||||
if(adj[i].x == 0 && adj[i].y == map.h() - 1) { // Lower left corner
|
||||
const gamemap::location corner(-1 , map.h());
|
||||
tm.clear_shroud(corner);
|
||||
tm.clear_fog(corner);
|
||||
|
||||
} else if(map.w() % 2 && adj[i].x == map.w() - 1 && adj[i].y == map.h() - 1) { // Lower right corner
|
||||
const gamemap::location corner(map.w() , map.h());
|
||||
tm.clear_shroud(corner);
|
||||
tm.clear_fog(corner);
|
||||
|
||||
} else if(!(map.w() % 2) && adj[i].x == map.w() - 1 && adj[i].y == 0) { // Upper right corner
|
||||
const gamemap::location corner(map.w() , -1);
|
||||
tm.clear_shroud(corner);
|
||||
tm.clear_fog(corner);
|
||||
}
|
||||
|
||||
if(cleared) {
|
||||
cleared->push_back(adj[i]);
|
||||
}
|
||||
if(res && cleared != NULL) {
|
||||
cleared->push_back(adj[i]);
|
||||
}
|
||||
|
||||
result |= res;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Returns true if some shroud is cleared.
|
||||
//! Returns true iff some shroud is cleared.
|
||||
//! seen_units will return new units that have been seen by this unit.
|
||||
//! If known_units is NULL, seen_units can be NULL and will not be changed.
|
||||
bool clear_shroud_unit(const gamemap& map,
|
||||
|
@ -1915,7 +1888,7 @@ bool clear_shroud_unit(const gamemap& map,
|
|||
if(sighted != units.end() &&
|
||||
(sighted->second.invisible(*it,units,teams) == false
|
||||
|| teams[team].is_enemy(sighted->second.side()) == false)) {
|
||||
//check if we know this unit, but we always know ourself
|
||||
//check if we know this unit, but we always know oursefl
|
||||
//just in case we managed to move on a fogged hex (teleport)
|
||||
if(!(seen_units == NULL || known_units == NULL)
|
||||
&& known_units->count(*it) == 0 && *it != loc) {
|
||||
|
@ -1940,7 +1913,6 @@ void recalculate_fog(const gamemap& map,
|
|||
unit_map& units,
|
||||
std::vector<team>& teams, int team) {
|
||||
|
||||
assert(team >= 0 && static_cast<size_t>(team) < teams.size());
|
||||
teams[team].refog();
|
||||
|
||||
for(unit_map::iterator i = units.begin(); i != units.end(); ++i) {
|
||||
|
@ -2168,7 +2140,6 @@ size_t move_unit(game_display* disp,
|
|||
p->first = steps.back();
|
||||
units.add(p);
|
||||
ui = units.find(p->first);
|
||||
unit::clear_status_caches();
|
||||
|
||||
if(move_recorder != NULL) {
|
||||
move_recorder->add_movement(steps.front(),steps.back());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
battle_context(const gamemap& map, const std::vector<team>& teams, const unit_map& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc, const gamemap::location& defender_loc,
|
||||
int attacker_weapon = -1, int defender_weapon = -1, double aggression = 0.0, const combatant *prev_def = NULL);
|
||||
int attacker_weapon = -1, int defender_weapon = -1, double aggression = 0.0, const combatant *prev_def = NULL, const unit* attacker_ptr=NULL);
|
||||
|
||||
// Used by the AI which caches unit_stats
|
||||
battle_context(const unit_stats &att, const unit_stats &def);
|
||||
|
|
171
src/ai.cpp
171
src/ai.cpp
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,7 +24,10 @@
|
|||
#include "ai_python.hpp"
|
||||
#endif
|
||||
#include "actions.hpp"
|
||||
#include "callable_objects.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "foreach.hpp"
|
||||
#include "formula_ai.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_events.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
|
@ -34,6 +37,7 @@
|
|||
#include "replay.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "unit_display.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "playturn.hpp"
|
||||
#include "wml_exception.hpp"
|
||||
|
||||
|
@ -202,6 +206,8 @@ ai_interface* create_ai(const std::string& name, ai_interface::info& info)
|
|||
return new sample_ai(info);
|
||||
else if(name == "idle_ai")
|
||||
return new idle_ai(info);
|
||||
else if(name == "formula_ai")
|
||||
return new formula_ai(info);
|
||||
else if(name == "dfool_ai")
|
||||
return new dfool::dfool_ai(info);
|
||||
//else if(name == "advanced_ai")
|
||||
|
@ -416,8 +422,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to,
|
|||
// Stop the user from issuing any commands while the unit is moving.
|
||||
const events::command_disabler disable_commands;
|
||||
|
||||
// show the unit in the sidebar without highlighting it
|
||||
info_.disp.display_unit_hex(from);
|
||||
info_.disp.select_hex(from);
|
||||
info_.disp.update_display();
|
||||
|
||||
log_scope2(ai, "move_unit");
|
||||
|
@ -435,6 +440,10 @@ gamemap::location ai_interface::move_unit_partial(location from, location to,
|
|||
|
||||
const bool show_move = preferences::show_ai_moves();
|
||||
|
||||
const bool teleport = u_it->second.get_ability_bool("teleport",u_it->first);
|
||||
paths current_paths(info_.map,info_.units,from,
|
||||
info_.teams,false,teleport,current_team());
|
||||
|
||||
const std::map<location,paths>::iterator p_it = possible_moves.find(from);
|
||||
|
||||
if(p_it != possible_moves.end()) {
|
||||
|
@ -518,6 +527,8 @@ gamemap::location ai_interface::move_unit_partial(location from, location to,
|
|||
if(show_move && unit_display::unit_visible_on_path(steps,
|
||||
u_it->second, info_.units,info_.teams)) {
|
||||
|
||||
info_.disp.scroll_to_tiles(from,to);
|
||||
|
||||
unit_map::iterator up = info_.units.find(u_it->first);
|
||||
unit_display::move_unit(steps,up->second,info_.teams);
|
||||
}
|
||||
|
@ -678,7 +689,7 @@ void ai::attack_enemy(const location& attacking_unit, const location& target,
|
|||
|
||||
void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_map& srcdst,
|
||||
move_map& dstsrc, bool enemy, bool assume_full_movement,
|
||||
const std::set<gamemap::location>* remove_destinations)
|
||||
const std::set<gamemap::location>* remove_destinations) const
|
||||
{
|
||||
calculate_moves(info_.units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations);
|
||||
}
|
||||
|
@ -687,7 +698,7 @@ void ai_interface::calculate_moves(const unit_map& units, std::map<location,path
|
|||
move_map& dstsrc, bool enemy, bool assume_full_movement,
|
||||
const std::set<gamemap::location>* remove_destinations,
|
||||
bool see_all
|
||||
)
|
||||
) const
|
||||
{
|
||||
|
||||
for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) {
|
||||
|
@ -1109,10 +1120,8 @@ void ai_interface::attack_enemy(const location u,
|
|||
LOG_STREAM(err, ai) << "attempt to attack twice with the same unit\n";
|
||||
return;
|
||||
}
|
||||
if(weapon >= 0)
|
||||
{
|
||||
recorder.add_attack(u,target,weapon,def_weapon);
|
||||
}
|
||||
|
||||
recorder.add_attack(u,target,weapon,def_weapon);
|
||||
try {
|
||||
attack(info_.disp, info_.map, info_.teams, u, target, weapon, def_weapon,
|
||||
info_.units, info_.state, info_.gameinfo);
|
||||
|
@ -2174,3 +2183,147 @@ int ai::attack_depth()
|
|||
attack_depth_ = maximum<int>(1,lexical_cast_default<int>(parms["attack_depth"],5));
|
||||
return attack_depth_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename Container>
|
||||
variant villages_from_set(const Container& villages,
|
||||
const std::set<gamemap::location>* exclude=NULL) {
|
||||
std::vector<variant> vars;
|
||||
foreach(const gamemap::location& loc, villages) {
|
||||
if(exclude && exclude->count(loc)) {
|
||||
continue;
|
||||
}
|
||||
vars.push_back(variant(new location_callable(loc)));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
}
|
||||
|
||||
variant ai_interface::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "turn") {
|
||||
return variant(get_info().state.turn());
|
||||
} else if(key == "units") {
|
||||
std::vector<variant> vars;
|
||||
for(unit_map::const_iterator i = info_.units.begin(); i != info_.units.end(); ++i) {
|
||||
vars.push_back(variant(new unit_callable(*i, info_.teams[info_.team_num-1], info_.team_num)));
|
||||
}
|
||||
return variant(&vars);
|
||||
} else if(key == "my_units") {
|
||||
std::vector<variant> vars;
|
||||
for(unit_map::const_iterator i = info_.units.begin(); i != info_.units.end(); ++i) {
|
||||
if(i->second.side() == info_.team_num) {
|
||||
vars.push_back(variant(new unit_callable(*i, info_.teams[info_.team_num-1], info_.team_num)));
|
||||
}
|
||||
}
|
||||
return variant(&vars);
|
||||
} else if(key == "enemy_units") {
|
||||
std::vector<variant> vars;
|
||||
for(unit_map::const_iterator i = info_.units.begin(); i != info_.units.end(); ++i) {
|
||||
if(info_.teams[info_.team_num-1].is_enemy(i->second.side())) {
|
||||
vars.push_back(variant(new unit_callable(*i, info_.teams[info_.team_num-1], info_.team_num)));
|
||||
}
|
||||
}
|
||||
return variant(&vars);
|
||||
} else if(key == "villages") {
|
||||
return villages_from_set(info_.map.villages());
|
||||
} else if(key == "my_villages") {
|
||||
return villages_from_set(current_team().villages());
|
||||
} else if(key == "enemy_and_unowned_villages") {
|
||||
return villages_from_set(info_.map.villages(), ¤t_team().villages());
|
||||
} else if(key == "map") {
|
||||
return variant(new gamemap_callable(info_.map));
|
||||
}
|
||||
return variant();
|
||||
}
|
||||
|
||||
void ai_interface::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("turn", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("units", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("my_units", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("enemy_units", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("villages", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("my_villages", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("enemy_and_unowned_villages", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
||||
variant ai::attack_analysis::get_value(const std::string& key) const
|
||||
{
|
||||
using namespace game_logic;
|
||||
if(key == "target") {
|
||||
return variant(new location_callable(target));
|
||||
} else if(key == "movements") {
|
||||
std::vector<variant> res;
|
||||
for(int n = 0; n != movements.size(); ++n) {
|
||||
map_formula_callable* item = new map_formula_callable(NULL);
|
||||
item->add("src", variant(new location_callable(movements[n].first)));
|
||||
item->add("dst", variant(new location_callable(movements[n].second)));
|
||||
res.push_back(variant(item));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
} else if(key == "units") {
|
||||
std::vector<variant> res;
|
||||
for(int n = 0; n != movements.size(); ++n) {
|
||||
res.push_back(variant(new location_callable(movements[n].first)));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
} else if(key == "target_value") {
|
||||
return variant(static_cast<int>(target_value*1000));
|
||||
} else if(key == "avg_losses") {
|
||||
return variant(static_cast<int>(avg_losses*1000));
|
||||
} else if(key == "chance_to_kill") {
|
||||
return variant(static_cast<int>(chance_to_kill*100));
|
||||
} else if(key == "avg_damage_inflicted") {
|
||||
return variant(static_cast<int>(avg_damage_inflicted));
|
||||
} else if(key == "target_starting_damage") {
|
||||
return variant(target_starting_damage);
|
||||
} else if(key == "avg_damage_taken") {
|
||||
return variant(static_cast<int>(avg_damage_taken));
|
||||
} else if(key == "resources_used") {
|
||||
return variant(static_cast<int>(resources_used));
|
||||
} else if(key == "terrain_quality") {
|
||||
return variant(static_cast<int>(terrain_quality));
|
||||
} else if(key == "alternative_terrain_quality") {
|
||||
return variant(static_cast<int>(alternative_terrain_quality));
|
||||
} else if(key == "vulnerability") {
|
||||
return variant(static_cast<int>(vulnerability));
|
||||
} else if(key == "support") {
|
||||
return variant(static_cast<int>(support));
|
||||
} else if(key == "leader_threat") {
|
||||
return variant(leader_threat);
|
||||
} else if(key == "uses_leader") {
|
||||
return variant(uses_leader);
|
||||
} else if(key == "is_surrounded") {
|
||||
return variant(is_surrounded);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void ai::attack_analysis::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using namespace game_logic;
|
||||
inputs->push_back(formula_input("target", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("movements", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("units", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("target_value", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("avg_losses", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("chance_to_kill", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("avg_damage_inflicted", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("target_starting_damage", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("avg_damage_taken", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("resources_used", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("terrain_quality", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("alternative_terrain_quality", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("vulnerability", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("support", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("leader_threat", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("uses_leader", FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input("is_surrounded", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
|
10
src/ai.hpp
10
src/ai.hpp
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "actions.hpp"
|
||||
#include "ai_interface.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
|
||||
class ai : public ai_interface {
|
||||
public:
|
||||
|
@ -125,7 +126,8 @@ protected:
|
|||
const location& to, const location& via,
|
||||
const std::map<location,paths>& possible_moves) const;
|
||||
|
||||
struct attack_analysis
|
||||
public:
|
||||
struct attack_analysis : public game_logic::formula_callable
|
||||
{
|
||||
void analyze(const gamemap& map, unit_map& units,
|
||||
const std::vector<team>& teams,
|
||||
|
@ -135,6 +137,8 @@ protected:
|
|||
const move_map& enemy_dstsrc, double aggression);
|
||||
|
||||
double rating(double aggression, class ai& ai_obj) const;
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
|
||||
gamemap::location target;
|
||||
std::vector<std::pair<gamemap::location,gamemap::location> > movements;
|
||||
|
@ -181,6 +185,8 @@ protected:
|
|||
bool is_surrounded;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
virtual void do_attack_analysis(
|
||||
const location& loc,
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -45,8 +45,10 @@ void ai::do_attack_analysis(
|
|||
// This function is called fairly frequently, so interact with the user here.
|
||||
raise_user_interact();
|
||||
|
||||
if(cur_analysis.movements.size() >= size_t(attack_depth()))
|
||||
if(cur_analysis.movements.size() >= size_t(attack_depth())) {
|
||||
std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << attack_depth() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
static double best_results[6];
|
||||
if(result.empty()) {
|
||||
|
@ -242,6 +244,7 @@ void ai::do_attack_analysis(
|
|||
|
||||
cur_analysis.analyze(map_, units_, teams_, state_, gameinfo_, *this, dstsrc, srcdst, enemy_dstsrc, current_team().aggression());
|
||||
|
||||
//Remove this short-circuiting logic for now.. --David
|
||||
if(cur_analysis.rating(current_team().aggression(),*this) > rating_to_beat) {
|
||||
|
||||
result.push_back(cur_analysis);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -21,11 +21,12 @@
|
|||
class game_display;
|
||||
class gamemap;
|
||||
|
||||
#include "formula_callable.hpp"
|
||||
#include "generic_event.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
|
||||
class ai_interface {
|
||||
class ai_interface : public game_logic::formula_callable {
|
||||
public:
|
||||
|
||||
//! A convenient typedef for the often used 'location' object
|
||||
|
@ -129,12 +130,12 @@ protected:
|
|||
//! that all units can move their full movement allotment.
|
||||
//! 'remove_destinations': a pointer to a set of possible destinations to omit.
|
||||
void calculate_possible_moves(std::map<location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false,
|
||||
const std::set<location>* remove_destinations=NULL);
|
||||
const std::set<location>* remove_destinations=NULL) const;
|
||||
|
||||
//! A more fundamental version of calculate_possible_moves
|
||||
//! which allows the use of a speculative unit map.
|
||||
void calculate_moves(const unit_map& units, std::map<location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false,
|
||||
const std::set<location>* remove_destinations=NULL, bool see_all=false);
|
||||
const std::set<location>* remove_destinations=NULL, bool see_all=false) const;
|
||||
|
||||
//! Recruit a unit. It will recruit the unit with the given name,
|
||||
//! at the given location, or at an available location to recruit units
|
||||
|
@ -160,6 +161,9 @@ protected:
|
|||
void raise_unit_recruited() { unit_recruited_.notify_observers(); }
|
||||
void raise_unit_moved() { unit_moved_.notify_observers(); }
|
||||
void raise_enemy_attacked() { enemy_attacked_.notify_observers(); }
|
||||
protected:
|
||||
virtual void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
virtual variant get_value(const std::string& key) const;
|
||||
private:
|
||||
info info_;
|
||||
int last_interact_;
|
||||
|
|
129
src/callable_objects.cpp
Normal file
129
src/callable_objects.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
#include "callable_objects.hpp"
|
||||
#include "pathutils.hpp"
|
||||
|
||||
variant location_callable::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "x") {
|
||||
return variant(loc_.x+1);
|
||||
} else if(key == "y") {
|
||||
return variant(loc_.y+1);
|
||||
} else if(key == "adjacent_locs") {
|
||||
gamemap::location adj[6];
|
||||
get_adjacent_tiles(loc_, adj);
|
||||
|
||||
std::vector<variant> v;
|
||||
v.reserve(6);
|
||||
for(int n = 0; n != 6; ++n) {
|
||||
v.push_back(variant(new location_callable(adj[n])));
|
||||
}
|
||||
|
||||
return variant(&v);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void location_callable::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("x", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("y", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
||||
int location_callable::do_compare(const game_logic::formula_callable* callable) const
|
||||
{
|
||||
const location_callable* loc_callable = dynamic_cast<const location_callable*>(callable);
|
||||
if(loc_callable == NULL) {
|
||||
return formula_callable::do_compare(callable);
|
||||
}
|
||||
|
||||
const gamemap::location& other_loc = loc_callable->loc();
|
||||
if(other_loc.x != loc_.x) {
|
||||
return loc_.x - other_loc.x;
|
||||
}
|
||||
|
||||
return loc_.y - other_loc.y;
|
||||
}
|
||||
|
||||
variant move_map_callable::get_value(const std::string& key) const
|
||||
{
|
||||
using namespace game_logic;
|
||||
if(key == "moves") {
|
||||
std::vector<variant> vars;
|
||||
for(move_map::const_iterator i = srcdst_.begin(); i != srcdst_.end(); ++i) {
|
||||
move_callable* item = new move_callable(i->first, i->second);
|
||||
vars.push_back(variant(item));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
} else if(key == "has_moves") {
|
||||
return variant(!srcdst_.empty());
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void move_map_callable::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("moves", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
||||
variant unit_callable::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "x") {
|
||||
return variant(loc_.x+1);
|
||||
} else if(key == "y") {
|
||||
return variant(loc_.y+1);
|
||||
} else if(key == "loc") {
|
||||
return variant(new location_callable(loc_));
|
||||
} else if(key == "id") {
|
||||
return variant(u_.id());
|
||||
} else if(key == "leader") {
|
||||
return variant(u_.can_recruit());
|
||||
} else if(key == "hitpoints") {
|
||||
return variant(u_.hitpoints());
|
||||
} else if(key == "max_hitpoints") {
|
||||
return variant(u_.max_hitpoints());
|
||||
} else if(key == "experience") {
|
||||
return variant(u_.experience());
|
||||
} else if(key == "max_experience") {
|
||||
return variant(u_.max_experience());
|
||||
} else if(key == "level") {
|
||||
return variant(u_.level());
|
||||
} else if(key == "total_movement") {
|
||||
return variant(u_.total_movement());
|
||||
} else if(key == "movement_left") {
|
||||
return variant(u_.movement_left());
|
||||
} else if(key == "side") {
|
||||
return variant(u_.side());
|
||||
} else if(key == "is_enemy") {
|
||||
return variant(team_.is_enemy(u_.side()));
|
||||
} else if(key == "is_mine") {
|
||||
return variant(side_ == u_.side());
|
||||
} else if(key == "value") {
|
||||
return variant(u_.cost());
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void unit_callable::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("x", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("y", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("loc", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("id", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("leader", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("hitpoints", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("max_hitpoints", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("experience", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("max_experience", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("level", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("total_movement", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("movement_left", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("side", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("is_enemy", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("is_mine", FORMULA_READ_ONLY));
|
||||
}
|
116
src/callable_objects.hpp
Normal file
116
src/callable_objects.hpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#ifndef CALLABLE_OBJECTS_HPP_INCLUDED
|
||||
#define CALLABLE_OBJECTS_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "formula_callable.hpp"
|
||||
#include "map.hpp"
|
||||
#include "unit.hpp"
|
||||
|
||||
#define CALLABLE_WRAPPER_START(klass) \
|
||||
class klass##_callable : public game_logic::formula_callable { \
|
||||
const klass& object_; \
|
||||
public: \
|
||||
explicit klass##_callable(const klass& object) : object_(object) \
|
||||
{} \
|
||||
\
|
||||
const klass& get_##klass() const { return object_; } \
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
variant get_value(const std::string& key) const {
|
||||
|
||||
#define CALLABLE_WRAPPER_VAR(VAR) \
|
||||
if(key == #VAR) { \
|
||||
return variant(object_.VAR); \
|
||||
} else
|
||||
|
||||
#define CALLABLE_WRAPPER_FN(VAR) \
|
||||
if(key == #VAR) { \
|
||||
return variant(object_.VAR()); \
|
||||
} else
|
||||
|
||||
#define CALLABLE_WRAPPER_END \
|
||||
{ return variant(); } \
|
||||
} \
|
||||
};
|
||||
|
||||
CALLABLE_WRAPPER_START(gamemap)
|
||||
CALLABLE_WRAPPER_FN(w)
|
||||
CALLABLE_WRAPPER_FN(h)
|
||||
CALLABLE_WRAPPER_END
|
||||
|
||||
class location_callable : public game_logic::formula_callable {
|
||||
gamemap::location loc_;
|
||||
|
||||
variant get_value(const std::string& key) const;
|
||||
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
int do_compare(const game_logic::formula_callable* callable) const;
|
||||
|
||||
public:
|
||||
explicit location_callable(const gamemap::location& loc) : loc_(loc)
|
||||
{}
|
||||
|
||||
const gamemap::location& loc() const { return loc_; }
|
||||
};
|
||||
|
||||
class move_callable : public game_logic::formula_callable {
|
||||
gamemap::location src_, dst_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "src") {
|
||||
return variant(new location_callable(src_));
|
||||
} else if(key == "dst") {
|
||||
return variant(new location_callable(dst_));
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
|
||||
inputs->push_back(game_logic::formula_input("src", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("dst", game_logic::FORMULA_READ_ONLY));
|
||||
}
|
||||
public:
|
||||
move_callable(const gamemap::location& src, const gamemap::location& dst) :
|
||||
src_(src), dst_(dst)
|
||||
{}
|
||||
|
||||
const gamemap::location& src() const { return src_; }
|
||||
const gamemap::location& dst() const { return dst_; }
|
||||
};
|
||||
|
||||
class move_map_callable : public game_logic::formula_callable {
|
||||
typedef std::multimap<gamemap::location, gamemap::location> move_map;
|
||||
const move_map& srcdst_;
|
||||
const move_map& dstsrc_;
|
||||
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
public:
|
||||
move_map_callable(const move_map& srcdst, const move_map& dstsrc)
|
||||
: srcdst_(srcdst), dstsrc_(dstsrc)
|
||||
{}
|
||||
|
||||
const move_map& srcdst() const { return srcdst_; }
|
||||
const move_map& dstsrc() const { return dstsrc_; }
|
||||
};
|
||||
|
||||
class unit_callable : public game_logic::formula_callable {
|
||||
public:
|
||||
typedef gamemap::location location;
|
||||
unit_callable(const std::pair<location, unit>& pair, const team& current_team, int side)
|
||||
: loc_(pair.first), u_(pair.second), team_(current_team), side_(side)
|
||||
{}
|
||||
|
||||
const unit& get_unit() const { return u_; }
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
private:
|
||||
const location& loc_;
|
||||
const unit& u_;
|
||||
const team& team_;
|
||||
int side_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -28,7 +28,7 @@ class unit_map;
|
|||
namespace gui{
|
||||
|
||||
enum TEXTBOX_MODE { TEXTBOX_NONE, TEXTBOX_SEARCH, TEXTBOX_MESSAGE,
|
||||
TEXTBOX_COMMAND };
|
||||
TEXTBOX_COMMAND, TEXTBOX_AI };
|
||||
|
||||
class floating_textbox{
|
||||
public:
|
||||
|
|
782
src/foreach.hpp
Normal file
782
src/foreach.hpp
Normal file
|
@ -0,0 +1,782 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// foreach.hpp header file
|
||||
//
|
||||
// Copyright 2004 Eric Niebler.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_FOREACH
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility> // for std::pair
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
|
||||
// Some compilers let us detect even const-qualified rvalues at compile-time
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1310) \
|
||||
|| BOOST_WORKAROUND(__GNUC__, >= 4) \
|
||||
|| (BOOST_WORKAROUND(__GNUC__, == 3) && (__GNUC_MINOR__ >= 4))
|
||||
# define BOOST_FOREACH_COMPILE_TIME_CONST_RVALUE_DETECTION
|
||||
#else
|
||||
// Some compilers allow temporaries to be bound to non-const references.
|
||||
// These compilers make it impossible to for BOOST_FOREACH to detect
|
||||
// temporaries and avoid reevaluation of the collection expression.
|
||||
# if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) \
|
||||
|| BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) \
|
||||
|| (BOOST_WORKAROUND(BOOST_INTEL_CXX_VERSION, <= 700) && defined(_MSC_VER)) \
|
||||
|| BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x570)) \
|
||||
|| BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
|
||||
# define BOOST_FOREACH_NO_RVALUE_DETECTION
|
||||
# endif
|
||||
// Some compilers do not correctly implement the lvalue/rvalue conversion
|
||||
// rules of the ternary conditional operator.
|
||||
# if defined(BOOST_FOREACH_NO_RVALUE_DETECTION) \
|
||||
|| defined(BOOST_NO_SFINAE) \
|
||||
|| BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1400)) \
|
||||
|| BOOST_WORKAROUND(BOOST_INTEL_WIN, <= 810) \
|
||||
|| BOOST_WORKAROUND(__GNUC__, < 3) \
|
||||
|| (BOOST_WORKAROUND(__GNUC__, == 3) && (__GNUC_MINOR__ <= 2)) \
|
||||
|| (BOOST_WORKAROUND(__GNUC__, == 3) && (__GNUC_MINOR__ <= 3) && defined(__APPLE_CC__)) \
|
||||
|| BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600)) \
|
||||
|| BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3206))
|
||||
# define BOOST_FOREACH_NO_CONST_RVALUE_DETECTION
|
||||
# else
|
||||
# define BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <boost/mpl/if.hpp>
|
||||
#include <boost/mpl/logical.hpp>
|
||||
#include <boost/mpl/eval_if.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/range/end.hpp>
|
||||
#include <boost/range/begin.hpp>
|
||||
#include <boost/range/result_iterator.hpp>
|
||||
#include <boost/type_traits/is_array.hpp>
|
||||
#include <boost/type_traits/is_const.hpp>
|
||||
#include <boost/type_traits/is_base_and_derived.hpp>
|
||||
#include <boost/iterator/iterator_traits.hpp>
|
||||
#include <boost/utility/addressof.hpp>
|
||||
|
||||
#ifdef BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION
|
||||
# include <new>
|
||||
# include <boost/aligned_storage.hpp>
|
||||
# include <boost/utility/enable_if.hpp>
|
||||
# include <boost/type_traits/remove_const.hpp>
|
||||
#endif
|
||||
|
||||
// This must be at global scope, hence the uglified name
|
||||
enum boost_foreach_argument_dependent_lookup_hack
|
||||
{
|
||||
boost_foreach_argument_dependent_lookup_hack_value
|
||||
};
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
// forward declarations for iterator_range
|
||||
template<typename T>
|
||||
class iterator_range;
|
||||
|
||||
// forward declarations for sub_range
|
||||
template<typename T>
|
||||
class sub_range;
|
||||
|
||||
namespace foreach
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// in_range
|
||||
//
|
||||
template<typename T>
|
||||
inline std::pair<T, T> in_range(T begin, T end)
|
||||
{
|
||||
return std::make_pair(begin, end);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// boost::foreach::tag
|
||||
//
|
||||
typedef boost_foreach_argument_dependent_lookup_hack tag;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// boost::foreach::is_lightweight_proxy
|
||||
// Specialize this for user-defined collection types if they are inexpensive to copy.
|
||||
// This tells BOOST_FOREACH it can avoid the rvalue/lvalue detection stuff.
|
||||
template<typename T>
|
||||
struct is_lightweight_proxy
|
||||
: boost::mpl::false_
|
||||
{
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// boost::foreach::is_noncopyable
|
||||
// Specialize this for user-defined collection types if they cannot be copied.
|
||||
// This also tells BOOST_FOREACH to avoid the rvalue/lvalue detection stuff.
|
||||
template<typename T>
|
||||
struct is_noncopyable
|
||||
#ifndef BOOST_BROKEN_IS_BASE_AND_DERIVED
|
||||
: boost::is_base_and_derived<boost::noncopyable, T>
|
||||
#else
|
||||
: boost::mpl::false_
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace foreach
|
||||
|
||||
} // namespace boost
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// boost_foreach_is_lightweight_proxy
|
||||
// Another customization point for the is_lightweight_proxy optimization,
|
||||
// this one works on legacy compilers. Overload boost_foreach_is_lightweight_proxy
|
||||
// at the global namespace for your type.
|
||||
template<typename T>
|
||||
inline boost::foreach::is_lightweight_proxy<T> *
|
||||
boost_foreach_is_lightweight_proxy(T *&, ...) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *
|
||||
boost_foreach_is_lightweight_proxy(std::pair<T, T> *&, boost::foreach::tag) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *
|
||||
boost_foreach_is_lightweight_proxy(boost::iterator_range<T> *&, boost::foreach::tag) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *
|
||||
boost_foreach_is_lightweight_proxy(boost::sub_range<T> *&, boost::foreach::tag) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *
|
||||
boost_foreach_is_lightweight_proxy(T **, boost::foreach::tag) { return 0; }
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
inline boost::mpl::false_ *
|
||||
boost_foreach_is_lightweight_proxy(T (*)[N], boost::foreach::tag) { return 0; }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// boost_foreach_is_noncopyable
|
||||
// Another customization point for the is_noncopyable trait,
|
||||
// this one works on legacy compilers. Overload boost_foreach_is_noncopyable
|
||||
// at the global namespace for your type.
|
||||
template<typename T>
|
||||
inline boost::foreach::is_noncopyable<T> *
|
||||
boost_foreach_is_noncopyable(T *&, ...) { return 0; }
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
namespace foreach_detail_
|
||||
{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Define some utilities for assessing the properties of expressions
|
||||
//
|
||||
typedef char yes_type;
|
||||
typedef char (&no_type)[2];
|
||||
yes_type is_true(boost::mpl::true_ *);
|
||||
no_type is_true(boost::mpl::false_ *);
|
||||
|
||||
// Extracts the desired property from the expression without evaluating it
|
||||
#define BOOST_FOREACH_PROTECT(expr) \
|
||||
(static_cast<boost::mpl::bool_<1 == sizeof(boost::foreach_detail_::is_true(expr))> *>(0))
|
||||
|
||||
template<typename Bool1, typename Bool2>
|
||||
inline boost::mpl::and_<Bool1, Bool2> *and_(Bool1 *, Bool2 *) { return 0; }
|
||||
|
||||
template<typename Bool1, typename Bool2, typename Bool3>
|
||||
inline boost::mpl::and_<Bool1, Bool2, Bool3> *and_(Bool1 *, Bool2 *, Bool3 *) { return 0; }
|
||||
|
||||
template<typename Bool1, typename Bool2>
|
||||
inline boost::mpl::or_<Bool1, Bool2> *or_(Bool1 *, Bool2 *) { return 0; }
|
||||
|
||||
template<typename Bool1, typename Bool2, typename Bool3>
|
||||
inline boost::mpl::or_<Bool1, Bool2, Bool3> *or_(Bool1 *, Bool2 *, Bool3 *) { return 0; }
|
||||
|
||||
template<typename Bool>
|
||||
inline boost::mpl::not_<Bool> *not_(Bool *) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::false_ *is_rvalue(T &, int) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *is_rvalue(T const &, ...) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::is_array<T> *is_array(T const &) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline boost::is_const<T> *is_const(T &) { return 0; }
|
||||
|
||||
#ifndef BOOST_FOREACH_NO_RVALUE_DETECTION
|
||||
template<typename T>
|
||||
inline boost::mpl::true_ *is_const(T const &) { return 0; }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// auto_any_t/auto_any
|
||||
// General utility for putting an object of any type into automatic storage
|
||||
struct auto_any_base
|
||||
{
|
||||
// auto_any_base must evaluate to false in boolean context so that
|
||||
// they can be declared in if() statements.
|
||||
operator bool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct auto_any : auto_any_base
|
||||
{
|
||||
auto_any(T const &t)
|
||||
: item(t)
|
||||
{
|
||||
}
|
||||
|
||||
// temporaries of type auto_any will be bound to const auto_any_base
|
||||
// references, but we still want to be able to mutate the stored
|
||||
// data, so declare it as mutable.
|
||||
mutable T item;
|
||||
};
|
||||
|
||||
typedef auto_any_base const &auto_any_t;
|
||||
|
||||
template<typename T, typename C>
|
||||
inline BOOST_DEDUCED_TYPENAME boost::mpl::if_<C, T const, T>::type &auto_any_cast(auto_any_t a)
|
||||
{
|
||||
return static_cast<auto_any<T> const &>(a).item;
|
||||
}
|
||||
|
||||
typedef boost::mpl::true_ const_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// type2type
|
||||
//
|
||||
template<typename T, typename C = boost::mpl::false_>
|
||||
struct type2type
|
||||
: boost::mpl::if_<C, T const, T>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T, typename C = boost::mpl::false_>
|
||||
struct foreach_iterator
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::mpl::eval_if<
|
||||
C
|
||||
, range_const_iterator<T>
|
||||
, range_iterator<T>
|
||||
>::type type;
|
||||
};
|
||||
|
||||
template<typename T, typename C = boost::mpl::false_>
|
||||
struct foreach_reference
|
||||
: iterator_reference<BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type>
|
||||
{
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// encode_type
|
||||
//
|
||||
template<typename T>
|
||||
inline type2type<T> *encode_type(T &, boost::mpl::false_ *) { return 0; }
|
||||
|
||||
template<typename T>
|
||||
inline type2type<T, const_> *encode_type(T const &, boost::mpl::true_ *) { return 0; }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// set_false
|
||||
//
|
||||
inline bool set_false(bool &b) { return b = false; }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// to_ptr
|
||||
//
|
||||
template<typename T>
|
||||
inline T *&to_ptr(T const &)
|
||||
{
|
||||
static T *t = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
// Borland needs a little extra help with arrays
|
||||
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
|
||||
template<typename T,std::size_t N>
|
||||
inline T (*to_ptr(T (&t)[N]))[N] { return 0; }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// derefof
|
||||
//
|
||||
template<typename T>
|
||||
inline T &derefof(T *t)
|
||||
{
|
||||
// This is a work-around for a compiler bug in Borland. If T* is a pointer to array type U(*)[N],
|
||||
// then dereferencing it results in a U* instead of U(&)[N]. The cast forces the issue.
|
||||
return reinterpret_cast<T &>(
|
||||
*const_cast<char *>(
|
||||
reinterpret_cast<char const volatile *>(t)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef BOOST_FOREACH_COMPILE_TIME_CONST_RVALUE_DETECTION
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Detect at compile-time whether an expression yields an rvalue or
|
||||
// an lvalue. This is rather non-standard, but some popular compilers
|
||||
// accept it.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// rvalue_probe
|
||||
//
|
||||
template<typename T>
|
||||
struct rvalue_probe
|
||||
{
|
||||
// can't ever return an array by value
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<boost::is_array<T>, int, T>::type value_type;
|
||||
operator value_type();
|
||||
operator T &() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
rvalue_probe<T> const make_probe(T const &t);
|
||||
|
||||
# define BOOST_FOREACH_IS_RVALUE(COL) \
|
||||
boost::foreach_detail_::and_( \
|
||||
boost::foreach_detail_::not_(boost::foreach_detail_::is_array(COL)) \
|
||||
, BOOST_FOREACH_PROTECT(boost::foreach_detail_::is_rvalue( \
|
||||
(true ? boost::foreach_detail_::make_probe(COL) : (COL)), 0)))
|
||||
|
||||
#elif defined(BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Detect at run-time whether an expression yields an rvalue
|
||||
// or an lvalue. This is 100% standard C++, but not all compilers
|
||||
// accept it. Also, it causes FOREACH to break when used with non-
|
||||
// copyable collection types.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// rvalue_probe
|
||||
//
|
||||
template<typename T>
|
||||
struct rvalue_probe
|
||||
{
|
||||
rvalue_probe(T &t, bool &b)
|
||||
: value(t)
|
||||
, is_rvalue(b)
|
||||
{
|
||||
}
|
||||
|
||||
// can't ever return an array by value
|
||||
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<boost::is_array<T>, int, T>::type value_type;
|
||||
operator value_type()
|
||||
{
|
||||
this->is_rvalue = true;
|
||||
return this->value;
|
||||
}
|
||||
|
||||
operator T &() const
|
||||
{
|
||||
return this->value;
|
||||
}
|
||||
|
||||
private:
|
||||
T &value;
|
||||
bool &is_rvalue;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
rvalue_probe<T> make_probe(T &t, bool &b) { return rvalue_probe<T>(t, b); }
|
||||
|
||||
template<typename T>
|
||||
rvalue_probe<T const> make_probe(T const &t, bool &b) { return rvalue_probe<T const>(t, b); }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// simple_variant
|
||||
// holds either a T or a T const*
|
||||
template<typename T>
|
||||
struct simple_variant
|
||||
{
|
||||
simple_variant(T const *t)
|
||||
: is_rvalue(false)
|
||||
{
|
||||
*static_cast<T const **>(this->data.address()) = t;
|
||||
}
|
||||
|
||||
simple_variant(T const &t)
|
||||
: is_rvalue(true)
|
||||
{
|
||||
::new(this->data.address()) T(t);
|
||||
}
|
||||
|
||||
simple_variant(simple_variant const &that)
|
||||
: is_rvalue(that.is_rvalue)
|
||||
{
|
||||
if(this->is_rvalue)
|
||||
::new(this->data.address()) T(*that.get());
|
||||
else
|
||||
*static_cast<T const **>(this->data.address()) = that.get();
|
||||
}
|
||||
|
||||
~simple_variant()
|
||||
{
|
||||
if(this->is_rvalue)
|
||||
this->get()->~T();
|
||||
}
|
||||
|
||||
T const *get() const
|
||||
{
|
||||
if(this->is_rvalue)
|
||||
return static_cast<T const *>(this->data.address());
|
||||
else
|
||||
return *static_cast<T const * const *>(this->data.address());
|
||||
}
|
||||
|
||||
private:
|
||||
enum size_type { size = sizeof(T) > sizeof(T*) ? sizeof(T) : sizeof(T*) };
|
||||
simple_variant &operator =(simple_variant const &);
|
||||
bool const is_rvalue;
|
||||
aligned_storage<size> data;
|
||||
};
|
||||
|
||||
// If the collection is an array or is noncopyable, it must be an lvalue.
|
||||
// If the collection is a lightweight proxy, treat it as an rvalue
|
||||
// BUGBUG what about a noncopyable proxy?
|
||||
template<typename LValue, typename IsProxy>
|
||||
inline BOOST_DEDUCED_TYPENAME boost::enable_if<boost::mpl::or_<LValue, IsProxy>, IsProxy>::type *
|
||||
should_copy_impl(LValue *, IsProxy *, bool *)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Otherwise, we must determine at runtime whether it's an lvalue or rvalue
|
||||
inline bool *
|
||||
should_copy_impl(boost::mpl::false_ *, boost::mpl::false_ *, bool *is_rvalue)
|
||||
{
|
||||
return is_rvalue;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// contain
|
||||
//
|
||||
template<typename T>
|
||||
inline auto_any<T> contain(T const &t, boost::mpl::true_ *) // rvalue
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto_any<T *> contain(T &t, boost::mpl::false_ *) // lvalue
|
||||
{
|
||||
// Cannot seem to get sunpro to handle addressof() with array types.
|
||||
#if BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x570))
|
||||
return &t;
|
||||
#else
|
||||
return boost::addressof(t);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION
|
||||
template<typename T>
|
||||
auto_any<simple_variant<T> >
|
||||
contain(T const &t, bool *rvalue)
|
||||
{
|
||||
return *rvalue ? simple_variant<T>(t) : simple_variant<T>(&t);
|
||||
}
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// begin
|
||||
//
|
||||
template<typename T, typename C>
|
||||
inline auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type>
|
||||
begin(auto_any_t col, type2type<T, C> *, boost::mpl::true_ *) // rvalue
|
||||
{
|
||||
return boost::begin(auto_any_cast<T, C>(col));
|
||||
}
|
||||
|
||||
template<typename T, typename C>
|
||||
inline auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type>
|
||||
begin(auto_any_t col, type2type<T, C> *, boost::mpl::false_ *) // lvalue
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME type2type<T, C>::type type;
|
||||
typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iterator;
|
||||
return iterator(boost::begin(derefof(auto_any_cast<type *, boost::mpl::false_>(col))));
|
||||
}
|
||||
|
||||
#ifdef BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION
|
||||
template<typename T>
|
||||
auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, const_>::type>
|
||||
begin(auto_any_t col, type2type<T, const_> *, bool *)
|
||||
{
|
||||
return boost::begin(*auto_any_cast<simple_variant<T>, boost::mpl::false_>(col).get());
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// end
|
||||
//
|
||||
template<typename T, typename C>
|
||||
inline auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type>
|
||||
end(auto_any_t col, type2type<T, C> *, boost::mpl::true_ *) // rvalue
|
||||
{
|
||||
return boost::end(auto_any_cast<T, C>(col));
|
||||
}
|
||||
|
||||
template<typename T, typename C>
|
||||
inline auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type>
|
||||
end(auto_any_t col, type2type<T, C> *, boost::mpl::false_ *) // lvalue
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME type2type<T, C>::type type;
|
||||
typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iterator;
|
||||
return iterator(boost::end(derefof(auto_any_cast<type *, boost::mpl::false_>(col))));
|
||||
}
|
||||
|
||||
#ifdef BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION
|
||||
template<typename T>
|
||||
auto_any<BOOST_DEDUCED_TYPENAME foreach_iterator<T, const_>::type>
|
||||
end(auto_any_t col, type2type<T, const_> *, bool *)
|
||||
{
|
||||
return boost::end(*auto_any_cast<simple_variant<T>, boost::mpl::false_>(col).get());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
|
||||
template<typename T, typename C>
|
||||
inline auto_any<int>
|
||||
end(auto_any_t col, type2type<T *, C> *, boost::mpl::true_ *) // null-terminated C-style strings
|
||||
{
|
||||
return 0; // not used
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// done
|
||||
//
|
||||
template<typename T, typename C>
|
||||
inline bool done(auto_any_t cur, auto_any_t end, type2type<T, C> *)
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iter_t;
|
||||
return auto_any_cast<iter_t, boost::mpl::false_>(cur) == auto_any_cast<iter_t, boost::mpl::false_>(end);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
|
||||
template<typename T, typename C>
|
||||
inline bool done(auto_any_t cur, auto_any_t, type2type<T *, C> *) // null-terminated C-style strings
|
||||
{
|
||||
return ! *auto_any_cast<T *, boost::mpl::false_>(cur);
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// next
|
||||
//
|
||||
template<typename T, typename C>
|
||||
inline void next(auto_any_t cur, type2type<T, C> *)
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iter_t;
|
||||
++auto_any_cast<iter_t, boost::mpl::false_>(cur);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// deref
|
||||
//
|
||||
template<typename T, typename C>
|
||||
inline BOOST_DEDUCED_TYPENAME foreach_reference<T, C>::type
|
||||
deref(auto_any_t cur, type2type<T, C> *)
|
||||
{
|
||||
typedef BOOST_DEDUCED_TYPENAME foreach_iterator<T, C>::type iter_t;
|
||||
return *auto_any_cast<iter_t, boost::mpl::false_>(cur);
|
||||
}
|
||||
|
||||
} // namespace foreach_detail_
|
||||
} // namespace boost
|
||||
|
||||
// A sneaky way to get the type of the collection without evaluating the expression
|
||||
#define BOOST_FOREACH_TYPEOF(COL) \
|
||||
(true ? 0 : boost::foreach_detail_::encode_type(COL, boost::foreach_detail_::is_const(COL)))
|
||||
|
||||
// returns true_* if the type is noncopyable
|
||||
#define BOOST_FOREACH_IS_NONCOPYABLE(COL) \
|
||||
boost_foreach_is_noncopyable( \
|
||||
boost::foreach_detail_::to_ptr(COL) \
|
||||
, boost_foreach_argument_dependent_lookup_hack_value)
|
||||
|
||||
// returns true_* if the type is a lightweight proxy (and is not noncopyable)
|
||||
#define BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL) \
|
||||
boost::foreach_detail_::and_( \
|
||||
boost::foreach_detail_::not_(BOOST_FOREACH_IS_NONCOPYABLE(COL)) \
|
||||
, boost_foreach_is_lightweight_proxy( \
|
||||
boost::foreach_detail_::to_ptr(COL) \
|
||||
, boost_foreach_argument_dependent_lookup_hack_value))
|
||||
|
||||
#ifdef BOOST_FOREACH_COMPILE_TIME_CONST_RVALUE_DETECTION
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// R-values and const R-values supported here with zero runtime overhead
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// No variable is needed to track the rvalue-ness of the collection expression
|
||||
# define BOOST_FOREACH_PREAMBLE() \
|
||||
/**/
|
||||
|
||||
// Evaluate the collection expression
|
||||
# define BOOST_FOREACH_EVALUATE(COL) \
|
||||
(COL)
|
||||
|
||||
# define BOOST_FOREACH_SHOULD_COPY(COL) \
|
||||
(true ? 0 : boost::foreach_detail_::or_( \
|
||||
BOOST_FOREACH_IS_RVALUE(COL) \
|
||||
, BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL)))
|
||||
|
||||
#elif defined(BOOST_FOREACH_RUN_TIME_CONST_RVALUE_DETECTION)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// R-values and const R-values supported here
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Declare a variable to track the rvalue-ness of the collection expression
|
||||
# define BOOST_FOREACH_PREAMBLE() \
|
||||
if (bool _foreach_is_rvalue = false) {} else
|
||||
|
||||
// Evaluate the collection expression, and detect if it is an lvalue or and rvalue
|
||||
# define BOOST_FOREACH_EVALUATE(COL) \
|
||||
(true ? boost::foreach_detail_::make_probe((COL), _foreach_is_rvalue) : (COL))
|
||||
|
||||
// The rvalue/lvalue-ness of the collection expression is determined dynamically, unless
|
||||
// type type is an array or is noncopyable or is non-const, in which case we know it's an lvalue.
|
||||
// If the type happens to be a lightweight proxy, always make a copy.
|
||||
# define BOOST_FOREACH_SHOULD_COPY(COL) \
|
||||
(boost::foreach_detail_::should_copy_impl( \
|
||||
true ? 0 : boost::foreach_detail_::or_( \
|
||||
boost::foreach_detail_::is_array(COL) \
|
||||
, BOOST_FOREACH_IS_NONCOPYABLE(COL) \
|
||||
, boost::foreach_detail_::not_(boost::foreach_detail_::is_const(COL))) \
|
||||
, true ? 0 : BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL) \
|
||||
, &_foreach_is_rvalue))
|
||||
|
||||
#elif !defined(BOOST_FOREACH_NO_RVALUE_DETECTION)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// R-values supported here, const R-values NOT supported here
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// No variable is needed to track the rvalue-ness of the collection expression
|
||||
# define BOOST_FOREACH_PREAMBLE() \
|
||||
/**/
|
||||
|
||||
// Evaluate the collection expression
|
||||
# define BOOST_FOREACH_EVALUATE(COL) \
|
||||
(COL)
|
||||
|
||||
// Determine whether the collection expression is an lvalue or an rvalue.
|
||||
// NOTE: this gets the answer wrong for const rvalues.
|
||||
# define BOOST_FOREACH_SHOULD_COPY(COL) \
|
||||
(true ? 0 : boost::foreach_detail_::or_( \
|
||||
boost::foreach_detail_::is_rvalue((COL), 0) \
|
||||
, BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL)))
|
||||
|
||||
#else
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// R-values NOT supported here
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// No variable is needed to track the rvalue-ness of the collection expression
|
||||
# define BOOST_FOREACH_PREAMBLE() \
|
||||
/**/
|
||||
|
||||
// Evaluate the collection expression
|
||||
# define BOOST_FOREACH_EVALUATE(COL) \
|
||||
(COL)
|
||||
|
||||
// Can't use rvalues with BOOST_FOREACH (unless they are lightweight proxies)
|
||||
# define BOOST_FOREACH_SHOULD_COPY(COL) \
|
||||
(true ? 0 : BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL))
|
||||
|
||||
#endif
|
||||
|
||||
#define BOOST_FOREACH_CONTAIN(COL) \
|
||||
boost::foreach_detail_::contain( \
|
||||
BOOST_FOREACH_EVALUATE(COL) \
|
||||
, BOOST_FOREACH_SHOULD_COPY(COL))
|
||||
|
||||
#define BOOST_FOREACH_BEGIN(COL) \
|
||||
boost::foreach_detail_::begin( \
|
||||
_foreach_col \
|
||||
, BOOST_FOREACH_TYPEOF(COL) \
|
||||
, BOOST_FOREACH_SHOULD_COPY(COL))
|
||||
|
||||
#define BOOST_FOREACH_END(COL) \
|
||||
boost::foreach_detail_::end( \
|
||||
_foreach_col \
|
||||
, BOOST_FOREACH_TYPEOF(COL) \
|
||||
, BOOST_FOREACH_SHOULD_COPY(COL))
|
||||
|
||||
#define BOOST_FOREACH_DONE(COL) \
|
||||
boost::foreach_detail_::done( \
|
||||
_foreach_cur \
|
||||
, _foreach_end \
|
||||
, BOOST_FOREACH_TYPEOF(COL))
|
||||
|
||||
#define BOOST_FOREACH_NEXT(COL) \
|
||||
boost::foreach_detail_::next( \
|
||||
_foreach_cur \
|
||||
, BOOST_FOREACH_TYPEOF(COL))
|
||||
|
||||
#define BOOST_FOREACH_DEREF(COL) \
|
||||
boost::foreach_detail_::deref( \
|
||||
_foreach_cur \
|
||||
, BOOST_FOREACH_TYPEOF(COL))
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BOOST_FOREACH
|
||||
//
|
||||
// For iterating over collections. Collections can be
|
||||
// arrays, null-terminated strings, or STL containers.
|
||||
// The loop variable can be a value or reference. For
|
||||
// example:
|
||||
//
|
||||
// std::list<int> int_list(/*stuff*/);
|
||||
// BOOST_FOREACH(int &i, int_list)
|
||||
// {
|
||||
// /*
|
||||
// * loop body goes here.
|
||||
// * i is a reference to the int in int_list.
|
||||
// */
|
||||
// }
|
||||
//
|
||||
// Alternately, you can declare the loop variable first,
|
||||
// so you can access it after the loop finishes. Obviously,
|
||||
// if you do it this way, then the loop variable cannot be
|
||||
// a reference.
|
||||
//
|
||||
// int i;
|
||||
// BOOST_FOREACH(i, int_list)
|
||||
// { ... }
|
||||
//
|
||||
#define BOOST_FOREACH(VAR, COL) \
|
||||
BOOST_FOREACH_PREAMBLE() \
|
||||
if (boost::foreach_detail_::auto_any_t _foreach_col = BOOST_FOREACH_CONTAIN(COL)) {} else \
|
||||
if (boost::foreach_detail_::auto_any_t _foreach_cur = BOOST_FOREACH_BEGIN(COL)) {} else \
|
||||
if (boost::foreach_detail_::auto_any_t _foreach_end = BOOST_FOREACH_END(COL)) {} else \
|
||||
for (bool _foreach_continue = true; \
|
||||
_foreach_continue && !BOOST_FOREACH_DONE(COL); \
|
||||
_foreach_continue ? BOOST_FOREACH_NEXT(COL) : (void)0) \
|
||||
if (boost::foreach_detail_::set_false(_foreach_continue)) {} else \
|
||||
for (VAR = BOOST_FOREACH_DEREF(COL); !_foreach_continue; _foreach_continue = true)
|
||||
|
||||
#ifdef foreach
|
||||
#undef foreach
|
||||
#endif
|
||||
#define foreach BOOST_FOREACH
|
||||
|
||||
#endif
|
42
src/formatter.hpp
Normal file
42
src/formatter.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2007 by David White <dave.net>
|
||||
Part of the Silver Tree Project
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 or later.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef FORMATTER_HPP_INCLUDED
|
||||
#define FORMATTER_HPP_INCLUDED
|
||||
|
||||
#include <sstream>
|
||||
|
||||
class formatter
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
formatter& operator<<(const T& o) {
|
||||
stream_ << o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string str() {
|
||||
return stream_.str();
|
||||
}
|
||||
|
||||
const char* c_str() {
|
||||
return str().c_str();
|
||||
}
|
||||
|
||||
operator std::string() {
|
||||
return stream_.str();
|
||||
}
|
||||
private:
|
||||
std::ostringstream stream_;
|
||||
};
|
||||
|
||||
#endif
|
717
src/formula.cpp
Normal file
717
src/formula.cpp
Normal file
|
@ -0,0 +1,717 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2007 by David White <dave.net>
|
||||
Part of the Silver Tree Project
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 or later.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "foreach.hpp"
|
||||
#include "formula.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
#include "formula_function.hpp"
|
||||
#include "formula_tokenizer.hpp"
|
||||
#include "map_utils.hpp"
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
|
||||
void formula_callable::set_value(const std::string& key, const variant& value)
|
||||
{
|
||||
std::cerr << "ERROR: cannot set key '" << key << "' on object\n";
|
||||
}
|
||||
|
||||
map_formula_callable::map_formula_callable(
|
||||
const formula_callable* fallback) : fallback_(fallback)
|
||||
{}
|
||||
|
||||
map_formula_callable& map_formula_callable::add(const std::string& key,
|
||||
const variant& value)
|
||||
{
|
||||
values_[key] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
variant map_formula_callable::get_value(const std::string& key) const
|
||||
{
|
||||
return map_get_value_default(values_, key,
|
||||
fallback_ ? fallback_->query_value(key) : variant());
|
||||
}
|
||||
|
||||
void map_formula_callable::get_inputs(std::vector<formula_input>* inputs) const
|
||||
{
|
||||
if(fallback_) {
|
||||
fallback_->get_inputs(inputs);
|
||||
}
|
||||
for(std::map<std::string,variant>::const_iterator i = values_.begin(); i != values_.end(); ++i) {
|
||||
inputs->push_back(formula_input(i->first, FORMULA_READ_WRITE));
|
||||
}
|
||||
}
|
||||
|
||||
void map_formula_callable::set_value(const std::string& key, const variant& value)
|
||||
{
|
||||
values_[key] = value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class list_expression : public formula_expression {
|
||||
public:
|
||||
explicit list_expression(const std::vector<expression_ptr>& items)
|
||||
: items_(items)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
std::vector<variant> res;
|
||||
res.reserve(items_.size());
|
||||
for(std::vector<expression_ptr>::const_iterator i = items_.begin(); i != items_.end(); ++i) {
|
||||
res.push_back((*i)->evaluate(variables));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
}
|
||||
|
||||
std::vector<expression_ptr> items_;
|
||||
};
|
||||
|
||||
class unary_operator_expression : public formula_expression {
|
||||
public:
|
||||
unary_operator_expression(const std::string& op, expression_ptr arg)
|
||||
: operand_(arg)
|
||||
{
|
||||
if(op == "not") {
|
||||
op_ = NOT;
|
||||
} else if(op == "-") {
|
||||
op_ = SUB;
|
||||
} else {
|
||||
std::cerr << "illegal unary operator: '" << op << "'\n";
|
||||
throw formula_error();
|
||||
}
|
||||
}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant res = operand_->evaluate(variables);
|
||||
switch(op_) {
|
||||
case NOT: return res.as_bool() ? variant(0) : variant(1);
|
||||
case SUB: return -res;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
enum OP { NOT, SUB };
|
||||
OP op_;
|
||||
expression_ptr operand_;
|
||||
};
|
||||
|
||||
class list_callable : public formula_callable {
|
||||
variant list_;
|
||||
public:
|
||||
explicit list_callable(const variant& list) : list_(list)
|
||||
{}
|
||||
|
||||
void get_inputs(std::vector<formula_input>* inputs) const {
|
||||
inputs->push_back(formula_input("size", FORMULA_READ_WRITE));
|
||||
inputs->push_back(formula_input("empty", FORMULA_READ_WRITE));
|
||||
inputs->push_back(formula_input("first", FORMULA_READ_WRITE));
|
||||
inputs->push_back(formula_input("last", FORMULA_READ_WRITE));
|
||||
}
|
||||
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "size") {
|
||||
return variant(list_.num_elements());
|
||||
} else if(key == "empty") {
|
||||
return variant(list_.num_elements() == 0);
|
||||
} else if(key == "first") {
|
||||
if(list_.num_elements() > 0) {
|
||||
return list_[0];
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
} else if(key == "last") {
|
||||
if(list_.num_elements() > 0) {
|
||||
return list_[list_.num_elements()-1];
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class dot_expression : public formula_expression {
|
||||
public:
|
||||
dot_expression(expression_ptr left, expression_ptr right)
|
||||
: left_(left), right_(right)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant left = left_->evaluate(variables);
|
||||
if(!left.is_callable()) {
|
||||
if(left.is_list()) {
|
||||
return right_->evaluate(list_callable(left));
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
return right_->evaluate(*left.as_callable());
|
||||
}
|
||||
|
||||
expression_ptr left_, right_;
|
||||
};
|
||||
|
||||
class operator_expression : public formula_expression {
|
||||
public:
|
||||
operator_expression(const std::string& op, expression_ptr left,
|
||||
expression_ptr right)
|
||||
: op_(OP(op[0])), left_(left), right_(right)
|
||||
{
|
||||
if(op == ">=") {
|
||||
op_ = GTE;
|
||||
} else if(op == "<=") {
|
||||
op_ = LTE;
|
||||
} else if(op == "!=") {
|
||||
op_ = NEQ;
|
||||
} else if(op == "and") {
|
||||
op_ = AND;
|
||||
} else if(op == "or") {
|
||||
op_ = OR;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant left = left_->evaluate(variables);
|
||||
const variant right = right_->evaluate(variables);
|
||||
switch(op_) {
|
||||
case AND: return left.as_bool() == false ? left : right;
|
||||
case OR: return left.as_bool() ? left : right;
|
||||
case ADD: return left + right;
|
||||
case SUB: return left - right;
|
||||
case MUL: return left * right;
|
||||
case DIV: return left / right;
|
||||
case POW: return left ^ right;
|
||||
case EQ: return left == right ? variant(1) : variant(0);
|
||||
case NEQ: return left != right ? variant(1) : variant(0);
|
||||
case LTE: return left <= right ? variant(1) : variant(0);
|
||||
case GTE: return left >= right ? variant(1) : variant(0);
|
||||
case LT: return left < right ? variant(1) : variant(0);
|
||||
case GT: return left > right ? variant(1) : variant(0);
|
||||
case MOD: return left % right;
|
||||
case DICE:return variant(dice_roll(left.as_int(), right.as_int()));
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int dice_roll(int num_rolls, int faces) {
|
||||
int res = 0;
|
||||
while(faces > 0 && num_rolls-- > 0) {
|
||||
res += (rand()%faces)+1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
enum OP { AND, OR, NEQ, LTE, GTE, GT='>', LT='<', EQ='=',
|
||||
ADD='+', SUB='-', MUL='*', DIV='/', DICE='d', POW='^', MOD='%' };
|
||||
|
||||
OP op_;
|
||||
expression_ptr left_, right_;
|
||||
};
|
||||
|
||||
typedef std::map<std::string,expression_ptr> expr_table;
|
||||
typedef boost::shared_ptr<expr_table> expr_table_ptr;
|
||||
|
||||
class where_variables: public formula_callable {
|
||||
public:
|
||||
where_variables(const formula_callable &base,
|
||||
expr_table_ptr table )
|
||||
: base_(base), table_(table) { }
|
||||
private:
|
||||
const formula_callable& base_;
|
||||
expr_table_ptr table_;
|
||||
|
||||
void get_inputs(std::vector<formula_input>* inputs) const {
|
||||
for(expr_table::const_iterator i = table_->begin(); i != table_->end(); ++i) {
|
||||
inputs->push_back(formula_input(i->first, FORMULA_READ_ONLY));
|
||||
}
|
||||
}
|
||||
|
||||
variant get_value(const std::string& key) const {
|
||||
expr_table::iterator i = table_->find(key);
|
||||
if(i != table_->end()) {
|
||||
return i->second->evaluate(base_);
|
||||
}
|
||||
return base_.query_value(key);
|
||||
}
|
||||
};
|
||||
|
||||
class where_expression: public formula_expression {
|
||||
public:
|
||||
explicit where_expression(expression_ptr body,
|
||||
expr_table_ptr clauses)
|
||||
: body_(body), clauses_(clauses)
|
||||
{}
|
||||
|
||||
private:
|
||||
expression_ptr body_;
|
||||
expr_table_ptr clauses_;
|
||||
|
||||
variant execute(const formula_callable& variables) const {
|
||||
where_variables wrapped_variables(variables, clauses_);
|
||||
return body_->evaluate(wrapped_variables);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class identifier_expression : public formula_expression {
|
||||
public:
|
||||
explicit identifier_expression(const std::string& id) : id_(id)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variables.query_value(id_);
|
||||
}
|
||||
std::string id_;
|
||||
};
|
||||
|
||||
class integer_expression : public formula_expression {
|
||||
public:
|
||||
explicit integer_expression(int i) : i_(i)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(i_);
|
||||
}
|
||||
|
||||
int i_;
|
||||
};
|
||||
|
||||
class string_expression : public formula_expression {
|
||||
public:
|
||||
explicit string_expression(std::string str)
|
||||
{
|
||||
std::string::iterator i;
|
||||
while((i = std::find(str.begin(), str.end(), '{')) != str.end()) {
|
||||
std::string::iterator j = std::find(i, str.end(), '}');
|
||||
if(j == str.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string formula_str(i+1, j);
|
||||
const int pos = i - str.begin();
|
||||
str.erase(i, j+1);
|
||||
|
||||
substitution sub;
|
||||
sub.pos = pos;
|
||||
sub.calculation.reset(new formula(formula_str));
|
||||
subs_.push_back(sub);
|
||||
}
|
||||
|
||||
std::reverse(subs_.begin(), subs_.end());
|
||||
|
||||
str_ = variant(str);
|
||||
}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
if(subs_.empty()) {
|
||||
return str_;
|
||||
} else {
|
||||
std::string res = str_.as_string();
|
||||
foreach(const substitution& sub, subs_) {
|
||||
const std::string str = sub.calculation->execute(variables).string_cast();
|
||||
res.insert(sub.pos, str);
|
||||
}
|
||||
|
||||
return variant(res);
|
||||
}
|
||||
}
|
||||
|
||||
struct substitution {
|
||||
int pos;
|
||||
const_formula_ptr calculation;
|
||||
};
|
||||
|
||||
variant str_;
|
||||
std::vector<substitution> subs_;
|
||||
};
|
||||
|
||||
using namespace formula_tokenizer;
|
||||
int operator_precedence(const token& t)
|
||||
{
|
||||
static std::map<std::string,int> precedence_map;
|
||||
if(precedence_map.empty()) {
|
||||
int n = 0;
|
||||
precedence_map["not"] = ++n;
|
||||
precedence_map["where"] = ++n;
|
||||
precedence_map["or"] = ++n;
|
||||
precedence_map["and"] = ++n;
|
||||
precedence_map["="] = ++n;
|
||||
precedence_map["!="] = n;
|
||||
precedence_map["<"] = n;
|
||||
precedence_map[">"] = n;
|
||||
precedence_map["<="] = n;
|
||||
precedence_map[">="] = n;
|
||||
precedence_map["+"] = ++n;
|
||||
precedence_map["-"] = n;
|
||||
precedence_map["*"] = ++n;
|
||||
precedence_map["/"] = ++n;
|
||||
precedence_map["%"] = ++n;
|
||||
precedence_map["^"] = ++n;
|
||||
precedence_map["d"] = ++n;
|
||||
precedence_map["."] = ++n;
|
||||
}
|
||||
|
||||
assert(precedence_map.count(std::string(t.begin,t.end)));
|
||||
return precedence_map[std::string(t.begin,t.end)];
|
||||
}
|
||||
|
||||
expression_ptr parse_expression(const token* i1, const token* i2, const function_symbol_table* symbols);
|
||||
|
||||
void parse_args(const token* i1, const token* i2,
|
||||
std::vector<expression_ptr>* res,
|
||||
const function_symbol_table* symbols)
|
||||
{
|
||||
int parens = 0;
|
||||
const token* beg = i1;
|
||||
while(i1 != i2) {
|
||||
if(i1->type == TOKEN_LPARENS || i1->type == TOKEN_LSQUARE) {
|
||||
++parens;
|
||||
} else if(i1->type == TOKEN_RPARENS || i1->type == TOKEN_RSQUARE) {
|
||||
--parens;
|
||||
} else if(i1->type == TOKEN_COMMA && !parens) {
|
||||
res->push_back(parse_expression(beg,i1, symbols));
|
||||
beg = i1+1;
|
||||
}
|
||||
|
||||
++i1;
|
||||
}
|
||||
|
||||
if(beg != i1) {
|
||||
res->push_back(parse_expression(beg,i1, symbols));
|
||||
}
|
||||
}
|
||||
|
||||
void parse_where_clauses(const token* i1, const token * i2,
|
||||
expr_table_ptr res, const function_symbol_table* symbols) {
|
||||
int parens = 0;
|
||||
const token *original_i1_cached = i1;
|
||||
const token *beg = i1;
|
||||
std::string var_name;
|
||||
while(i1 != i2) {
|
||||
if(i1->type == TOKEN_LPARENS) {
|
||||
++parens;
|
||||
} else if(i1->type == TOKEN_RPARENS) {
|
||||
--parens;
|
||||
} else if(!parens) {
|
||||
if(i1->type == TOKEN_COMMA) {
|
||||
if(var_name.empty()) {
|
||||
std::cerr << "There is 'where <expression>,; "
|
||||
<< "'where name=<expression>,' was needed.\n";
|
||||
throw formula_error();
|
||||
}
|
||||
(*res)[var_name] = parse_expression(beg,i1, symbols);
|
||||
beg = i1+1;
|
||||
var_name.clear();
|
||||
} else if(i1->type == TOKEN_OPERATOR) {
|
||||
std::string op_name(i1->begin, i1->end);
|
||||
if(op_name == "=") {
|
||||
if(beg->type != TOKEN_IDENTIFIER) {
|
||||
if(i1 == original_i1_cached) {
|
||||
std::cerr<< "There is 'where =<expression'; "
|
||||
<< "'where name=<expression>' was needed.\n";
|
||||
} else {
|
||||
std::cerr<< "There is 'where <expression>=<expression>'; "
|
||||
<< "'where name=<expression>' was needed.\n";
|
||||
}
|
||||
throw formula_error();
|
||||
} else if(beg+1 != i1) {
|
||||
std::cerr<<"There is 'where name <expression>=<expression>'; "
|
||||
<< "'where name=<expression>' was needed.\n";
|
||||
throw formula_error();
|
||||
} else if(!var_name.empty()) {
|
||||
std::cerr<<"There is 'where name=name=<expression>'; "
|
||||
<<"'where name=<expression>' was needed.\n";
|
||||
throw formula_error();
|
||||
}
|
||||
var_name.insert(var_name.end(), beg->begin, beg->end);
|
||||
beg = i1+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
++i1;
|
||||
}
|
||||
if(beg != i1) {
|
||||
if(var_name.empty()) {
|
||||
std::cerr << "There is 'where <expression>'; "
|
||||
<< "'where name=<expression> was needed.\n";
|
||||
throw formula_error();
|
||||
}
|
||||
(*res)[var_name] = parse_expression(beg,i1, symbols);
|
||||
}
|
||||
}
|
||||
|
||||
expression_ptr parse_expression(const token* i1, const token* i2, const function_symbol_table* symbols)
|
||||
{
|
||||
if(i1 == i2) {
|
||||
std::cerr << "empty expression\n";
|
||||
throw formula_error();
|
||||
}
|
||||
|
||||
int parens = 0;
|
||||
const token* op = NULL;
|
||||
for(const token* i = i1; i != i2; ++i) {
|
||||
if(i->type == TOKEN_LPARENS || i->type == TOKEN_LSQUARE) {
|
||||
++parens;
|
||||
} else if(i->type == TOKEN_RPARENS || i->type == TOKEN_RSQUARE) {
|
||||
--parens;
|
||||
} else if(parens == 0 && i->type == TOKEN_OPERATOR) {
|
||||
if(op == NULL || operator_precedence(*op) >
|
||||
operator_precedence(*i)) {
|
||||
op = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(op == NULL) {
|
||||
if(i1->type == TOKEN_LPARENS && (i2-1)->type == TOKEN_RPARENS) {
|
||||
return parse_expression(i1+1,i2-1,symbols);
|
||||
} else if(i1->type == TOKEN_LSQUARE && (i2-1)->type == TOKEN_RSQUARE) {
|
||||
std::vector<expression_ptr> args;
|
||||
parse_args(i1+1,i2-1,&args,symbols);
|
||||
return expression_ptr(new list_expression(args));
|
||||
} else if(i2 - i1 == 1) {
|
||||
if(i1->type == TOKEN_IDENTIFIER) {
|
||||
return expression_ptr(new identifier_expression(
|
||||
std::string(i1->begin,i1->end)));
|
||||
} else if(i1->type == TOKEN_INTEGER) {
|
||||
int n = boost::lexical_cast<int>(std::string(i1->begin,i1->end));
|
||||
return expression_ptr(new integer_expression(n));
|
||||
} else if(i1->type == TOKEN_STRING_LITERAL) {
|
||||
return expression_ptr(new string_expression(std::string(i1->begin+1,i1->end-1)));
|
||||
}
|
||||
} else if(i1->type == TOKEN_IDENTIFIER &&
|
||||
(i1+1)->type == TOKEN_LPARENS &&
|
||||
(i2-1)->type == TOKEN_RPARENS) {
|
||||
int nleft = 0, nright = 0;
|
||||
for(const token* i = i1; i != i2; ++i) {
|
||||
if(i->type == TOKEN_LPARENS) {
|
||||
++nleft;
|
||||
} else if(i->type == TOKEN_RPARENS) {
|
||||
++nright;
|
||||
}
|
||||
}
|
||||
|
||||
if(nleft == nright) {
|
||||
std::vector<expression_ptr> args;
|
||||
parse_args(i1+2,i2-1,&args,symbols);
|
||||
return expression_ptr(
|
||||
create_function(std::string(i1->begin,i1->end),args,symbols));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream expr;
|
||||
while(i1 != i2) {
|
||||
expr << std::string(i1->begin,i1->end);
|
||||
++i1;
|
||||
}
|
||||
std::cerr << "could not parse expression: '" << expr.str() << "'\n";
|
||||
throw formula_error();
|
||||
}
|
||||
|
||||
if(op == i1) {
|
||||
return expression_ptr(new unary_operator_expression(
|
||||
std::string(op->begin,op->end),
|
||||
parse_expression(op+1,i2,symbols)));
|
||||
}
|
||||
|
||||
const std::string op_name(op->begin,op->end);
|
||||
|
||||
if(op_name == ".") {
|
||||
return expression_ptr(new dot_expression(
|
||||
parse_expression(i1,op,symbols),
|
||||
parse_expression(op+1,i2,symbols)));
|
||||
}
|
||||
|
||||
if(op_name == "where") {
|
||||
expr_table_ptr table(new expr_table());
|
||||
parse_where_clauses(op+1, i2, table, symbols);
|
||||
return expression_ptr(new where_expression(parse_expression(i1, op, symbols),
|
||||
table));
|
||||
}
|
||||
|
||||
return expression_ptr(new operator_expression(
|
||||
op_name, parse_expression(i1,op,symbols),
|
||||
parse_expression(op+1,i2,symbols)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
formula_ptr formula::create_string_formula(const std::string& str)
|
||||
{
|
||||
formula_ptr res(new formula());
|
||||
res->expr_.reset(new string_expression(str));
|
||||
return res;
|
||||
}
|
||||
|
||||
formula_ptr formula::create_optional_formula(const std::string& str, const function_symbol_table* symbols)
|
||||
{
|
||||
if(str.empty()) {
|
||||
return formula_ptr();
|
||||
}
|
||||
|
||||
try {
|
||||
return formula_ptr(new formula(str, symbols));
|
||||
} catch(...) {
|
||||
std::cerr << "ERROR parsing optional formula: '" << str << "'\n";
|
||||
return formula_ptr();
|
||||
}
|
||||
}
|
||||
|
||||
formula::formula(const std::string& str, const function_symbol_table* symbols) : str_(str)
|
||||
{
|
||||
using namespace formula_tokenizer;
|
||||
|
||||
std::vector<token> tokens;
|
||||
std::string::const_iterator i1 = str.begin(), i2 = str.end();
|
||||
while(i1 != i2) {
|
||||
try {
|
||||
tokens.push_back(get_token(i1,i2));
|
||||
if(tokens.back().type == TOKEN_WHITESPACE) {
|
||||
tokens.pop_back();
|
||||
}
|
||||
} catch(token_error& e) {
|
||||
throw formula_error();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
expr_ = parse_expression(&tokens[0],&tokens[0] + tokens.size(), symbols);
|
||||
} catch(...) {
|
||||
std::cerr << "error parsing formula '" << str << "'\n";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
variant formula::execute(const formula_callable& variables) const
|
||||
{
|
||||
try {
|
||||
return expr_->evaluate(variables);
|
||||
} catch(type_error& e) {
|
||||
std::cerr << "formula type error: " << e.message << "\n";
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
variant formula::execute() const
|
||||
{
|
||||
static map_formula_callable null_callable;
|
||||
return execute(null_callable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST_FORMULA
|
||||
using namespace game_logic;
|
||||
class mock_char : public formula_callable {
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "strength") {
|
||||
return variant(15);
|
||||
} else if(key == "agility") {
|
||||
return variant(12);
|
||||
}
|
||||
|
||||
return variant(10);
|
||||
}
|
||||
};
|
||||
class mock_party : public formula_callable {
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "members") {
|
||||
i_[0].add("strength",variant(12));
|
||||
i_[1].add("strength",variant(16));
|
||||
i_[2].add("strength",variant(14));
|
||||
std::vector<variant> members;
|
||||
for(int n = 0; n != 3; ++n) {
|
||||
members.push_back(variant(&i_[n]));
|
||||
}
|
||||
|
||||
return variant(&members);
|
||||
} else if(key == "char") {
|
||||
return variant(&c_);
|
||||
} else {
|
||||
return variant(0);
|
||||
}
|
||||
}
|
||||
|
||||
mock_char c_;
|
||||
mutable map_formula_callable i_[3];
|
||||
|
||||
};
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
srand(time(NULL));
|
||||
mock_char c;
|
||||
mock_party p;
|
||||
try {
|
||||
assert(formula("strength").execute(c).as_int() == 15);
|
||||
assert(formula("17").execute(c).as_int() == 17);
|
||||
assert(formula("strength/2 + agility").execute(c).as_int() == 19);
|
||||
assert(formula("(strength+agility)/2").execute(c).as_int() == 13);
|
||||
assert(formula("strength > 12").execute(c).as_int() == 1);
|
||||
assert(formula("strength > 18").execute(c).as_int() == 0);
|
||||
assert(formula("if(strength > 12, 7, 2)").execute(c).as_int() == 7);
|
||||
assert(formula("if(strength > 18, 7, 2)").execute(c).as_int() == 2);
|
||||
assert(formula("2 and 1").execute(c).as_int() == 1);
|
||||
assert(formula("2 and 0").execute(c).as_int() == 0);
|
||||
assert(formula("2 or 0").execute(c).as_int() == 1);
|
||||
assert(formula("-5").execute(c).as_int() == -5);
|
||||
assert(formula("not 5").execute(c).as_int() == 0);
|
||||
assert(formula("not 0").execute(c).as_int() == 1);
|
||||
assert(formula("abs(5)").execute(c).as_int() == 5);
|
||||
assert(formula("abs(-5)").execute(c).as_int() == 5);
|
||||
assert(formula("min(3,5)").execute(c).as_int() == 3);
|
||||
assert(formula("min(5,2)").execute(c).as_int() == 2);
|
||||
assert(formula("max(3,5)").execute(c).as_int() == 5);
|
||||
assert(formula("max(5,2)").execute(c).as_int() == 5);
|
||||
assert(formula("max(4,5,[2,18,7])").execute(c).as_int() == 18);
|
||||
assert(formula("char.strength").execute(p).as_int() == 15);
|
||||
assert(formula("choose(members,strength).strength").execute(p).as_int() == 16);
|
||||
assert(formula("4^2").execute().as_int() == 16);
|
||||
assert(formula("2+3^3").execute().as_int() == 29);
|
||||
assert(formula("2*3^3+2").execute().as_int() == 56);
|
||||
assert(formula("9^3").execute().as_int() == 729);
|
||||
assert(formula("x*5 where x=1").execute().as_int() == 5);
|
||||
assert(formula("x*(a*b where a=2,b=1) where x=5").execute().as_int() == 10);
|
||||
assert(formula("char.strength * ability where ability=3").execute(p).as_int() == 45);
|
||||
assert(formula("'abcd' = 'abcd'").execute(p).as_bool() == true);
|
||||
assert(formula("'abcd' = 'acd'").execute(p).as_bool() == false);
|
||||
assert(formula("'strength, agility: {strength}, {agility}'").execute(c).as_string() ==
|
||||
"strength, agility: 15, 12");
|
||||
const int dice_roll = formula("3d6").execute().as_int();
|
||||
assert(dice_roll >= 3 && dice_roll <= 18);
|
||||
|
||||
assert(formula::create_string_formula("Your strength is {strength}")->execute(c).as_string() ==
|
||||
"Your strength is 15");
|
||||
variant myarray = formula("[1,2,3]").execute();
|
||||
assert(myarray.num_elements() == 3);
|
||||
assert(myarray[0].as_int() == 1);
|
||||
assert(myarray[1].as_int() == 2);
|
||||
assert(myarray[2].as_int() == 3);
|
||||
} catch(formula_error& e) {
|
||||
std::cerr << "parse error\n";
|
||||
}
|
||||
}
|
||||
#endif
|
63
src/formula.hpp
Normal file
63
src/formula.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2007 by David White <dave.net>
|
||||
Part of the Silver Tree Project
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 or later.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef FORMULA_HPP_INCLUDED
|
||||
#define FORMULA_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "formula_fwd.hpp"
|
||||
#include "variant.hpp"
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
|
||||
class formula_callable;
|
||||
class formula_expression;
|
||||
class function_symbol_table;
|
||||
typedef boost::shared_ptr<formula_expression> expression_ptr;
|
||||
|
||||
class formula {
|
||||
public:
|
||||
static variant evaluate(const const_formula_ptr& f,
|
||||
const formula_callable& variables,
|
||||
variant default_res=variant(0)) {
|
||||
if(f) {
|
||||
return f->execute(variables);
|
||||
} else {
|
||||
return default_res;
|
||||
}
|
||||
}
|
||||
|
||||
// function which will create a formula that is a single string literal, 'str'.
|
||||
// 'str' should not be enclosed in quotes.
|
||||
static formula_ptr create_string_formula(const std::string& str);
|
||||
static formula_ptr create_optional_formula(const std::string& str, const function_symbol_table* symbols=NULL);
|
||||
explicit formula(const std::string& str, const function_symbol_table* symbols=NULL);
|
||||
variant execute(const formula_callable& variables) const;
|
||||
variant execute() const;
|
||||
const std::string& str() const { return str_; }
|
||||
|
||||
private:
|
||||
formula() {}
|
||||
expression_ptr expr_;
|
||||
std::string str_;
|
||||
};
|
||||
|
||||
struct formula_error
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
778
src/formula_ai.cpp
Normal file
778
src/formula_ai.cpp
Normal file
|
@ -0,0 +1,778 @@
|
|||
#include "actions.hpp"
|
||||
#include "callable_objects.hpp"
|
||||
#include "formula.hpp"
|
||||
#include "formula_ai.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
#include "formula_function.hpp"
|
||||
#include "pathutils.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace game_logic;
|
||||
|
||||
class position_callable : public formula_callable {
|
||||
unit_map units_;
|
||||
int chance_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "chance") {
|
||||
return variant(chance_);
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
|
||||
inputs->push_back(game_logic::formula_input("chance", game_logic::FORMULA_READ_ONLY));
|
||||
}
|
||||
public:
|
||||
position_callable(unit_map* units, int chance) : chance_(chance)
|
||||
{
|
||||
units->swap(units_);
|
||||
}
|
||||
|
||||
void swap_position(formula_ai& ai) {
|
||||
ai.get_info().units.swap(units_);
|
||||
}
|
||||
|
||||
struct swapper {
|
||||
formula_ai& ai;
|
||||
unit_map& a;
|
||||
unit_map& b;
|
||||
formula_ai::move_map_backup backup;
|
||||
void swap() {
|
||||
a.swap(b);
|
||||
ai.swap_move_map(backup);
|
||||
}
|
||||
swapper(formula_ai& ai, position_callable& pos)
|
||||
: ai(ai), a(ai.get_info().units), b(pos.units_) {
|
||||
swap();
|
||||
}
|
||||
|
||||
~swapper() {
|
||||
swap();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class distance_between_function : public function_expression {
|
||||
public:
|
||||
explicit distance_between_function(const args_list& args)
|
||||
: function_expression("distance_between", args, 2, 2)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const location_callable* loc1 = args()[0]->evaluate(variables).convert_to<location_callable>();
|
||||
const location_callable* loc2 = args()[1]->evaluate(variables).convert_to<location_callable>();
|
||||
return variant(distance_between(loc1->loc(), loc2->loc()));
|
||||
}
|
||||
};
|
||||
|
||||
class distance_to_nearest_unowned_village_function : public function_expression {
|
||||
public:
|
||||
distance_to_nearest_unowned_village_function(const args_list& args, const formula_ai& ai)
|
||||
: function_expression("distance_to_nearest_unowned_village", args, 1, 1), ai_(ai) {
|
||||
}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const gamemap::location& loc = args()[0]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
int best = 1000000;
|
||||
const std::vector<gamemap::location>& villages = ai_.get_info().map.villages();
|
||||
const std::set<gamemap::location>& my_villages = ai_.current_team().villages();
|
||||
for(std::vector<gamemap::location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
|
||||
int distance = distance_between(loc, *i);
|
||||
if(distance < best) {
|
||||
if(my_villages.count(*i) == 0) {
|
||||
best = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variant(best);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class outcomes_function : public function_expression {
|
||||
public:
|
||||
outcomes_function(const args_list& args, const formula_ai& ai)
|
||||
: function_expression("outcomes", args, 1, 1), ai_(ai) {
|
||||
}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant attack = args()[0]->evaluate(variables);
|
||||
ai::attack_analysis* analysis = attack.convert_to<ai::attack_analysis>();
|
||||
unit_map units_with_moves(ai_.get_info().units);
|
||||
for(int n = 0; n != analysis->movements.size(); ++n) {
|
||||
std::pair<gamemap::location,unit>* pair = units_with_moves.extract(analysis->movements[n].first);
|
||||
pair->first = analysis->movements[n].second;
|
||||
units_with_moves.add(pair);
|
||||
}
|
||||
|
||||
std::vector<variant> vars;
|
||||
if(analysis->chance_to_kill > 0.0) {
|
||||
unit_map units(units_with_moves);
|
||||
units.erase(analysis->target);
|
||||
vars.push_back(variant(new position_callable(&units, analysis->chance_to_kill*100)));
|
||||
|
||||
}
|
||||
|
||||
if(analysis->chance_to_kill < 1.0) {
|
||||
unit_map units(units_with_moves);
|
||||
vars.push_back(variant(new position_callable(&units, 100 - analysis->chance_to_kill*100)));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class evaluate_for_position_function : public function_expression {
|
||||
public:
|
||||
evaluate_for_position_function(const args_list& args, formula_ai& ai)
|
||||
: function_expression("evaluate_for_position", args, 2, 2), ai_(ai) {
|
||||
}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant position = args()[0]->evaluate(variables);
|
||||
position_callable* pos = position.convert_to<position_callable>();
|
||||
position_callable::swapper swapper(ai_, *pos);
|
||||
return args()[1]->evaluate(variables);
|
||||
}
|
||||
|
||||
formula_ai& ai_;
|
||||
};
|
||||
|
||||
class recruit_callable : public formula_callable {
|
||||
gamemap::location loc_;
|
||||
std::string type_;
|
||||
variant get_value(const std::string& key) const { return variant(); }
|
||||
public:
|
||||
recruit_callable(const gamemap::location& loc, const std::string& type)
|
||||
: loc_(loc), type_(type)
|
||||
{}
|
||||
|
||||
const gamemap::location& loc() const { return loc_; }
|
||||
const std::string& type() const { return type_; }
|
||||
};
|
||||
|
||||
class recruit_function : public function_expression {
|
||||
public:
|
||||
explicit recruit_function(const args_list& args)
|
||||
: function_expression("recruit", args, 1, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const std::string type = args()[0]->evaluate(variables).as_string();
|
||||
gamemap::location loc;
|
||||
if(args().size() >= 2) {
|
||||
loc = args()[1]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
}
|
||||
|
||||
return variant(new recruit_callable(loc, type));
|
||||
}
|
||||
};
|
||||
|
||||
class move_function : public function_expression {
|
||||
public:
|
||||
explicit move_function(const args_list& args)
|
||||
: function_expression("move", args, 2, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const gamemap::location& src = args()[0]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
const gamemap::location& dst = args()[1]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
return variant(new move_callable(src, dst));
|
||||
}
|
||||
};
|
||||
|
||||
class set_var_callable : public formula_callable {
|
||||
std::string key_;
|
||||
variant value_;
|
||||
variant get_value(const std::string& key) const { return variant(); }
|
||||
public:
|
||||
set_var_callable(const std::string& key, const variant& value)
|
||||
: key_(key), value_(value)
|
||||
{}
|
||||
|
||||
const std::string& key() const { return key_; }
|
||||
variant value() const { return value_; }
|
||||
};
|
||||
|
||||
class set_var_function : public function_expression {
|
||||
public:
|
||||
explicit set_var_function(const args_list& args)
|
||||
: function_expression("set_var", args, 2, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(new set_var_callable(args()[0]->evaluate(variables).as_string(), args()[1]->evaluate(variables)));
|
||||
}
|
||||
};
|
||||
|
||||
class fallback_callable : public formula_callable {
|
||||
std::string key_;
|
||||
variant get_value(const std::string& key) const { return variant(); }
|
||||
public:
|
||||
explicit fallback_callable(const std::string& key) : key_(key) {
|
||||
}
|
||||
|
||||
const std::string& key() const { return key_; }
|
||||
};
|
||||
|
||||
class fallback_function : public function_expression {
|
||||
public:
|
||||
explicit fallback_function(const args_list& args)
|
||||
: function_expression("fallback", args, 1, 1)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(new fallback_callable(args()[0]->evaluate(variables).as_string()));
|
||||
}
|
||||
};
|
||||
|
||||
class attack_callable : public formula_callable {
|
||||
gamemap::location move_from_, src_, dst_;
|
||||
battle_context bc_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "attacker") {
|
||||
return variant(new location_callable(src_));
|
||||
} else if(key == "defender") {
|
||||
return variant(new location_callable(dst_));
|
||||
} else if(key == "move_from") {
|
||||
return variant(new location_callable(move_from_));
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
|
||||
inputs->push_back(game_logic::formula_input("attacker", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("defender", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("move_from", game_logic::FORMULA_READ_ONLY));
|
||||
}
|
||||
public:
|
||||
attack_callable(const formula_ai& ai,
|
||||
const gamemap::location& move_from,
|
||||
const gamemap::location& src, const gamemap::location& dst,
|
||||
int weapon)
|
||||
: move_from_(move_from), src_(src), dst_(dst),
|
||||
bc_(ai.get_info().map, ai.get_info().teams, ai.get_info().units,
|
||||
ai.get_info().state, ai.get_info().gameinfo,
|
||||
src, dst, weapon, -1, 1.0, NULL, &ai.get_info().units.find(move_from)->second)
|
||||
{
|
||||
}
|
||||
|
||||
const gamemap::location& move_from() const { return move_from_; }
|
||||
const gamemap::location& src() const { return src_; }
|
||||
const gamemap::location& dst() const { return dst_; }
|
||||
int weapon() const { return bc_.get_attacker_stats().attack_num; }
|
||||
int defender_weapon() const { return bc_.get_defender_stats().attack_num; }
|
||||
};
|
||||
|
||||
class attack_function : public function_expression {
|
||||
public:
|
||||
explicit attack_function(const args_list& args, const formula_ai& ai)
|
||||
: function_expression("attack", args, 3, 4),
|
||||
ai_(ai)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const gamemap::location& move_from = args()[0]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
const gamemap::location& src = args()[1]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
const gamemap::location& dst = args()[2]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
const int weapon = args().size() == 4 ? args()[3]->evaluate(variables).as_int() : -1;
|
||||
if(ai_.get_info().units.count(move_from) == 0 || ai_.get_info().units.count(dst) == 0) {
|
||||
std::cerr << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << "\n";
|
||||
return variant();
|
||||
}
|
||||
return variant(new attack_callable(ai_, move_from, src, dst, weapon));
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class loc_function : public function_expression {
|
||||
public:
|
||||
explicit loc_function(const args_list& args)
|
||||
: function_expression("loc", args, 2, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(new location_callable(gamemap::location(args()[0]->evaluate(variables).as_int()-1, args()[1]->evaluate(variables).as_int()-1)));
|
||||
}
|
||||
};
|
||||
|
||||
class is_village_function : public function_expression {
|
||||
public:
|
||||
explicit is_village_function(const args_list& args)
|
||||
: function_expression("is_village", args, 2, 3)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const gamemap& m = args()[0]->evaluate(variables).convert_to<gamemap_callable>()->get_gamemap();
|
||||
|
||||
gamemap::location loc;
|
||||
if(args().size() == 2) {
|
||||
loc = args()[1]->evaluate(variables).convert_to<location_callable>()->loc();
|
||||
} else {
|
||||
loc = gamemap::location(args()[1]->evaluate(variables).as_int(),
|
||||
args()[2]->evaluate(variables).as_int());
|
||||
}
|
||||
return variant(m.is_village(loc));
|
||||
}
|
||||
};
|
||||
|
||||
class unit_at_function : public function_expression {
|
||||
public:
|
||||
unit_at_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression("unit_at", args, 1, 1), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const location_callable* loc = args()[0]->evaluate(variables).convert_to<location_callable>();
|
||||
const unit_map::const_iterator i = ai_.get_info().units.find(loc->loc());
|
||||
if(i != ai_.get_info().units.end()) {
|
||||
return variant(new unit_callable(*i, ai_.current_team(), ai_.get_info().team_num));
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class unit_moves_function : public function_expression {
|
||||
public:
|
||||
unit_moves_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression("unit_moves", args, 1, 1), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant res = args()[0]->evaluate(variables);
|
||||
std::vector<variant> vars;
|
||||
if(res.is_null()) {
|
||||
return variant(&vars);
|
||||
}
|
||||
|
||||
const gamemap::location& loc = res.convert_to<location_callable>()->loc();
|
||||
const formula_ai::move_map& srcdst = ai_.srcdst();
|
||||
typedef formula_ai::move_map::const_iterator Itor;
|
||||
std::pair<Itor,Itor> range = srcdst.equal_range(loc);
|
||||
|
||||
for(Itor i = range.first; i != range.second; ++i) {
|
||||
vars.push_back(variant(new location_callable(i->second)));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class units_can_reach_function : public function_expression {
|
||||
public:
|
||||
units_can_reach_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression("units_can_reach", args, 2, 2), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
std::vector<variant> vars;
|
||||
variant dstsrc_var = args()[0]->evaluate(variables);
|
||||
const ai::move_map& dstsrc = dstsrc_var.convert_to<move_map_callable>()->dstsrc();
|
||||
std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
|
||||
dstsrc.equal_range(args()[1]->evaluate(variables).convert_to<location_callable>()->loc());
|
||||
while(range.first != range.second) {
|
||||
unit_map::const_iterator un = ai_.get_info().units.find(range.first->second);
|
||||
assert(un != ai_.get_info().units.end());
|
||||
const int side = un->second.side();
|
||||
vars.push_back(variant(new unit_callable(*un, ai_.get_info().teams[side-1], side)));
|
||||
++range.first;
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class defense_on_function : public function_expression {
|
||||
public:
|
||||
defense_on_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression("defense_on", args, 2, 2), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant u = args()[0]->evaluate(variables);
|
||||
variant loc_var = args()[1]->evaluate(variables);
|
||||
if(u.is_null() || loc_var.is_null()) {
|
||||
return variant();
|
||||
}
|
||||
|
||||
const unit& un = u.convert_to<unit_callable>()->get_unit();
|
||||
const gamemap::location& loc = loc_var.convert_to<location_callable>()->loc();
|
||||
if(!ai_.get_info().map.on_board(loc)) {
|
||||
return variant();
|
||||
}
|
||||
|
||||
return variant(un.defense_modifier(ai_.get_info().map[loc]));
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class max_possible_damage_function : public function_expression {
|
||||
public:
|
||||
max_possible_damage_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression("max_possible_damage", args, 2, 2), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant u1 = args()[0]->evaluate(variables);
|
||||
variant u2 = args()[1]->evaluate(variables);
|
||||
if(u1.is_null() || u2.is_null()) {
|
||||
return variant();
|
||||
}
|
||||
|
||||
const unit& attacker = u1.convert_to<unit_callable>()->get_unit();
|
||||
const unit& defender = u2.convert_to<unit_callable>()->get_unit();
|
||||
const std::vector<attack_type>& attacks = attacker.attacks();
|
||||
|
||||
int best = 0;
|
||||
for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
|
||||
const int dmg = ((defender.damage_from(*i, false, gamemap::location()) * i->damage())/100) * i->num_attacks();
|
||||
if(dmg > best) {
|
||||
best = dmg;
|
||||
}
|
||||
}
|
||||
|
||||
return variant(best);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class ai_function_symbol_table : public function_symbol_table {
|
||||
formula_ai& ai_;
|
||||
|
||||
expression_ptr create_function(const std::string& fn,
|
||||
const std::vector<expression_ptr>& args) const {
|
||||
if(fn == "outcomes") {
|
||||
return expression_ptr(new outcomes_function(args, ai_));
|
||||
} else if(fn == "evaluate_for_position") {
|
||||
return expression_ptr(new evaluate_for_position_function(args, ai_));
|
||||
} else if(fn == "move") {
|
||||
return expression_ptr(new move_function(args));
|
||||
} else if(fn == "attack") {
|
||||
return expression_ptr(new attack_function(args, ai_));
|
||||
} else if(fn == "recruit") {
|
||||
return expression_ptr(new recruit_function(args));
|
||||
} else if(fn == "is_village") {
|
||||
return expression_ptr(new is_village_function(args));
|
||||
} else if(fn == "loc") {
|
||||
return expression_ptr(new loc_function(args));
|
||||
} else if(fn == "unit_at") {
|
||||
return expression_ptr(new unit_at_function(args, ai_));
|
||||
} else if(fn == "unit_moves") {
|
||||
return expression_ptr(new unit_moves_function(args, ai_));
|
||||
} else if(fn == "set_var") {
|
||||
return expression_ptr(new set_var_function(args));
|
||||
} else if(fn == "fallback") {
|
||||
return expression_ptr(new fallback_function(args));
|
||||
} else if(fn == "units_can_reach") {
|
||||
return expression_ptr(new units_can_reach_function(args, ai_));
|
||||
} else if(fn == "defense_on") {
|
||||
return expression_ptr(new defense_on_function(args, ai_));
|
||||
} else if(fn == "max_possible_damage") {
|
||||
return expression_ptr(new max_possible_damage_function(args, ai_));
|
||||
} else if(fn == "distance_to_nearest_unowned_village") {
|
||||
return expression_ptr(new distance_to_nearest_unowned_village_function(args, ai_));
|
||||
} else if(fn == "distance_between") {
|
||||
return expression_ptr(new distance_between_function(args));
|
||||
} else {
|
||||
return function_symbol_table::create_function(fn, args);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ai_function_symbol_table(formula_ai& ai) : ai_(ai)
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
formula_ai::formula_ai(info& i) : ai(i), move_maps_valid_(false)
|
||||
{
|
||||
//make sure we don't run out of refcount
|
||||
add_ref();
|
||||
vars_.add_ref();
|
||||
}
|
||||
|
||||
void formula_ai::play_turn()
|
||||
{
|
||||
ai_function_symbol_table function_table(*this);
|
||||
|
||||
const config& ai_param = current_team().ai_parameters();
|
||||
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"];
|
||||
const t_string& inputs = (**i)["inputs"];
|
||||
const t_string& formula_str = (**i)["formula"];
|
||||
|
||||
std::vector<std::string> args = utils::split(inputs);
|
||||
function_table.add_formula_function(name, game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)), game_logic::formula::create_optional_formula((**i)["precondition"], &function_table), args);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
while(make_move()) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string formula_ai::evaluate(const std::string& formula_str)
|
||||
{
|
||||
ai_function_symbol_table function_table(*this);
|
||||
const config& ai_param = current_team().ai_parameters();
|
||||
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"];
|
||||
const t_string& inputs = (**i)["inputs"];
|
||||
const t_string& formula_str = (**i)["formula"];
|
||||
std::vector<std::string> args = utils::split(inputs);
|
||||
function_table.add_formula_function(name, game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)), game_logic::formula::create_optional_formula((**i)["precondition"], &function_table), args);
|
||||
}
|
||||
|
||||
game_logic::formula f(formula_str, &function_table);
|
||||
const variant v = f.execute(*this);
|
||||
return v.to_debug_string();
|
||||
}
|
||||
|
||||
void formula_ai::swap_move_map(move_map_backup& backup)
|
||||
{
|
||||
std::swap(move_maps_valid_, backup.move_maps_valid);
|
||||
std::swap(backup.attacks_cache, attacks_cache_);
|
||||
backup.move_maps_valid = move_maps_valid_;
|
||||
backup.srcdst.swap(srcdst_);
|
||||
backup.dstsrc.swap(dstsrc_);
|
||||
backup.full_srcdst.swap(full_srcdst_);
|
||||
backup.full_dstsrc.swap(full_dstsrc_);
|
||||
backup.enemy_srcdst.swap(enemy_srcdst_);
|
||||
backup.enemy_dstsrc.swap(enemy_dstsrc_);
|
||||
}
|
||||
|
||||
void formula_ai::prepare_move() const
|
||||
{
|
||||
if(move_maps_valid_) {
|
||||
return;
|
||||
}
|
||||
|
||||
possible_moves_.clear();
|
||||
srcdst_.clear();
|
||||
dstsrc_.clear();
|
||||
|
||||
calculate_possible_moves(possible_moves_, srcdst_, dstsrc_, false);
|
||||
|
||||
full_srcdst_.clear();
|
||||
full_dstsrc_.clear();
|
||||
|
||||
std::map<location,paths> possible_moves_dummy;
|
||||
calculate_possible_moves(possible_moves_dummy, full_srcdst_, full_dstsrc_, false, true);
|
||||
|
||||
enemy_srcdst_.clear();
|
||||
enemy_dstsrc_.clear();
|
||||
possible_moves_dummy.clear();
|
||||
calculate_possible_moves(possible_moves_dummy, enemy_srcdst_, enemy_dstsrc_, true);
|
||||
|
||||
attacks_cache_ = variant();
|
||||
move_maps_valid_ = true;
|
||||
}
|
||||
|
||||
bool formula_ai::make_move()
|
||||
{
|
||||
if(!move_formula_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
move_maps_valid_ = false;
|
||||
|
||||
std::cerr << "do move...\n";
|
||||
const variant var = move_formula_->execute(*this);
|
||||
std::vector<variant> vars;
|
||||
if(var.is_list()) {
|
||||
for(int n = 0; n != var.num_elements(); ++n) {
|
||||
vars.push_back(var[n]);
|
||||
}
|
||||
} else {
|
||||
vars.push_back(var);
|
||||
}
|
||||
|
||||
bool made_move = false;
|
||||
for(std::vector<variant>::const_iterator i = vars.begin(); i != vars.end(); ++i) {
|
||||
if(i->is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const move_callable* move = i->try_convert<move_callable>();
|
||||
const attack_callable* attack = i->try_convert<attack_callable>();
|
||||
const recruit_callable* recruit_command = i->try_convert<recruit_callable>();
|
||||
const set_var_callable* set_var_command = i->try_convert<set_var_callable>();
|
||||
const fallback_callable* fallback_command = i->try_convert<fallback_callable>();
|
||||
|
||||
prepare_move();
|
||||
if(move) {
|
||||
std::cerr << "moving " << move->src().x << "," << move->src().y << " -> " << move->dst().x << "," << move->dst().y << "\n";
|
||||
if(possible_moves_.count(move->src()) > 0) {
|
||||
move_unit(move->src(), move->dst(), possible_moves_);
|
||||
made_move = true;
|
||||
}
|
||||
} else if(attack) {
|
||||
if(get_info().units.count(attack->dst()) == 0) {
|
||||
//this is a legitimate situation; someone might send a series of units in
|
||||
//to attack, but if the defender dies in the middle, we'll save the unit
|
||||
//ordered to move so it can get a different command.
|
||||
continue;
|
||||
}
|
||||
|
||||
if(attack->move_from() != attack->src()) {
|
||||
move_unit(attack->move_from(), attack->src(), possible_moves_);
|
||||
}
|
||||
std::cerr << "ATTACK: " << attack->src() << " -> " << attack->dst() << " " << attack->weapon() << "\n";
|
||||
attack_enemy(attack->src(), attack->dst(), attack->weapon(), attack->defender_weapon());
|
||||
made_move = true;
|
||||
} else if(recruit_command) {
|
||||
std::cerr << "RECRUIT: '" << recruit_command->type() << "'\n";
|
||||
if(recruit(recruit_command->type(), recruit_command->loc())) {
|
||||
made_move = true;
|
||||
}
|
||||
} else if(set_var_command) {
|
||||
std::cerr << "setting var: " << set_var_command->key() << " -> " << set_var_command->value().to_debug_string() << "\n";
|
||||
vars_.add(set_var_command->key(), set_var_command->value());
|
||||
made_move = true;
|
||||
} else if(i->is_string() && i->as_string() == "recruit") {
|
||||
do_recruitment();
|
||||
made_move = true;
|
||||
} else if(i->is_string() && i->as_string() == "end_turn") {
|
||||
return false;
|
||||
} else if(fallback_command) {
|
||||
ai_interface* fallback = create_ai(fallback_command->key(), get_info());
|
||||
if(fallback) {
|
||||
fallback->play_turn();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
std::cerr << "UNRECOGNIZED MOVE\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return made_move;
|
||||
}
|
||||
|
||||
void formula_ai::do_recruitment()
|
||||
{
|
||||
if(!recruit_formula_) {
|
||||
return;
|
||||
}
|
||||
|
||||
variant var = recruit_formula_->execute(*this);
|
||||
std::vector<variant> vars;
|
||||
if(var.is_list()) {
|
||||
for(int n = 0; n != var.num_elements(); ++n) {
|
||||
vars.push_back(var[n]);
|
||||
}
|
||||
} else {
|
||||
vars.push_back(var);
|
||||
}
|
||||
|
||||
for(std::vector<variant>::const_iterator i = vars.begin(); i != vars.end(); ++i) {
|
||||
if(!i->is_string()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!recruit(i->as_string())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do_recruitment();
|
||||
}
|
||||
|
||||
variant formula_ai::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "attacks") {
|
||||
prepare_move();
|
||||
if(attacks_cache_.is_null() == false) {
|
||||
return attacks_cache_;
|
||||
}
|
||||
|
||||
std::vector<attack_analysis> attacks = const_cast<formula_ai*>(this)->analyze_targets(srcdst_, dstsrc_, enemy_srcdst_, enemy_dstsrc_);
|
||||
std::vector<variant> vars;
|
||||
for(std::vector<attack_analysis>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
|
||||
vars.push_back(variant(new attack_analysis(*i)));
|
||||
}
|
||||
|
||||
attacks_cache_ = variant(&vars);
|
||||
return attacks_cache_;
|
||||
} else if(key == "my_moves") {
|
||||
prepare_move();
|
||||
return variant(new move_map_callable(srcdst_, dstsrc_));
|
||||
} else if(key == "enemy_moves") {
|
||||
prepare_move();
|
||||
return variant(new move_map_callable(enemy_srcdst_, enemy_dstsrc_));
|
||||
} else if(key == "my_leader") {
|
||||
unit_map::const_iterator i = team_leader(get_info().team_num, get_info().units);
|
||||
if(i == get_info().units.end()) {
|
||||
return variant();
|
||||
}
|
||||
|
||||
return variant(new location_callable(i->first));
|
||||
} else if(key == "vars") {
|
||||
return variant(&vars_);
|
||||
} else if(key == "keeps") {
|
||||
return get_keeps();
|
||||
}
|
||||
|
||||
return ai_interface::get_value(key);
|
||||
}
|
||||
|
||||
void formula_ai::get_inputs(std::vector<formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("attacks", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("my_moves", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("enemy_moves", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("my_leader", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("vars", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("keeps", FORMULA_READ_ONLY));
|
||||
|
||||
ai_interface::get_inputs(inputs);
|
||||
}
|
||||
|
||||
variant formula_ai::get_keeps() const
|
||||
{
|
||||
if(keeps_cache_.is_null()) {
|
||||
std::vector<variant> vars;
|
||||
for(size_t x = 0; x != size_t(get_info().map.w()); ++x) {
|
||||
for(size_t y = 0; y != size_t(get_info().map.h()); ++y) {
|
||||
const gamemap::location loc(x,y);
|
||||
if(get_info().map.is_keep(loc)) {
|
||||
gamemap::location adj[6];
|
||||
get_adjacent_tiles(loc,adj);
|
||||
for(size_t n = 0; n != 6; ++n) {
|
||||
if(get_info().map.is_castle(adj[n])) {
|
||||
vars.push_back(variant(new location_callable(loc)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
keeps_cache_ = variant(&vars);
|
||||
}
|
||||
|
||||
return keeps_cache_;
|
||||
}
|
52
src/formula_ai.hpp
Normal file
52
src/formula_ai.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef FORMULA_AI_HPP_INCLUDED
|
||||
#define FORMULA_AI_HPP_INCLUDED
|
||||
|
||||
#include "ai.hpp"
|
||||
#include "ai_interface.hpp"
|
||||
#include "formula_fwd.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
|
||||
class formula_ai : public ai {
|
||||
public:
|
||||
explicit formula_ai(info& i);
|
||||
virtual void play_turn();
|
||||
|
||||
using ai_interface::get_info;
|
||||
using ai_interface::current_team;
|
||||
using ai_interface::move_map;
|
||||
|
||||
const move_map& srcdst() const { if(!move_maps_valid_) { prepare_move(); } return srcdst_; }
|
||||
|
||||
std::string evaluate(const std::string& formula_str);
|
||||
|
||||
struct move_map_backup {
|
||||
move_map_backup() : move_maps_valid(false) {}
|
||||
bool move_maps_valid;
|
||||
move_map srcdst, dstsrc, full_srcdst, full_dstsrc, enemy_srcdst, enemy_dstsrc;
|
||||
variant attacks_cache;
|
||||
};
|
||||
|
||||
void swap_move_map(move_map_backup& backup);
|
||||
|
||||
private:
|
||||
void do_recruitment();
|
||||
bool make_move();
|
||||
virtual variant get_value(const std::string& key) const;
|
||||
virtual void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
game_logic::const_formula_ptr recruit_formula_;
|
||||
game_logic::const_formula_ptr move_formula_;
|
||||
|
||||
mutable std::map<location,paths> possible_moves_;
|
||||
|
||||
void prepare_move() const;
|
||||
mutable bool move_maps_valid_;
|
||||
mutable move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_;
|
||||
mutable variant attacks_cache_;
|
||||
mutable variant keeps_cache_;
|
||||
|
||||
variant get_keeps() const;
|
||||
|
||||
game_logic::map_formula_callable vars_;
|
||||
};
|
||||
|
||||
#endif
|
113
src/formula_callable.hpp
Normal file
113
src/formula_callable.hpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#ifndef FORMULA_CALLABLE_HPP_INCLUDED
|
||||
#define FORMULA_CALLABLE_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "reference_counted_object.hpp"
|
||||
#include "variant.hpp"
|
||||
|
||||
namespace game_logic
|
||||
{
|
||||
|
||||
enum FORMULA_ACCESS_TYPE { FORMULA_READ_ONLY, FORMULA_WRITE_ONLY, FORMULA_READ_WRITE };
|
||||
struct formula_input {
|
||||
std::string name;
|
||||
FORMULA_ACCESS_TYPE access;
|
||||
explicit formula_input(const std::string& name, FORMULA_ACCESS_TYPE access=FORMULA_READ_WRITE)
|
||||
: name(name), access(access)
|
||||
{}
|
||||
};
|
||||
|
||||
//interface for objects that can have formulae run on them
|
||||
class formula_callable : public reference_counted_object {
|
||||
public:
|
||||
explicit formula_callable(bool has_self=true) : has_self_(has_self)
|
||||
{}
|
||||
|
||||
variant query_value(const std::string& key) const {
|
||||
if(has_self_ && key == "self") {
|
||||
return variant(this);
|
||||
}
|
||||
return get_value(key);
|
||||
}
|
||||
|
||||
void mutate_value(const std::string& key, const variant& value) {
|
||||
set_value(key, value);
|
||||
}
|
||||
|
||||
std::vector<formula_input> inputs() const {
|
||||
std::vector<formula_input> res;
|
||||
get_inputs(&res);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool equals(const formula_callable* other) const {
|
||||
return do_compare(other) == 0;
|
||||
}
|
||||
|
||||
bool less(const formula_callable* other) const {
|
||||
return do_compare(other) < 0;
|
||||
}
|
||||
|
||||
virtual void get_inputs(std::vector<formula_input>* inputs) const {};
|
||||
protected:
|
||||
virtual ~formula_callable() {}
|
||||
|
||||
virtual void set_value(const std::string& key, const variant& value);
|
||||
virtual int do_compare(const formula_callable* callable) const {
|
||||
return this < callable ? -1 : (this == callable ? 0 : 1);
|
||||
}
|
||||
private:
|
||||
virtual variant get_value(const std::string& key) const = 0;
|
||||
bool has_self_;
|
||||
};
|
||||
|
||||
class formula_callable_no_ref_count : public formula_callable {
|
||||
public:
|
||||
formula_callable_no_ref_count() {
|
||||
turn_reference_counting_off();
|
||||
}
|
||||
virtual ~formula_callable_no_ref_count() {}
|
||||
};
|
||||
|
||||
class formula_callable_with_backup : public formula_callable {
|
||||
const formula_callable& main_;
|
||||
const formula_callable& backup_;
|
||||
variant get_value(const std::string& key) const {
|
||||
variant var = main_.query_value(key);
|
||||
if(var.is_null()) {
|
||||
return backup_.query_value(key);
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
void get_inputs(std::vector<formula_input>* inputs) const {
|
||||
main_.get_inputs(inputs);
|
||||
backup_.get_inputs(inputs);
|
||||
}
|
||||
public:
|
||||
formula_callable_with_backup(const formula_callable& main, const formula_callable& backup) : formula_callable(false), main_(main), backup_(backup)
|
||||
{}
|
||||
};
|
||||
|
||||
class map_formula_callable : public formula_callable {
|
||||
public:
|
||||
explicit map_formula_callable(const formula_callable* fallback=NULL);
|
||||
map_formula_callable& add(const std::string& key, const variant& value);
|
||||
void set_fallback(const formula_callable* fallback) { fallback_ = fallback; }
|
||||
private:
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<formula_input>* inputs) const;
|
||||
void set_value(const std::string& key, const variant& value);
|
||||
std::map<std::string,variant> values_;
|
||||
const formula_callable* fallback_;
|
||||
};
|
||||
|
||||
typedef boost::intrusive_ptr<map_formula_callable> map_formula_callable_ptr;
|
||||
typedef boost::intrusive_ptr<const map_formula_callable> const_map_formula_callable_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
14
src/formula_callable_fwd.hpp
Normal file
14
src/formula_callable_fwd.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef FORMULA_CALLABLE_FWD_HPP_INCLUDED
|
||||
#define FORMULA_CALLABLE_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
|
||||
namespace game_logic {
|
||||
|
||||
class formula_callable;
|
||||
typedef boost::intrusive_ptr<formula_callable> formula_callable_ptr;
|
||||
typedef boost::intrusive_ptr<const formula_callable> const_formula_callable_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
586
src/formula_function.cpp
Normal file
586
src/formula_function.cpp
Normal file
|
@ -0,0 +1,586 @@
|
|||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
#include "foreach.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
#include "formula_function.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
namespace game_logic {
|
||||
|
||||
namespace {
|
||||
|
||||
class dir_function : public function_expression {
|
||||
public:
|
||||
explicit dir_function(const args_list& args)
|
||||
: function_expression("dir", args, 1, 1)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant var = args()[0]->evaluate(variables);
|
||||
const formula_callable* callable = var.as_callable();
|
||||
std::vector<formula_input> inputs = callable->inputs();
|
||||
std::vector<variant> res;
|
||||
foreach(const formula_input& input, inputs) {
|
||||
res.push_back(variant(input.name));
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
}
|
||||
};
|
||||
|
||||
class if_function : public function_expression {
|
||||
public:
|
||||
explicit if_function(const args_list& args)
|
||||
: function_expression("if", args, 3, 3)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const int i = args()[0]->evaluate(variables).as_bool() ? 1 : 2;
|
||||
return args()[i]->evaluate(variables);
|
||||
}
|
||||
};
|
||||
|
||||
class rgb_function : public function_expression {
|
||||
public:
|
||||
explicit rgb_function(const args_list& args)
|
||||
: function_expression("rgb", args, 3, 3)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(10000*
|
||||
std::min<int>(99,std::max<int>(0,args()[0]->evaluate(variables).as_int())) +
|
||||
std::min<int>(99,std::max<int>(0,args()[1]->evaluate(variables).as_int()))*100+
|
||||
std::min<int>(99,std::max<int>(0,args()[2]->evaluate(variables).as_int())));
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
int transition(int begin, int val1, int end, int val2, int value) {
|
||||
if(value < begin || value > end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(value == begin) {
|
||||
return val1;
|
||||
} else if(value == end) {
|
||||
return val2;
|
||||
}
|
||||
|
||||
const int comp1 = val1*(end - value);
|
||||
const int comp2 = val2*(value - begin);
|
||||
return (comp1 + comp2)/(end - begin);
|
||||
}
|
||||
}
|
||||
|
||||
class transition_function : public function_expression {
|
||||
public:
|
||||
explicit transition_function(const args_list& args)
|
||||
: function_expression("transition", args, 5, 5)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const int value = args()[0]->evaluate(variables).as_int();
|
||||
const int begin = args()[1]->evaluate(variables).as_int();
|
||||
const int end = args()[3]->evaluate(variables).as_int();
|
||||
if(value < begin || value > end) {
|
||||
return variant(0);
|
||||
}
|
||||
const int val1 = args()[2]->evaluate(variables).as_int();
|
||||
const int val2 = args()[4]->evaluate(variables).as_int();
|
||||
return variant(transition(begin, val1, end, val2, value));
|
||||
}
|
||||
};
|
||||
|
||||
class color_transition_function : public function_expression {
|
||||
public:
|
||||
explicit color_transition_function(const args_list& args)
|
||||
: function_expression("color_transition", args, 5)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const int value = args()[0]->evaluate(variables).as_int();
|
||||
int begin = args()[1]->evaluate(variables).as_int();
|
||||
int end = -1;
|
||||
int n = 3;
|
||||
while(n < args().size()) {
|
||||
end = args()[n]->evaluate(variables).as_int();
|
||||
if(value >= begin && value <= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
begin = end;
|
||||
n += 2;
|
||||
}
|
||||
|
||||
if(value < begin || value > end) {
|
||||
return variant(0);
|
||||
}
|
||||
const int val1 = args()[n-1]->evaluate(variables).as_int();
|
||||
const int val2 = args()[n+1 < args().size() ? n+1 : n]->
|
||||
evaluate(variables).as_int();
|
||||
const int r1 = (val1/10000)%100;
|
||||
const int g1 = (val1/100)%100;
|
||||
const int b1 = (val1)%100;
|
||||
const int r2 = (val2/10000)%100;
|
||||
const int g2 = (val2/100)%100;
|
||||
const int b2 = (val2)%100;
|
||||
|
||||
const int r = transition(begin,r1,end,r2,value);
|
||||
const int g = transition(begin,g1,end,g2,value);
|
||||
const int b = transition(begin,b1,end,b2,value);
|
||||
return variant(
|
||||
std::min<int>(99,std::max<int>(0,r))*100*100 +
|
||||
std::min<int>(99,std::max<int>(0,g))*100+
|
||||
std::min<int>(99,std::max<int>(0,b)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class abs_function : public function_expression {
|
||||
public:
|
||||
explicit abs_function(const args_list& args)
|
||||
: function_expression("abs", args, 1, 1)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const int n = args()[0]->evaluate(variables).as_int();
|
||||
return variant(n >= 0 ? n : -n);
|
||||
}
|
||||
};
|
||||
|
||||
class min_function : public function_expression {
|
||||
public:
|
||||
explicit min_function(const args_list& args)
|
||||
: function_expression("min", args, 1, -1)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
bool found = false;
|
||||
int res = 0;
|
||||
for(int n = 0; n != args().size(); ++n) {
|
||||
const variant v = args()[n]->evaluate(variables);
|
||||
if(v.is_list()) {
|
||||
for(int m = 0; m != v.num_elements(); ++m) {
|
||||
if(!found || v[m].as_int() < res) {
|
||||
res = v[m].as_int();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else if(v.is_int()) {
|
||||
if(!found || v.as_int() < res) {
|
||||
res = v.as_int();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
class max_function : public function_expression {
|
||||
public:
|
||||
explicit max_function(const args_list& args)
|
||||
: function_expression("max", args, 1, -1)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
bool found = false;
|
||||
int res = 0;
|
||||
for(int n = 0; n != args().size(); ++n) {
|
||||
const variant v = args()[n]->evaluate(variables);
|
||||
if(v.is_list()) {
|
||||
for(int m = 0; m != v.num_elements(); ++m) {
|
||||
if(!found || v[m].as_int() > res) {
|
||||
res = v[m].as_int();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else if(v.is_int()) {
|
||||
if(!found || v.as_int() > res) {
|
||||
res = v.as_int();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variant(res);
|
||||
}
|
||||
};
|
||||
|
||||
class choose_element_function : public function_expression {
|
||||
public:
|
||||
explicit choose_element_function(const args_list& args)
|
||||
: function_expression("choose_element", args, 2, 2)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
int max_index = -1;
|
||||
variant max_value;
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
if(max_index == -1 || val > max_value) {
|
||||
max_index = n;
|
||||
max_value = val;
|
||||
}
|
||||
}
|
||||
|
||||
if(max_index == -1) {
|
||||
return variant(0);
|
||||
} else {
|
||||
return items[max_index];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class wave_function : public function_expression {
|
||||
public:
|
||||
explicit wave_function(const args_list& args)
|
||||
: function_expression("wave", args, 1, 1)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const int value = args()[0]->evaluate(variables).as_int()%1000;
|
||||
const double angle = 2.0*3.141592653589*(static_cast<double>(value)/1000.0);
|
||||
return variant(static_cast<int>(sin(angle)*1000.0));
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
class variant_comparator : public formula_callable {
|
||||
expression_ptr expr_;
|
||||
const formula_callable* fallback_;
|
||||
mutable variant a_, b_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "a") {
|
||||
return a_;
|
||||
} else if(key == "b") {
|
||||
return b_;
|
||||
} else {
|
||||
return fallback_->query_value(key);
|
||||
}
|
||||
}
|
||||
|
||||
void get_inputs(std::vector<formula_input>* inputs) const {
|
||||
fallback_->get_inputs(inputs);
|
||||
}
|
||||
public:
|
||||
variant_comparator(const expression_ptr& expr, const formula_callable& fallback) : expr_(expr), fallback_(&fallback)
|
||||
{}
|
||||
|
||||
bool operator()(const variant& a, const variant& b) const {
|
||||
a_ = a;
|
||||
b_ = b;
|
||||
return expr_->evaluate(*this).as_bool();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class sort_function : public function_expression {
|
||||
public:
|
||||
explicit sort_function(const args_list& args)
|
||||
: function_expression("sort", args, 1, 2)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant list = args()[0]->evaluate(variables);
|
||||
std::vector<variant> vars;
|
||||
vars.reserve(list.num_elements());
|
||||
for(int n = 0; n != list.num_elements(); ++n) {
|
||||
vars.push_back(list[n]);
|
||||
}
|
||||
|
||||
if(args().size() == 1) {
|
||||
std::sort(vars.begin(), vars.end());
|
||||
} else {
|
||||
std::sort(vars.begin(), vars.end(), variant_comparator(args()[1], variables));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
};
|
||||
|
||||
class filter_function : public function_expression {
|
||||
public:
|
||||
explicit filter_function(const args_list& args)
|
||||
: function_expression("filter", args, 2, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
std::vector<variant> vars;
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
if(val.as_bool()) {
|
||||
vars.push_back(items[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
};
|
||||
|
||||
class find_element_function : public function_expression {
|
||||
public:
|
||||
explicit find_element_function(const args_list& args)
|
||||
: function_expression("find_element", args, 2, 3)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
|
||||
if(args().size() == 2) {
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
if(val.as_bool()) {
|
||||
return items[n];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map_formula_callable self_callable;
|
||||
const std::string self = args()[1]->evaluate(variables).as_string();
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
self_callable.add(self, items[n]);
|
||||
const variant val = args().back()->evaluate(formula_callable_with_backup(self_callable, formula_callable_with_backup(*items[n].as_callable(), variables)));
|
||||
if(val.as_bool()) {
|
||||
return items[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variant();
|
||||
}
|
||||
};
|
||||
|
||||
class map_function : public function_expression {
|
||||
public:
|
||||
explicit map_function(const args_list& args)
|
||||
: function_expression("map", args, 2, 3)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
std::vector<variant> vars;
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
|
||||
if(args().size() == 2) {
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
const variant val = args().back()->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
vars.push_back(val);
|
||||
}
|
||||
} else {
|
||||
map_formula_callable self_callable;
|
||||
const std::string self = args()[1]->evaluate(variables).as_string();
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
self_callable.add(self, items[n]);
|
||||
const variant val = args().back()->evaluate(formula_callable_with_backup(self_callable, formula_callable_with_backup(*items[n].as_callable(), variables)));
|
||||
vars.push_back(val);
|
||||
}
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
};
|
||||
|
||||
class sum_function : public function_expression {
|
||||
public:
|
||||
explicit sum_function(const args_list& args)
|
||||
: function_expression("sum", args, 1, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant res(0);
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
if(args().size() >= 2) {
|
||||
res = args()[1]->evaluate(variables);
|
||||
}
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
res = res + items[n];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class head_function : public function_expression {
|
||||
public:
|
||||
explicit head_function(const args_list& args)
|
||||
: function_expression("head", args, 1, 1)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
return items[0];
|
||||
}
|
||||
};
|
||||
|
||||
class size_function : public function_expression {
|
||||
public:
|
||||
explicit size_function(const args_list& args)
|
||||
: function_expression("size", args, 1, 1)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
return variant(static_cast<int>(items.num_elements()));
|
||||
}
|
||||
};
|
||||
|
||||
class null_function : public function_expression {
|
||||
public:
|
||||
explicit null_function(const args_list& args)
|
||||
: function_expression("null", args, 0, 0)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant();
|
||||
}
|
||||
};
|
||||
|
||||
class refcount_function : public function_expression {
|
||||
public:
|
||||
explicit refcount_function(const args_list& args)
|
||||
: function_expression("refcount", args, 1, 1)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(args()[0]->evaluate(variables).refcount());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
formula_function_expression::formula_function_expression(const std::string& name, const args_list& args, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& arg_names)
|
||||
: function_expression(name, args, arg_names.size(), arg_names.size()),
|
||||
formula_(formula), precondition_(precondition), arg_names_(arg_names), star_arg_(-1)
|
||||
{
|
||||
for(int n = 0; n != arg_names_.size(); ++n) {
|
||||
if(arg_names_.empty() == false && arg_names_[n][arg_names_[n].size()-1] == '*') {
|
||||
arg_names_[n].resize(arg_names_[n].size()-1);
|
||||
star_arg_ = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variant formula_function_expression::execute(const formula_callable& variables) const
|
||||
{
|
||||
static std::string indent;
|
||||
indent += " ";
|
||||
std::cerr << indent << "executing '" << formula_->str() << "'\n";
|
||||
const int begin_time = SDL_GetTicks();
|
||||
map_formula_callable callable;
|
||||
for(int n = 0; n != arg_names_.size(); ++n) {
|
||||
variant var = args()[n]->evaluate(variables);
|
||||
callable.add(arg_names_[n], var);
|
||||
if(n == star_arg_) {
|
||||
callable.set_fallback(var.as_callable());
|
||||
}
|
||||
}
|
||||
|
||||
if(precondition_) {
|
||||
if(!precondition_->execute(callable).as_bool()) {
|
||||
std::cerr << "FAILED function precondition for function '" << formula_->str() << "' with arguments: ";
|
||||
for(int n = 0; n != arg_names_.size(); ++n) {
|
||||
std::cerr << " arg " << (n+1) << ": " << args()[n]->evaluate(variables).to_debug_string() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variant res = formula_->execute(callable);
|
||||
const int taken = SDL_GetTicks() - begin_time;
|
||||
std::cerr << indent << "returning: " << taken << "\n";
|
||||
indent.resize(indent.size() - 2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function_expression_ptr formula_function::generate_function_expression(const std::vector<expression_ptr>& args) const
|
||||
{
|
||||
return function_expression_ptr(new formula_function_expression(name_, args, formula_, precondition_, args_));
|
||||
}
|
||||
|
||||
void function_symbol_table::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
|
||||
{
|
||||
custom_formulas_[name] = formula_function(name, formula, precondition, args);
|
||||
}
|
||||
|
||||
expression_ptr function_symbol_table::create_function(const std::string& fn, const std::vector<expression_ptr>& args) const
|
||||
{
|
||||
const std::map<std::string, formula_function>::const_iterator i = custom_formulas_.find(fn);
|
||||
if(i != custom_formulas_.end()) {
|
||||
return i->second.generate_function_expression(args);
|
||||
}
|
||||
|
||||
return expression_ptr();
|
||||
}
|
||||
|
||||
expression_ptr create_function(const std::string& fn,
|
||||
const std::vector<expression_ptr>& args,
|
||||
const function_symbol_table* symbols)
|
||||
{
|
||||
if(symbols) {
|
||||
expression_ptr res(symbols->create_function(fn, args));
|
||||
if(res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "FN: '" << fn << "' " << fn.size() << "\n";
|
||||
if(fn == "dir") {
|
||||
return expression_ptr(new dir_function(args));
|
||||
} else if(fn == "if") {
|
||||
return expression_ptr(new if_function(args));
|
||||
} else if(fn == "abs") {
|
||||
return expression_ptr(new abs_function(args));
|
||||
} else if(fn == "min") {
|
||||
return expression_ptr(new min_function(args));
|
||||
} else if(fn == "max") {
|
||||
return expression_ptr(new max_function(args));
|
||||
} else if(fn == "choose") {
|
||||
return expression_ptr(new choose_element_function(args));
|
||||
} else if(fn == "wave") {
|
||||
return expression_ptr(new wave_function(args));
|
||||
} else if(fn == "sort") {
|
||||
return expression_ptr(new sort_function(args));
|
||||
} else if(fn == "filter") {
|
||||
return expression_ptr(new filter_function(args));
|
||||
} else if(fn == "find") {
|
||||
return expression_ptr(new find_element_function(args));
|
||||
} else if(fn == "map") {
|
||||
return expression_ptr(new map_function(args));
|
||||
} else if(fn == "sum") {
|
||||
return expression_ptr(new sum_function(args));
|
||||
} else if(fn == "head") {
|
||||
return expression_ptr(new head_function(args));
|
||||
} else if(fn == "rgb") {
|
||||
return expression_ptr(new rgb_function(args));
|
||||
} else if(fn == "transition") {
|
||||
return expression_ptr(new transition_function(args));
|
||||
} else if(fn == "color_transition") {
|
||||
return expression_ptr(new color_transition_function(args));
|
||||
} else if(fn == "size") {
|
||||
return expression_ptr(new size_function(args));
|
||||
} else if(fn == "null") {
|
||||
return expression_ptr(new null_function(args));
|
||||
} else if(fn == "refcount") {
|
||||
return expression_ptr(new refcount_function(args));
|
||||
} else {
|
||||
std::cerr << "no function '" << fn << "'\n";
|
||||
throw formula_error();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
98
src/formula_function.hpp
Normal file
98
src/formula_function.hpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#ifndef FORMULA_FUNCTION_HPP_INCLUDED
|
||||
#define FORMULA_FUNCTION_HPP_INCLUDED
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "formula.hpp"
|
||||
#include "variant.hpp"
|
||||
|
||||
namespace game_logic {
|
||||
|
||||
class formula_expression {
|
||||
public:
|
||||
formula_expression() : name_(NULL) {}
|
||||
virtual ~formula_expression() {}
|
||||
variant evaluate(const formula_callable& variables) const {
|
||||
call_stack_manager manager(name_);
|
||||
return execute(variables);
|
||||
}
|
||||
void set_name(const char* name) { name_ = name; }
|
||||
private:
|
||||
virtual variant execute(const formula_callable& variables) const = 0;
|
||||
const char* name_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<formula_expression> expression_ptr;
|
||||
|
||||
class function_expression : public formula_expression {
|
||||
public:
|
||||
typedef std::vector<expression_ptr> args_list;
|
||||
explicit function_expression(
|
||||
const std::string& name,
|
||||
const args_list& args,
|
||||
int min_args=-1, int max_args=-1)
|
||||
: name_(name), args_(args)
|
||||
{
|
||||
set_name(name.c_str());
|
||||
if(min_args != -1 && args_.size() < min_args) {
|
||||
std::cerr << "too few arguments\n";
|
||||
throw formula_error();
|
||||
}
|
||||
|
||||
if(max_args != -1 && args_.size() > max_args) {
|
||||
std::cerr << "too many arguments\n";
|
||||
throw formula_error();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
const args_list& args() const { return args_; }
|
||||
private:
|
||||
std::string name_;
|
||||
args_list args_;
|
||||
};
|
||||
|
||||
class formula_function_expression : public function_expression {
|
||||
public:
|
||||
explicit formula_function_expression(const std::string& name, const args_list& args, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& arg_names);
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const;
|
||||
const_formula_ptr formula_;
|
||||
const_formula_ptr precondition_;
|
||||
std::vector<std::string> arg_names_;
|
||||
int star_arg_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<function_expression> function_expression_ptr;
|
||||
|
||||
class formula_function {
|
||||
std::string name_;
|
||||
const_formula_ptr formula_;
|
||||
const_formula_ptr precondition_;
|
||||
std::vector<std::string> args_;
|
||||
public:
|
||||
formula_function() {}
|
||||
formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args) : name_(name), formula_(formula), precondition_(precondition), args_(args)
|
||||
{}
|
||||
|
||||
function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const;
|
||||
};
|
||||
|
||||
class function_symbol_table {
|
||||
std::map<std::string, formula_function> custom_formulas_;
|
||||
public:
|
||||
virtual ~function_symbol_table() {}
|
||||
void add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args);
|
||||
virtual expression_ptr create_function(const std::string& fn,
|
||||
const std::vector<expression_ptr>& args) const;
|
||||
};
|
||||
|
||||
expression_ptr create_function(const std::string& fn,
|
||||
const std::vector<expression_ptr>& args,
|
||||
const function_symbol_table* symbols);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
14
src/formula_fwd.hpp
Normal file
14
src/formula_fwd.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef FORMULA_FWD_HPP_INCLUDED
|
||||
#define FORMULA_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace game_logic {
|
||||
|
||||
class formula;
|
||||
typedef boost::shared_ptr<formula> formula_ptr;
|
||||
typedef boost::shared_ptr<const formula> const_formula_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
89
src/formula_tokenizer.cpp
Normal file
89
src/formula_tokenizer.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2007 by David White <dave.net>
|
||||
Part of the Silver Tree Project
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 or later.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include "foreach.hpp"
|
||||
#include "formula_tokenizer.hpp"
|
||||
|
||||
namespace formula_tokenizer
|
||||
{
|
||||
|
||||
namespace {
|
||||
struct token_type {
|
||||
boost::regex re;
|
||||
TOKEN_TYPE type;
|
||||
};
|
||||
}
|
||||
|
||||
token get_token(iterator& i1, iterator i2) {
|
||||
using boost::regex;
|
||||
|
||||
token_type types[] = { { regex("^(not\\b|and\\b|or\\b|where\\b|d(?=[^a-zA-Z])|\\*|\\+|\\-|\\^|%|/|<=|>=|<|>|!=|=|\\.)"), TOKEN_OPERATOR },
|
||||
{ regex("^'[^']*'"), TOKEN_STRING_LITERAL },
|
||||
{ regex("^[a-zA-Z_]+"), TOKEN_IDENTIFIER },
|
||||
{ regex("^\\d+"), TOKEN_INTEGER },
|
||||
{ regex("^\\("), TOKEN_LPARENS },
|
||||
{ regex("^\\)"), TOKEN_RPARENS },
|
||||
{ regex("^\\["), TOKEN_LSQUARE },
|
||||
{ regex("^\\]"), TOKEN_RSQUARE },
|
||||
{ regex("^,"), TOKEN_COMMA },
|
||||
{ regex("^\\s+"), TOKEN_WHITESPACE } };
|
||||
|
||||
foreach(const token_type& t, types) {
|
||||
boost::smatch match;
|
||||
if(regex_search(i1, i2, match, t.re)) {
|
||||
token res;
|
||||
res.type = t.type;
|
||||
res.begin = i1;
|
||||
i1 = res.end = i1 + match.length();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Unrecognized token: '" << std::string(i1,i2) << "'\n";
|
||||
throw token_error();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST_TOKENIZER
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace formula_tokenizer;
|
||||
std::string test = "(abc + 4 * (5+3))^2";
|
||||
std::string::const_iterator i1 = test.begin();
|
||||
std::string::const_iterator i2 = test.end();
|
||||
TOKEN_TYPE types[] = {TOKEN_LPARENS, TOKEN_IDENTIFIER,
|
||||
TOKEN_WHITESPACE, TOKEN_OPERATOR,
|
||||
TOKEN_WHITESPACE, TOKEN_INTEGER,
|
||||
TOKEN_WHITESPACE, TOKEN_OPERATOR,
|
||||
TOKEN_WHITESPACE, TOKEN_LPARENS,
|
||||
TOKEN_INTEGER, TOKEN_OPERATOR,
|
||||
TOKEN_INTEGER, TOKEN_RPARENS,
|
||||
TOKEN_RPARENS,
|
||||
TOKEN_OPERATOR, TOKEN_INTEGER};
|
||||
std::string tokens[] = {"(", "abc", " ", "+", " ", "4", " ",
|
||||
"*", " ", "(", "5", "+", "3", ")", ")"};
|
||||
for(int n = 0; n != sizeof(types)/sizeof(*types); ++n) {
|
||||
token t = get_token(i1,i2);
|
||||
assert(std::string(t.begin,t.end) == tokens[n]);
|
||||
assert(t.type == types[n]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
40
src/formula_tokenizer.hpp
Normal file
40
src/formula_tokenizer.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2007 by David White <dave.net>
|
||||
Part of the Silver Tree Project
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 or later.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef FORMULA_TOKENIZER_HPP_INCLUDED
|
||||
#define FORMULA_TOKENIZER_HPP_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace formula_tokenizer
|
||||
{
|
||||
|
||||
typedef std::string::const_iterator iterator;
|
||||
|
||||
enum TOKEN_TYPE { TOKEN_OPERATOR, TOKEN_STRING_LITERAL,
|
||||
TOKEN_IDENTIFIER, TOKEN_INTEGER,
|
||||
TOKEN_LPARENS, TOKEN_RPARENS,
|
||||
TOKEN_LSQUARE, TOKEN_RSQUARE, TOKEN_COMMA,
|
||||
TOKEN_WHITESPACE };
|
||||
|
||||
struct token {
|
||||
TOKEN_TYPE type;
|
||||
iterator begin, end;
|
||||
};
|
||||
|
||||
token get_token(iterator& i1, iterator i2);
|
||||
|
||||
struct token_error {};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -137,6 +137,7 @@ const struct {
|
|||
{ hotkey::HOTKEY_LANGUAGE, "changelanguage", N_("Change the language"), true },
|
||||
|
||||
{ hotkey::HOTKEY_USER_CMD, "command", N_("Enter user command"), false },
|
||||
{ hotkey::HOTKEY_AI_FORMULA, "aiformula", N_("Run AI formula"), false },
|
||||
{ hotkey::HOTKEY_CLEAR_MSG, "clearmessages", N_("Clear messages"), false },
|
||||
#ifdef USRCMD2
|
||||
{ hotkey::HOTKEY_USER_CMD_2, "usercommand#2", N_("User-Command#2"), false },
|
||||
|
@ -152,8 +153,6 @@ hotkey::hotkey_item null_hotkey_;
|
|||
|
||||
namespace hotkey {
|
||||
|
||||
const std::string CLEARED_TEXT = "__none__";
|
||||
|
||||
static void key_event_execute(display& disp, const SDL_KeyboardEvent& event, command_executor* executor);
|
||||
|
||||
|
||||
|
@ -192,11 +191,7 @@ void hotkey_item::load_from_config(const config& cfg)
|
|||
if (!key.empty()) {
|
||||
// They may really want a specific key on the keyboard: we assume
|
||||
// that any single character keyname is a character.
|
||||
if (key == CLEARED_TEXT)
|
||||
{
|
||||
type_ = hotkey_item::CLEARED;
|
||||
}
|
||||
else if (key.size() > 1) {
|
||||
if (key.size() > 1) {
|
||||
type_ = BY_KEYCODE;
|
||||
|
||||
keycode_ = sdl_keysym_from_name(key);
|
||||
|
@ -260,7 +255,7 @@ void hotkey_item::set_description(const std::string& description)
|
|||
}
|
||||
void hotkey_item::clear_hotkey()
|
||||
{
|
||||
type_ = CLEARED;
|
||||
type_ = UNBOUND;
|
||||
}
|
||||
|
||||
void hotkey_item::set_key(int character, int keycode, bool shift, bool ctrl, bool alt, bool cmd)
|
||||
|
@ -350,17 +345,10 @@ void save_hotkeys(config& cfg)
|
|||
for(std::vector<hotkey_item>::iterator i = hotkeys_.begin(); i != hotkeys_.end(); ++i) {
|
||||
if (i->hidden() || i->get_type() == hotkey_item::UNBOUND)
|
||||
continue;
|
||||
|
||||
config& item = cfg.add_child("hotkey");
|
||||
|
||||
item["command"] = i->get_command();
|
||||
|
||||
if (i->get_type() == hotkey_item::CLEARED)
|
||||
{
|
||||
item["key"] = CLEARED_TEXT;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (i->get_type() == hotkey_item::BY_KEYCODE) {
|
||||
item["key"] = SDL_GetKeyName(SDLKey(i->get_keycode()));
|
||||
item["shift"] = i->get_shift() ? "yes" : "no";
|
||||
|
@ -675,6 +663,10 @@ bool command_executor::execute_command(HOTKEY_COMMAND command, int /*index*/)
|
|||
user_command();
|
||||
break;
|
||||
//%%
|
||||
case HOTKEY_AI_FORMULA:
|
||||
std::cerr <<" run ai formula\n";
|
||||
ai_formula();
|
||||
break;
|
||||
case HOTKEY_CLEAR_MSG:
|
||||
clear_messages();
|
||||
break;
|
||||
|
@ -844,7 +836,7 @@ void execute_command(display& disp, HOTKEY_COMMAND command, command_executor* ex
|
|||
break;
|
||||
case HOTKEY_QUIT_GAME: {
|
||||
if(disp.in_game()) {
|
||||
DBG_G << "is in game -- showing quit message\n";
|
||||
ERR_G << "is in game -- showing quit message\n";
|
||||
const int res = gui::dialog(disp,_("Quit"),_("Do you really want to quit?"),gui::YES_NO).show();
|
||||
if(res == 0) {
|
||||
throw end_level_exception(QUIT);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
|
||||
Copyright (C) 2003 - 2007 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -61,6 +61,7 @@ enum HOTKEY_COMMAND {
|
|||
|
||||
//misc.
|
||||
HOTKEY_USER_CMD,
|
||||
HOTKEY_AI_FORMULA,
|
||||
HOTKEY_CLEAR_MSG,
|
||||
#ifdef USRCMD2
|
||||
HOTKEY_USER_CMD_2,
|
||||
|
@ -100,8 +101,7 @@ public:
|
|||
enum type {
|
||||
UNBOUND,
|
||||
BY_KEYCODE,
|
||||
BY_CHARACTER,
|
||||
CLEARED
|
||||
BY_CHARACTER
|
||||
};
|
||||
|
||||
enum type get_type() const { return type_; }
|
||||
|
@ -211,6 +211,7 @@ public:
|
|||
virtual void show_help() {}
|
||||
virtual void show_chat_log() {}
|
||||
virtual void user_command() {}
|
||||
virtual void ai_formula() {}
|
||||
virtual void clear_messages() {}
|
||||
#ifdef USRCMD2
|
||||
virtual void user_command_2() {}
|
||||
|
|
16
src/map_utils.hpp
Normal file
16
src/map_utils.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef MAP_UTILS_HPP_INCLUDED
|
||||
#define MAP_UTILS_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
|
||||
template<typename K, typename V>
|
||||
const V& map_get_value_default(const std::map<K,V>& m, const K& key, const V& val) {
|
||||
typename std::map<K,V>::const_iterator i = m.find(key);
|
||||
if(i != m.end()) {
|
||||
return i->second;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "construct_dialog.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "formula_ai.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_errors.hpp"
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include "log.hpp"
|
||||
#include "marked-up_text.hpp"
|
||||
#include "menu_events.hpp"
|
||||
#include "playturn.hpp"
|
||||
#include "preferences_display.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "sound.hpp"
|
||||
|
@ -337,27 +339,6 @@ namespace events{
|
|||
return textbox_info_;
|
||||
}
|
||||
|
||||
std::string menu_handler::get_title_suffix(int team_num)
|
||||
{
|
||||
int controlled_recruiters = 0;
|
||||
for(size_t i = 0; i < teams_.size(); ++i) {
|
||||
if(teams_[i].is_human() && !teams_[i].recruits().empty()
|
||||
&& team_leader(i+1, units_) != units_.end()) {
|
||||
++controlled_recruiters;
|
||||
}
|
||||
}
|
||||
std::stringstream msg;
|
||||
if(controlled_recruiters >= 2) {
|
||||
const unit_map::const_iterator leader = team_leader(team_num, units_);
|
||||
if(leader != units_.end() && !leader->second.name().empty()) {
|
||||
msg << " (";
|
||||
msg << leader->second.name();
|
||||
msg << ")";
|
||||
}
|
||||
}
|
||||
return msg.str();
|
||||
}
|
||||
|
||||
void menu_handler::objectives(const unsigned int team_num)
|
||||
{
|
||||
dialogs::show_objectives(*gui_, level_, teams_[team_num - 1].objectives());
|
||||
|
@ -605,8 +586,7 @@ private:
|
|||
}
|
||||
str << COLUMN_SEPARATOR << team::get_side_highlight(n)
|
||||
<< teams_[n].current_player() << COLUMN_SEPARATOR
|
||||
<< (data.teamname.empty() ? teams_[n].team_name() : data.teamname)
|
||||
<< COLUMN_SEPARATOR;
|
||||
<< data.teamname << COLUMN_SEPARATOR;
|
||||
|
||||
if(!known && !game_config::debug) {
|
||||
// We don't spare more info (only name)
|
||||
|
@ -1034,7 +1014,7 @@ private:
|
|||
std::vector<gui::preview_pane*> preview_panes;
|
||||
preview_panes.push_back(&unit_preview);
|
||||
|
||||
gui::dialog rmenu(*gui_,_("Recruit") + get_title_suffix(team_num),
|
||||
gui::dialog rmenu(*gui_,_("Recruit"),
|
||||
_("Select unit:") + std::string("\n"),
|
||||
gui::OK_CANCEL,
|
||||
gui::dialog::default_style);
|
||||
|
@ -1185,7 +1165,7 @@ private:
|
|||
|
||||
{
|
||||
dialogs::units_list_preview_pane unit_preview(*gui_,&map_,recall_list);
|
||||
gui::dialog rmenu(*gui_,_("Recall") + get_title_suffix(team_num),
|
||||
gui::dialog rmenu(*gui_,_("Recall"),
|
||||
_("Select unit:") + std::string("\n"),
|
||||
gui::OK_CANCEL,
|
||||
gui::dialog::default_style);
|
||||
|
@ -1324,7 +1304,6 @@ private:
|
|||
up->second.set_movement(starting_moves);
|
||||
up->first = route.back();
|
||||
units_.add(up);
|
||||
unit::clear_status_caches();
|
||||
up->second.set_standing(up->first);
|
||||
gui_->invalidate(route.back());
|
||||
gui_->draw();
|
||||
|
@ -1441,7 +1420,6 @@ private:
|
|||
up->second.set_movement(starting_moves);
|
||||
up->first = route.back();
|
||||
units_.add(up);
|
||||
unit::clear_status_caches();
|
||||
up->second.set_standing(up->first);
|
||||
|
||||
if(map_.is_village(route.back())) {
|
||||
|
@ -1532,7 +1510,7 @@ private:
|
|||
}
|
||||
|
||||
//Ask for confirmation if the player hasn't made any moves (other than gotos).
|
||||
if(preferences::confirm_no_moves() && units_alive && !some_units_have_moved) {
|
||||
if(false && preferences::confirm_no_moves() && units_alive && !some_units_have_moved) {
|
||||
const int res = gui::dialog(*gui_,"",_("You have not started your turn yet. Do you really want to end your turn?"), gui::YES_NO).show();
|
||||
if(res != 0) {
|
||||
return false;
|
||||
|
@ -1959,7 +1937,6 @@ private:
|
|||
_("Add a nick to your friends list."
|
||||
" Usage: /list addfriend <nick>"));
|
||||
} else if (subcommand == "addignore"){
|
||||
// s/ignore/addignore/
|
||||
add_chat_message(time(NULL), "help", 0 ,
|
||||
_("Add a nick to your ignores list."
|
||||
" Usage: /list ignore <nick>"));
|
||||
|
@ -2224,7 +2201,14 @@ private:
|
|||
if (player == preferences::login())
|
||||
return;
|
||||
change_side_controller(side,player,true);
|
||||
teams_[side_num - 1].make_network();
|
||||
textbox_info_.close(*gui_);
|
||||
if(team_num == side_num) {
|
||||
//if it is our turn at the moment, we have to indicate to the
|
||||
//play_controller, that we are no longer in control
|
||||
gui_->set_team(0);
|
||||
throw end_turn_exception(side_num);
|
||||
}
|
||||
} else {
|
||||
//it is not our side, the server will decide if we can change the
|
||||
//controller (that is if we are host of the game)
|
||||
|
@ -2335,11 +2319,34 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void menu_handler::do_ai_formula(const std::string& str,
|
||||
const unsigned int team_num, mouse_handler& mousehandler)
|
||||
{
|
||||
replay dummy_replay;
|
||||
replay_network_sender dummy_sender(dummy_replay);
|
||||
undo_list dummy_undo;
|
||||
|
||||
turn_info turn_data(gameinfo_, gamestate_, status_, *gui_, const_cast<gamemap&>(map_), teams_, team_num, units_, dummy_sender, dummy_undo);
|
||||
ai_interface::info info(*gui_, map_, gameinfo_, units_, teams_, team_num, status_, turn_data, gamestate_);
|
||||
formula_ai eval(info);
|
||||
try {
|
||||
add_chat_message(time(NULL), _("ai"), 0, eval.evaluate(str));
|
||||
} catch(...) {
|
||||
add_chat_message(time(NULL), _("ai"), 0, "ERROR IN FORMULA");
|
||||
}
|
||||
}
|
||||
|
||||
void menu_handler::user_command()
|
||||
{
|
||||
textbox_info_.show(gui::TEXTBOX_COMMAND,sgettext("prompt^Command:"), "", false, *gui_);
|
||||
}
|
||||
|
||||
void menu_handler::ai_formula()
|
||||
{
|
||||
std::cerr << "showing ai formula...\n";
|
||||
textbox_info_.show(gui::TEXTBOX_AI,sgettext("prompt^Command:"), "", false, *gui_);
|
||||
}
|
||||
|
||||
void menu_handler::clear_messages()
|
||||
{
|
||||
gui_->clear_chat_messages(); // also clear debug-messages and WML-error-messages
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -99,7 +99,6 @@ public:
|
|||
gui::floating_textbox& get_textbox();
|
||||
void set_gui(game_display* gui) { gui_ = gui; }
|
||||
|
||||
std::string get_title_suffix(int team_num);
|
||||
void objectives(const unsigned int team_num);
|
||||
void show_statistics(const unsigned int team_num);
|
||||
void unit_list();
|
||||
|
@ -135,6 +134,7 @@ public:
|
|||
void end_unit_turn(mouse_handler& mousehandler, const unsigned int team_num);
|
||||
void search();
|
||||
void user_command();
|
||||
void ai_formula();
|
||||
void clear_messages();
|
||||
#ifdef USRCMD2
|
||||
void user_command_2();
|
||||
|
@ -148,6 +148,7 @@ public:
|
|||
void do_speak();
|
||||
void do_search(const std::string& new_search);
|
||||
void do_command(const std::string& str, const unsigned int team_num, mouse_handler& mousehandler);
|
||||
void do_ai_formula(const std::string& str, const unsigned int team_num, mouse_handler& mousehandler);
|
||||
void clear_undo_stack(const unsigned int team_num);
|
||||
void autosave(const std::string &label, unsigned turn, const config &starting_pos) const;
|
||||
bool has_team() const;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -276,14 +276,10 @@ void play_controller::show_help(){
|
|||
}
|
||||
|
||||
void play_controller::undo(){
|
||||
// deselect unit (only here, not to be done when undoing attack-move)
|
||||
mouse_handler_.deselect_hex();
|
||||
menu_handler_.undo(player_number_);
|
||||
}
|
||||
|
||||
void play_controller::redo(){
|
||||
// deselect unit (only here, not to be done when undoing attack-move)
|
||||
mouse_handler_.deselect_hex();
|
||||
menu_handler_.redo(player_number_);
|
||||
}
|
||||
|
||||
|
@ -350,8 +346,7 @@ void play_controller::init_side(const unsigned int team_index, bool /*is_replay*
|
|||
&& !current_team.get_disallow_observers()) {
|
||||
gui_->set_team(size_t(team_index));
|
||||
}
|
||||
|
||||
gui_->set_playing_team(size_t(team_index));
|
||||
gui_->set_playing_team(size_t(team_index));
|
||||
|
||||
std::stringstream player_number_str;
|
||||
player_number_str << player_number_;
|
||||
|
@ -421,7 +416,7 @@ void play_controller::init_side(const unsigned int team_index, bool /*is_replay*
|
|||
gui_->invalidate_all();
|
||||
}
|
||||
|
||||
if (!recorder.is_skipping() && !skip_replay_){
|
||||
if (!recorder.is_skipping()){
|
||||
gui_->scroll_to_leader(units_, player_number_);
|
||||
}
|
||||
}
|
||||
|
@ -532,6 +527,7 @@ bool play_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int in
|
|||
case hotkey::HOTKEY_SEARCH:
|
||||
case hotkey::HOTKEY_HELP:
|
||||
case hotkey::HOTKEY_USER_CMD:
|
||||
case hotkey::HOTKEY_AI_FORMULA:
|
||||
case hotkey::HOTKEY_CLEAR_MSG:
|
||||
#ifdef USRCMD2
|
||||
//%%
|
||||
|
@ -591,6 +587,9 @@ void play_controller::enter_textbox()
|
|||
case gui::TEXTBOX_COMMAND:
|
||||
menu_handler_.do_command(menu_handler_.get_textbox().box()->text(), player_number_, mouse_handler_);
|
||||
break;
|
||||
case gui::TEXTBOX_AI:
|
||||
menu_handler_.do_ai_formula(menu_handler_.get_textbox().box()->text(), player_number_, mouse_handler_);
|
||||
break;
|
||||
default:
|
||||
LOG_STREAM(err, display) << "unknown textbox mode\n";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -131,6 +131,10 @@ void playsingle_controller::user_command(){
|
|||
menu_handler_.user_command();
|
||||
}
|
||||
|
||||
void playsingle_controller::ai_formula(){
|
||||
menu_handler_.ai_formula();
|
||||
}
|
||||
|
||||
void playsingle_controller::clear_messages(){
|
||||
menu_handler_.clear_messages();
|
||||
}
|
||||
|
@ -674,12 +678,6 @@ void playsingle_controller::after_human_turn(){
|
|||
end_turn_record();
|
||||
end_turn_record_unlock();
|
||||
menu_handler_.clear_undo_stack(player_number_);
|
||||
|
||||
if(teams_[player_number_-1].uses_fog()) {
|
||||
// needed because currently fog is only recalculated when a hex is /un/covered
|
||||
recalculate_fog(map_,units_,teams_,player_number_-1);
|
||||
}
|
||||
|
||||
gui_->set_route(NULL);
|
||||
gui_->unhighlight_reach();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
Copyright (C) 2006 - 2007 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
|
||||
wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
|
@ -51,6 +51,7 @@ public:
|
|||
virtual void unit_hold_position();
|
||||
virtual void end_unit_turn();
|
||||
virtual void user_command();
|
||||
virtual void ai_formula();
|
||||
virtual void clear_messages();
|
||||
#ifdef USRCMD2
|
||||
virtual void user_command_2();
|
||||
|
|
38
src/reference_counted_object.hpp
Normal file
38
src/reference_counted_object.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef REFERENCE_COUNTED_OBJECT_HPP_INCLUDED
|
||||
#define REFERENCE_COUNTED_OBJECT_HPP_INCLUDED
|
||||
|
||||
#include "boost/intrusive_ptr.hpp"
|
||||
|
||||
class reference_counted_object
|
||||
{
|
||||
public:
|
||||
reference_counted_object() : count_(0) {}
|
||||
reference_counted_object(const reference_counted_object& obj) : count_(0) {}
|
||||
reference_counted_object& operator=(const reference_counted_object& obj) {
|
||||
return *this;
|
||||
}
|
||||
virtual ~reference_counted_object() {}
|
||||
|
||||
void add_ref() const { ++count_; }
|
||||
void dec_ref() const { if(--count_ == 0) { delete const_cast<reference_counted_object*>(this); } }
|
||||
|
||||
int refcount() const { return count_; }
|
||||
|
||||
protected:
|
||||
void turn_reference_counting_off() { count_ = 1000000; }
|
||||
private:
|
||||
mutable int count_;
|
||||
};
|
||||
|
||||
inline void intrusive_ptr_add_ref(const reference_counted_object* obj) {
|
||||
obj->add_ref();
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(const reference_counted_object* obj) {
|
||||
obj->dec_ref();
|
||||
}
|
||||
|
||||
typedef boost::intrusive_ptr<reference_counted_object> object_ptr;
|
||||
typedef boost::intrusive_ptr<const reference_counted_object> const_object_ptr;
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2004 - 2008 by Philippe Plantier <ayin@anathas.org>
|
||||
Copyright (C) 2004 - 2007 by Philippe Plantier <ayin@anathas.org>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Rusty Russell <rusty@rustcorp.com.au>
|
||||
Copyright (C) 2006 - 2007 by Rusty Russell <rusty@rustcorp.com.au>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -46,11 +46,11 @@ unit_map::~unit_map()
|
|||
}
|
||||
|
||||
// Due to unit <-> unit_map dependencies, must be out of line.
|
||||
std::pair<gamemap::location,unit> unit_map::iterator::operator*() const
|
||||
std::pair<gamemap::location,unit>& unit_map::iterator::operator*() const
|
||||
{
|
||||
return *i_->second;
|
||||
}
|
||||
std::pair<gamemap::location,unit> unit_map::const_iterator::operator*() const
|
||||
const std::pair<gamemap::location,unit>& unit_map::const_iterator::operator*() const
|
||||
{
|
||||
return *i_->second;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2006 - 2008 by Rusty Russell <rusty@rustcorp.com.au>
|
||||
Copyright (C) 2006 - 2007 by Rusty Russell <rusty@rustcorp.com.au>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
const std::pair<gamemap::location,unit>* operator->() const
|
||||
{ return i_->second; }
|
||||
|
||||
std::pair<gamemap::location,unit> operator*() const;
|
||||
const std::pair<gamemap::location,unit>& operator*() const;
|
||||
|
||||
const_iterator operator++()
|
||||
{ return const_iterator(++i_); }
|
||||
|
@ -81,7 +81,7 @@ public:
|
|||
std::pair<gamemap::location,unit> *operator->() const
|
||||
{ return i_->second; }
|
||||
|
||||
std::pair<gamemap::location,unit> operator*() const;
|
||||
std::pair<gamemap::location,unit>& operator*() const;
|
||||
|
||||
iterator operator++()
|
||||
{ return iterator(++i_); }
|
||||
|
@ -147,6 +147,10 @@ public:
|
|||
void erase(iterator pos);
|
||||
size_t erase(const gamemap::location &loc);
|
||||
|
||||
void swap(unit_map& o) {
|
||||
map_.swap(o.map_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void delete_all();
|
||||
|
|
538
src/variant.cpp
Normal file
538
src/variant.cpp
Normal file
|
@ -0,0 +1,538 @@
|
|||
#include <cmath>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "foreach.hpp"
|
||||
#include "formatter.hpp"
|
||||
#include "formula.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
#include "variant.hpp"
|
||||
|
||||
namespace {
|
||||
std::string variant_type_to_string(variant::TYPE type) {
|
||||
switch(type) {
|
||||
case variant::TYPE_NULL: return "null";
|
||||
case variant::TYPE_INT: return "int";
|
||||
case variant::TYPE_CALLABLE: return "object";
|
||||
case variant::TYPE_LIST: return "list";
|
||||
case variant::TYPE_STRING: return "string";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const char*> call_stack;
|
||||
}
|
||||
|
||||
void push_call_stack(const char* str)
|
||||
{
|
||||
call_stack.push_back(str);
|
||||
}
|
||||
|
||||
void pop_call_stack()
|
||||
{
|
||||
call_stack.pop_back();
|
||||
}
|
||||
|
||||
std::string get_call_stack()
|
||||
{
|
||||
std::string res;
|
||||
for(std::vector<const char*>::const_iterator i = call_stack.begin();
|
||||
i != call_stack.end(); ++i) {
|
||||
if(!*i) {
|
||||
continue;
|
||||
}
|
||||
res += " ";
|
||||
res += *i;
|
||||
res += "\n";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
type_error::type_error(const std::string& str) : message(str) {
|
||||
std::cerr << "ERROR: " << message << "\n" << get_call_stack();
|
||||
}
|
||||
|
||||
struct variant_list {
|
||||
variant_list() : refcount(0)
|
||||
{}
|
||||
std::vector<variant> elements;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
struct variant_string {
|
||||
variant_string() : refcount(0)
|
||||
{}
|
||||
std::string str;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
void variant::increment_refcount()
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_LIST:
|
||||
++list_->refcount;
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
++string_->refcount;
|
||||
break;
|
||||
case TYPE_CALLABLE:
|
||||
intrusive_ptr_add_ref(callable_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void variant::release()
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_LIST:
|
||||
if(--list_->refcount == 0) {
|
||||
delete list_;
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
if(--string_->refcount == 0) {
|
||||
delete string_;
|
||||
}
|
||||
break;
|
||||
case TYPE_CALLABLE:
|
||||
intrusive_ptr_release(callable_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
variant::variant() : type_(TYPE_NULL), int_value_(0)
|
||||
{}
|
||||
|
||||
variant::variant(int n) : type_(TYPE_INT), int_value_(n)
|
||||
{}
|
||||
|
||||
variant::variant(const game_logic::formula_callable* callable)
|
||||
: type_(TYPE_CALLABLE), callable_(callable)
|
||||
{
|
||||
assert(callable_);
|
||||
increment_refcount();
|
||||
}
|
||||
|
||||
variant::variant(std::vector<variant>* array)
|
||||
: type_(TYPE_LIST)
|
||||
{
|
||||
assert(array);
|
||||
list_ = new variant_list;
|
||||
list_->elements.swap(*array);
|
||||
increment_refcount();
|
||||
}
|
||||
|
||||
variant::variant(const std::string& str)
|
||||
: type_(TYPE_STRING)
|
||||
{
|
||||
string_ = new variant_string;
|
||||
string_->str = str;
|
||||
increment_refcount();
|
||||
}
|
||||
|
||||
variant::variant(const variant& v)
|
||||
{
|
||||
memcpy(this, &v, sizeof(v));
|
||||
increment_refcount();
|
||||
}
|
||||
|
||||
variant::~variant()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
const variant& variant::operator=(const variant& v)
|
||||
{
|
||||
if(&v != this) {
|
||||
release();
|
||||
memcpy(this, &v, sizeof(v));
|
||||
increment_refcount();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const variant& variant::operator[](size_t n) const
|
||||
{
|
||||
if(type_ == TYPE_CALLABLE) {
|
||||
assert(n == 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
must_be(TYPE_LIST);
|
||||
assert(list_);
|
||||
if(n >= list_->elements.size()) {
|
||||
throw type_error("invalid index");
|
||||
}
|
||||
|
||||
return list_->elements[n];
|
||||
}
|
||||
|
||||
size_t variant::num_elements() const
|
||||
{
|
||||
if(type_ == TYPE_CALLABLE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
must_be(TYPE_LIST);
|
||||
assert(list_);
|
||||
return list_->elements.size();
|
||||
}
|
||||
|
||||
bool variant::as_bool() const
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_NULL:
|
||||
return false;
|
||||
case TYPE_INT:
|
||||
return int_value_ != 0;
|
||||
case TYPE_CALLABLE:
|
||||
return callable_ != NULL;
|
||||
case TYPE_LIST:
|
||||
return !list_->elements.empty();
|
||||
case TYPE_STRING:
|
||||
return !string_->str.empty();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& variant::as_string() const
|
||||
{
|
||||
must_be(TYPE_STRING);
|
||||
assert(string_);
|
||||
return string_->str;
|
||||
}
|
||||
|
||||
variant variant::operator+(const variant& v) const
|
||||
{
|
||||
if(type_ == TYPE_LIST) {
|
||||
if(v.type_ == TYPE_LIST) {
|
||||
std::vector<variant> res;
|
||||
res.reserve(list_->elements.size() + v.list_->elements.size());
|
||||
foreach(const variant& var, list_->elements) {
|
||||
res.push_back(var);
|
||||
}
|
||||
|
||||
foreach(const variant& var, v.list_->elements) {
|
||||
res.push_back(var);
|
||||
}
|
||||
|
||||
return variant(&res);
|
||||
}
|
||||
}
|
||||
|
||||
return variant(as_int() + v.as_int());
|
||||
}
|
||||
|
||||
variant variant::operator-(const variant& v) const
|
||||
{
|
||||
return variant(as_int() - v.as_int());
|
||||
}
|
||||
|
||||
variant variant::operator*(const variant& v) const
|
||||
{
|
||||
return variant(as_int() * v.as_int());
|
||||
}
|
||||
|
||||
variant variant::operator/(const variant& v) const
|
||||
{
|
||||
const int numerator = as_int();
|
||||
const int denominator = v.as_int();
|
||||
if(denominator == 0) {
|
||||
throw type_error(formatter() << "divide by zero error");
|
||||
}
|
||||
|
||||
return variant(numerator/denominator);
|
||||
}
|
||||
|
||||
variant variant::operator%(const variant& v) const
|
||||
{
|
||||
const int numerator = as_int();
|
||||
const int denominator = v.as_int();
|
||||
if(denominator == 0) {
|
||||
throw type_error(formatter() << "divide by zero error");
|
||||
}
|
||||
|
||||
return variant(numerator%denominator);
|
||||
}
|
||||
|
||||
variant variant::operator^(const variant& v) const
|
||||
{
|
||||
return variant(static_cast<int>(pow(as_int(), v.as_int())));
|
||||
}
|
||||
|
||||
variant variant::operator-() const
|
||||
{
|
||||
return variant(-as_int());
|
||||
}
|
||||
|
||||
bool variant::operator==(const variant& v) const
|
||||
{
|
||||
if(type_ != v.type_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(type_) {
|
||||
case TYPE_NULL: {
|
||||
return v.is_null();
|
||||
}
|
||||
|
||||
case TYPE_STRING: {
|
||||
return string_->str == v.string_->str;
|
||||
}
|
||||
|
||||
case TYPE_INT: {
|
||||
return int_value_ == v.int_value_;
|
||||
}
|
||||
|
||||
case TYPE_LIST: {
|
||||
if(num_elements() != v.num_elements()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int n = 0; n != num_elements(); ++n) {
|
||||
if((*this)[n] != v[n]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case TYPE_CALLABLE: {
|
||||
return callable_->equals(v.callable_);
|
||||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool variant::operator!=(const variant& v) const
|
||||
{
|
||||
return !operator==(v);
|
||||
}
|
||||
|
||||
bool variant::operator<=(const variant& v) const
|
||||
{
|
||||
if(type_ != v.type_) {
|
||||
if(type_ == TYPE_NULL) {
|
||||
return variant(0) <= v;
|
||||
}
|
||||
|
||||
if(v.type_ == TYPE_NULL) {
|
||||
return *this < variant(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(type_) {
|
||||
case TYPE_NULL: {
|
||||
return true;
|
||||
}
|
||||
|
||||
case TYPE_STRING: {
|
||||
return string_->str <= v.string_->str;
|
||||
}
|
||||
|
||||
case TYPE_INT: {
|
||||
return int_value_ <= v.int_value_;
|
||||
}
|
||||
|
||||
case TYPE_LIST: {
|
||||
if(num_elements() != v.num_elements()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int n = 0; n != num_elements() && n != v.num_elements(); ++n) {
|
||||
if((*this)[n] < v[n]) {
|
||||
return true;
|
||||
} else if((*this)[n] > v[n]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return num_elements() <= v.num_elements();
|
||||
}
|
||||
|
||||
case TYPE_CALLABLE: {
|
||||
return !v.callable_->less(callable_);
|
||||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool variant::operator>=(const variant& v) const
|
||||
{
|
||||
return v <= *this;
|
||||
}
|
||||
|
||||
bool variant::operator<(const variant& v) const
|
||||
{
|
||||
return !(*this >= v);
|
||||
}
|
||||
|
||||
bool variant::operator>(const variant& v) const
|
||||
{
|
||||
return !(*this <= v);
|
||||
}
|
||||
|
||||
void variant::must_be(variant::TYPE t) const
|
||||
{
|
||||
if(type_ != t) {
|
||||
throw type_error(formatter() << "type error: " << " expected " << variant_type_to_string(t) << " but found " << variant_type_to_string(type_) << " (" << to_debug_string() << ")");
|
||||
}
|
||||
}
|
||||
|
||||
void variant::serialize_to_string(std::string& str) const
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_NULL:
|
||||
str += "null()";
|
||||
case TYPE_INT:
|
||||
str += boost::lexical_cast<std::string>(int_value_);
|
||||
break;
|
||||
case TYPE_CALLABLE:
|
||||
std::cerr << "ERROR: attempt to serialize callable variant\n";
|
||||
break;
|
||||
case TYPE_LIST: {
|
||||
str += "[";
|
||||
bool first_time = true;
|
||||
foreach(const variant& var, list_->elements) {
|
||||
if(!first_time) {
|
||||
str += ",";
|
||||
}
|
||||
first_time = false;
|
||||
var.serialize_to_string(str);
|
||||
}
|
||||
str += "]";
|
||||
break;
|
||||
}
|
||||
case TYPE_STRING:
|
||||
str += "'";
|
||||
str += string_->str;
|
||||
str += "'";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void variant::serialize_from_string(const std::string& str)
|
||||
{
|
||||
*this = game_logic::formula(str).execute();
|
||||
}
|
||||
|
||||
int variant::refcount() const
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_LIST:
|
||||
return list_->refcount;
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
return string_->refcount;
|
||||
break;
|
||||
case TYPE_CALLABLE:
|
||||
return callable_->refcount();
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string variant::string_cast() const
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_NULL:
|
||||
return "0";
|
||||
case TYPE_INT:
|
||||
return boost::lexical_cast<std::string>(int_value_);
|
||||
case TYPE_CALLABLE:
|
||||
return "(object)";
|
||||
case TYPE_LIST: {
|
||||
std::string res = "";
|
||||
foreach(const variant& var, list_->elements) {
|
||||
if(!res.empty()) {
|
||||
res += ", ";
|
||||
}
|
||||
|
||||
res += var.string_cast();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
case TYPE_STRING:
|
||||
return string_->str;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string variant::to_debug_string(std::vector<const game_logic::formula_callable*>* seen) const
|
||||
{
|
||||
std::vector<const game_logic::formula_callable*> seen_stack;
|
||||
if(!seen) {
|
||||
seen = &seen_stack;
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
switch(type_) {
|
||||
case TYPE_NULL:
|
||||
s << "(null)";
|
||||
case TYPE_INT:
|
||||
s << int_value_;
|
||||
break;
|
||||
case TYPE_LIST: {
|
||||
s << "[";
|
||||
for(int n = 0; n != num_elements(); ++n) {
|
||||
if(n != 0) {
|
||||
s << ", ";
|
||||
}
|
||||
|
||||
s << operator[](n).to_debug_string(seen);
|
||||
}
|
||||
s << "]";
|
||||
break;
|
||||
}
|
||||
case TYPE_CALLABLE: {
|
||||
s << "{";
|
||||
if(std::find(seen->begin(), seen->end(), callable_) == seen->end()) {
|
||||
seen->push_back(callable_);
|
||||
std::vector<game_logic::formula_input> v = callable_->inputs();
|
||||
bool first = true;
|
||||
foreach(const game_logic::formula_input& input, v) {
|
||||
if(!first) {
|
||||
s << ", ";
|
||||
}
|
||||
first = false;
|
||||
s << input.name << " ";
|
||||
if(input.access == game_logic::FORMULA_READ_WRITE) {
|
||||
s << "(read-write) ";
|
||||
} else if(input.access == game_logic::FORMULA_WRITE_ONLY) {
|
||||
s << "(writeonly) ";
|
||||
}
|
||||
|
||||
s << "-> " << callable_->query_value(input.name).to_debug_string(seen);
|
||||
}
|
||||
} else {
|
||||
s << "...";
|
||||
}
|
||||
s << "}";
|
||||
break;
|
||||
}
|
||||
case TYPE_STRING: {
|
||||
s << "'" << string_->str << "'";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
124
src/variant.hpp
Normal file
124
src/variant.hpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#ifndef VARIANT_HPP_INCLUDED
|
||||
#define VARIANT_HPP_INCLUDED
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace game_logic {
|
||||
class formula_callable;
|
||||
}
|
||||
|
||||
void push_call_stack(const char* str);
|
||||
void pop_call_stack();
|
||||
std::string get_call_stack();
|
||||
|
||||
struct call_stack_manager {
|
||||
explicit call_stack_manager(const char* str) {
|
||||
push_call_stack(str);
|
||||
}
|
||||
|
||||
~call_stack_manager() {
|
||||
pop_call_stack();
|
||||
}
|
||||
};
|
||||
|
||||
struct variant_list;
|
||||
struct variant_string;
|
||||
|
||||
struct type_error {
|
||||
explicit type_error(const std::string& str);
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class variant {
|
||||
public:
|
||||
variant();
|
||||
explicit variant(int n);
|
||||
explicit variant(const game_logic::formula_callable* callable);
|
||||
explicit variant(std::vector<variant>* array);
|
||||
explicit variant(const std::string& str);
|
||||
~variant();
|
||||
|
||||
variant(const variant& v);
|
||||
const variant& operator=(const variant& v);
|
||||
|
||||
const variant& operator[](size_t n) const;
|
||||
size_t num_elements() const;
|
||||
|
||||
bool is_string() const { return type_ == TYPE_STRING; }
|
||||
bool is_null() const { return type_ == TYPE_NULL; }
|
||||
bool is_int() const { return type_ == TYPE_INT; }
|
||||
int as_int() const { if(type_ == TYPE_NULL) { return 0; } must_be(TYPE_INT); return int_value_; }
|
||||
bool as_bool() const;
|
||||
|
||||
bool is_list() const { return type_ == TYPE_LIST; }
|
||||
|
||||
const std::string& as_string() const;
|
||||
|
||||
bool is_callable() const { return type_ == TYPE_CALLABLE; }
|
||||
const game_logic::formula_callable* as_callable() const {
|
||||
must_be(TYPE_CALLABLE); return callable_; }
|
||||
game_logic::formula_callable* mutable_callable() const {
|
||||
must_be(TYPE_CALLABLE); return mutable_callable_; }
|
||||
|
||||
template<typename T>
|
||||
T* try_convert() const {
|
||||
if(!is_callable()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dynamic_cast<T*>(mutable_callable());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* convert_to() const {
|
||||
must_be(TYPE_CALLABLE);
|
||||
T* res = dynamic_cast<T*>(mutable_callable());
|
||||
if(!res) {
|
||||
throw type_error("could not convert type");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
variant operator+(const variant&) const;
|
||||
variant operator-(const variant&) const;
|
||||
variant operator*(const variant&) const;
|
||||
variant operator/(const variant&) const;
|
||||
variant operator^(const variant&) const;
|
||||
variant operator%(const variant&) const;
|
||||
variant operator-() const;
|
||||
|
||||
bool operator==(const variant&) const;
|
||||
bool operator!=(const variant&) const;
|
||||
bool operator<(const variant&) const;
|
||||
bool operator>(const variant&) const;
|
||||
bool operator<=(const variant&) const;
|
||||
bool operator>=(const variant&) const;
|
||||
|
||||
void serialize_to_string(std::string& str) const;
|
||||
void serialize_from_string(const std::string& str);
|
||||
|
||||
int refcount() const;
|
||||
|
||||
std::string string_cast() const;
|
||||
|
||||
std::string to_debug_string(std::vector<const game_logic::formula_callable*>* seen=NULL) const;
|
||||
enum TYPE { TYPE_NULL, TYPE_INT, TYPE_CALLABLE, TYPE_LIST, TYPE_STRING };
|
||||
private:
|
||||
void must_be(TYPE t) const;
|
||||
TYPE type_;
|
||||
union {
|
||||
int int_value_;
|
||||
const game_logic::formula_callable* callable_;
|
||||
game_logic::formula_callable* mutable_callable_;
|
||||
variant_list* list_;
|
||||
variant_string* string_;
|
||||
};
|
||||
|
||||
void increment_refcount();
|
||||
void release();
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue