initial branch changes

This commit is contained in:
David White 2008-01-28 03:04:25 +00:00
parent 4a990a75c6
commit 5c0df60c23
36 changed files with 3911 additions and 11 deletions

View file

@ -37,6 +37,10 @@
command=command
key=:
[/hotkey]
[hotkey]
command=aiformula
key=f
[/hotkey]
[hotkey]
command=continue
key=t

View file

@ -56,9 +56,18 @@ wesnoth_source = \
ai_village.cpp \
animated_game.cpp \
attack_prediction.cpp \
callable_objects.cpp \
config_adapter.cpp \
dialogs.cpp \
floating_textbox.cpp \
<<<<<<< .mine
formula.cpp \
formula_ai.cpp \
formula_function.cpp \
formula_tokenizer.cpp \
game.cpp \
=======
>>>>>>> .r23279
game_display.cpp \
game_events.cpp \
game_preferences.cpp \
@ -105,8 +114,9 @@ wesnoth_source = \
unit_types.cpp \
upload_log.cpp \
variable.cpp \
variant.cpp \
widgets/combo.cpp \
widgets/scrollpane.cpp
widgets/scrollpane.cpp
#############################################################################
# Wesnoth #

View file

@ -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;

View file

@ -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);

View file

@ -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")
@ -2177,3 +2183,139 @@ int ai::attack_depth()
attack_depth_ = maximum<int>(1,lexical_cast_default<int>(parms["attack_depth"],5));
return attack_depth_;
}
void ai_interface::get_inputs(std::vector<game_logic::formula_input>* inputs) const
{
}
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)));
std::cerr << "ADD UNIT: " << (int)&i->second << ": '" << i->second.id() << "'\n";
}
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(), &current_team().villages());
} else if(key == "map") {
return variant(new gamemap_callable(info_.map));
}
return variant();
}
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));
} 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));
}

View file

@ -22,6 +22,7 @@
#include "actions.hpp"
#include "ai_interface.hpp"
#include "formula_callable.hpp"
class ai : public ai_interface {
public:
@ -125,7 +126,7 @@ protected:
const location& to, const location& via,
const std::map<location,paths>& possible_moves) const;
struct attack_analysis
struct attack_analysis : public game_logic::formula_callable
{
void analyze(const gamemap& map, unit_map& units,
const std::vector<team>& teams,
@ -135,6 +136,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;

View file

@ -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
@ -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_;

128
src/callable_objects.cpp Normal file
View file

@ -0,0 +1,128 @@
#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) {
map_formula_callable* item = new map_formula_callable;
item->add("src", variant(new location_callable(i->first)));
item->add("dst", variant(new location_callable(i->second)));
vars.push_back(variant(item));
}
return variant(&vars);
} 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
{
std::cerr << "get_value: '" << key << "': " << (int)&u_ << " -> '" << u_.id() << "'\n";
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 {
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));
}

85
src/callable_objects.hpp Normal file
View file

@ -0,0 +1,85 @@
#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_; } \
\
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_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)
{}
};
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)
{}
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_;
team team_;
int side_;
};
#endif

View file

@ -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
View 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
View 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

677
src/formula.cpp Normal file
View file

@ -0,0 +1,677 @@
/*
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(0));
}
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_ONLY));
}
}
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 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()) {
const variant index = right_->evaluate(variables);
return left[index.as_int()];
}
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() && right.as_bool() ? variant(1) : variant(0);
case OR: return left.as_bool() || right.as_bool() ? variant(1) : variant(0);
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
View 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

430
src/formula_ai.cpp Normal file
View file

@ -0,0 +1,430 @@
#include "actions.hpp"
#include "callable_objects.hpp"
#include "formula.hpp"
#include "formula_ai.hpp"
#include "formula_callable.hpp"
#include "formula_function.hpp"
namespace {
using namespace game_logic;
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(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_callable : public formula_callable {
gamemap::location src_, dst_;
variant get_value(const std::string& key) const { return variant(); }
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_function : public function_expression {
public:
explicit move_function(const args_list& args)
: function_expression(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 attack_callable : public formula_callable {
gamemap::location move_from_, src_, dst_;
int weapon_;
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 {
return variant();
}
}
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), weapon_(weapon),
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 weapon_; }
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(args, 3, 4),
ai_(ai)
{}
private:
variant execute(const formula_callable& variables) const {
const gamemap::location& move_from = args().front()->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& src = args()[args().size()-3]->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& dst = args()[args().size()-2]->evaluate(variables).convert_to<location_callable>()->loc();
const int weapon = args()[args().size()-1]->evaluate(variables).as_int();
if(ai_.get_info().units.count(move_from) == 0 || ai_.get_info().units.count(dst) == 0) {
std::cerr << "AI ERROR: Formula produced illegal attack!\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(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(args, 3, 3)
{}
private:
variant execute(const formula_callable& variables) const {
const gamemap& m = args()[0]->evaluate(variables).convert_to<gamemap_callable>()->get_gamemap();
return variant(m.is_village(gamemap::location(args()[1]->evaluate(variables).as_int(), args()[2]->evaluate(variables).as_int())));
}
};
class unit_at_function : public function_expression {
public:
unit_at_function(const args_list& args, const formula_ai& ai_object)
: function_expression(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(args, 1, 1), ai_(ai_object)
{}
private:
variant execute(const formula_callable& variables) const {
const gamemap::location& loc = args()[0]->evaluate(variables).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);
std::vector<variant> vars;
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 ai_function_symbol_table : public function_symbol_table {
const formula_ai& ai_;
expression_ptr create_function(const std::string& fn,
const std::vector<expression_ptr>& args) const {
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 {
return function_symbol_table::create_function(fn, args);
}
}
public:
explicit ai_function_symbol_table(const formula_ai& ai) : ai_(ai)
{}
};
}
formula_ai::formula_ai(info& i) : ai(i)
{
}
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)), 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)), args);
}
game_logic::formula f(formula_str, &function_table);
const variant v = f.execute(*this);
return v.to_debug_string();
}
namespace {
void debug_console(const game_logic::formula_callable& info, const formula_ai& ai) {
std::cerr << "starting debug console. Type formula to evaluate. Type 'continue' when you're ready to continue\n";
std::cerr << variant(&info).to_debug_string() << "\n";
ai_function_symbol_table function_table(ai);
const config& ai_param = ai.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)), args);
}
for(;;) {
std::cerr << "\n>>> ";
char buf[1024];
std::cin.getline(buf, sizeof(buf));
std::string cmd(buf);
if(cmd == "continue") {
break;
}
try {
formula f(cmd, &function_table);
const variant v = f.execute(info);
std::cerr << v.to_debug_string() << "\n";
} catch(formula_error& e) {
std::cerr << "ERROR IN FORMULA\n";
}
}
}
}
void formula_ai::prepare_move()
{
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, full_srcdst_, full_dstsrc_, true);
attacks_cache_ = variant();
}
bool formula_ai::make_move()
{
if(!move_formula_) {
return false;
}
prepare_move();
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) {
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>();
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;
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_);
}
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(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 {
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") {
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") {
return variant(new move_map_callable(srcdst_, dstsrc_));
} else if(key == "enemy_moves") {
return variant(new move_map_callable(enemy_srcdst_, enemy_dstsrc_));
}
return ai_interface::get_value(key);
}

34
src/formula_ai.hpp Normal file
View file

@ -0,0 +1,34 @@
#ifndef FORMULA_AI_HPP_INCLUDED
#define FORMULA_AI_HPP_INCLUDED
#include "ai.hpp"
#include "ai_interface.hpp"
#include "formula_fwd.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 { return srcdst_; }
std::string evaluate(const std::string& formula_str);
void prepare_move();
private:
void do_recruitment();
bool make_move();
virtual variant get_value(const std::string& key) const;
game_logic::const_formula_ptr recruit_formula_;
game_logic::const_formula_ptr move_formula_;
std::map<location,paths> possible_moves_;
move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_;
mutable variant attacks_cache_;
};
#endif

86
src/formula_callable.hpp Normal file
View file

@ -0,0 +1,86 @@
#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:
variant query_value(const std::string& key) const {
if(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 (int)this - (int)callable;
}
private:
virtual variant get_value(const std::string& key) const = 0;
};
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 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);
private:
variant get_value(const std::string& key) const;
void get_inputs(std::vector<formula_input>* inputs) const;
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

View 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

486
src/formula_function.cpp Normal file
View file

@ -0,0 +1,486 @@
#include <iostream>
#include <math.h>
#include "formula_callable.hpp"
#include "formula_function.hpp"
namespace game_logic {
namespace {
class if_function : public function_expression {
public:
explicit if_function(const args_list& args)
: function_expression(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(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(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(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(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(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(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(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(*items[n].as_callable());
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(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(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(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(*items[n].as_callable());
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(args, 2, 2)
{}
private:
variant execute(const formula_callable& variables) const {
const variant items = args()[0]->evaluate(variables);
for(int n = 0; n != items.num_elements(); ++n) {
const variant val = args()[1]->evaluate(*items[n].as_callable());
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(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(*items[n].as_callable());
vars.push_back(val);
}
return variant(&vars);
}
};
class sum_function : public function_expression {
public:
explicit sum_function(const args_list& args)
: function_expression(args, 1, 1)
{}
private:
variant execute(const formula_callable& variables) const {
variant res(0);
const variant items = args()[0]->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(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(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(args, 0, 0)
{}
private:
variant execute(const formula_callable& variables) const {
return variant();
}
};
}
variant formula_function_expression::execute(const formula_callable& variables) const
{
map_formula_callable callable;
for(int n = 0; n != arg_names_.size(); ++n) {
callable.add(arg_names_[n], args()[n]->evaluate(variables));
}
return formula_->execute(callable);
}
function_expression_ptr formula_function::generate_function_expression(const std::vector<expression_ptr>& args) const
{
return function_expression_ptr(new formula_function_expression(args, formula_, args_));
}
void function_symbol_table::add_formula_function(const std::string& name, const_formula_ptr formula, const std::vector<std::string>& args)
{
custom_formulas_[name] = formula_function(formula, 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 == "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 {
std::cerr << "no function '" << fn << "'\n";
throw formula_error();
}
}
}

89
src/formula_function.hpp Normal file
View file

@ -0,0 +1,89 @@
#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:
virtual ~formula_expression() {}
variant evaluate(const formula_callable& variables) const {
return execute(variables);
}
private:
virtual variant execute(const formula_callable& variables) const = 0;
};
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 args_list& args,
int min_args=-1, int max_args=-1)
: args_(args)
{
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:
args_list args_;
};
class formula_function_expression : public function_expression {
public:
explicit formula_function_expression(const args_list& args, const_formula_ptr formula, const std::vector<std::string>& arg_names)
: function_expression(args, arg_names.size(), arg_names.size()),
formula_(formula), arg_names_(arg_names)
{}
private:
variant execute(const formula_callable& variables) const;
const_formula_ptr formula_;
std::vector<std::string> arg_names_;
};
typedef boost::shared_ptr<function_expression> function_expression_ptr;
class formula_function {
const_formula_ptr formula_;
std::vector<std::string> args_;
public:
formula_function() {}
formula_function(const_formula_ptr formula, const std::vector<std::string>& args) : formula_(formula), 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 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
View 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
View 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
View 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

View file

@ -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 },
@ -662,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;

View file

@ -61,6 +61,7 @@ enum HOTKEY_COMMAND {
//misc.
HOTKEY_USER_CMD,
HOTKEY_AI_FORMULA,
HOTKEY_CLEAR_MSG,
#ifdef USRCMD2
HOTKEY_USER_CMD_2,
@ -210,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
View 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

View file

@ -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"
@ -2317,11 +2319,35 @@ 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);
eval.prepare_move();
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

View file

@ -134,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();
@ -147,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;

View file

@ -527,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
//%%
@ -586,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";
}

View file

@ -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();
}

View file

@ -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();

View file

@ -0,0 +1,36 @@
#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); } }
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

View file

@ -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;
}

View file

@ -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_); }

475
src/variant.cpp Normal file
View file

@ -0,0 +1,475 @@
#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);
}
}
}
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);
}
variant::variant(const std::string& str)
: type_(TYPE_STRING)
{
string_ = new variant_string;
string_->str = str;
}
variant::variant(const variant& v)
{
memcpy(this, &v, sizeof(v));
increment_refcount();
}
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_) {
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();
}
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();
}

107
src/variant.hpp Normal file
View file

@ -0,0 +1,107 @@
#ifndef VARIANT_HPP_INCLUDED
#define VARIANT_HPP_INCLUDED
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
namespace game_logic {
class formula_callable;
}
struct variant_list;
struct variant_string;
struct type_error {
explicit type_error(const std::string& str) : message(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(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);
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