ai_python.cpp

Go to the documentation of this file.
00001 /* $Id: ai_python.cpp 26622 2008-05-14 16:30:16Z elias $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version
00008    or at your option any later version2
00009    or at your option any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 //! @file ai_python.cpp
00017 //! Interface to python for ai-scripts.
00018 //
00019 //! Note this file isn't C-style cast safe:
00020 //! PyObject_NEW this macro contains 2 casts
00021 //! Py_DECREF this macro contains 3 casts
00022 //! Py_InitModule3 this macro contains 1 cast
00023 //! Py_XDECREF this macro contains 3 casts
00024 
00025 /* Note about Python exceptions:
00026 
00027    Originally, runtime exceptions were used for things like using out of bounds
00028    coordinates or moving a unit which already got killed, and were supposed to
00029    be catched by scripts. The subset of the Python language allowed in
00030    scripts now has no exceptions anymore, to keep things simple. Therefore the
00031    API was changed so scripts are not supposed to catch exceptions anymore.
00032 
00033    This means, invalid positions or units will now only be indicated by things
00034    like return values, but are ignored otherwise.
00035 
00036    Most API functions still will cause exceptions (like if the game ends, or
00037    the wrong number of parameters is passed to a function) - but those are not
00038    supposed to be catched by user scripts.
00039 */
00040 
00041 #ifdef HAVE_PYTHON
00042 
00043 #include "global.hpp"
00044 
00045 #include "ai.hpp"
00046 #include "ai_python.hpp"
00047 #include "attack_prediction.hpp"
00048 #include "gamestatus.hpp"
00049 #include "filesystem.hpp"
00050 #include "log.hpp"
00051 #include "menu_events.hpp"
00052 #include "game_events.hpp"
00053 #include "game_config.hpp"
00054 #include "settings.hpp"
00055 
00056 #include <cassert>
00057 #include <fstream>
00058 #include <marshal.h>
00059 
00060 #define LOG_AI LOG_STREAM(info, ai)
00061 
00062 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 4
00063 #define Py_ssize_t int
00064 #endif
00065 
00066 static python_ai* running_instance;
00067 bool python_ai::init_ = false;
00068 
00069 // Appeace gcc 2.4 which doesn't want us pass inline strings to the Python API.
00070 #define CC(x) const_cast<char *>(x)
00071 #define STRINGVALUE CC("s")
00072 #define INTVALUE CC("i")
00073 #define DOUBLEVALUE CC("d")
00074 #define NOVALUE CC("")
00075 #define OBVALUE CC("O!")
00076 
00077 #define GSDEF(x, func, doc) \
00078     {CC(x), reinterpret_cast<getter>(func), NULL,\
00079     CC(doc), NULL },
00080 
00081 #define return_none do {Py_INCREF(Py_None); return Py_None;} while(false)
00082 
00083 #define wrap(code) \
00084     try { code } \
00085     catch(end_level_exception& e) { \
00086         running_instance->exception = e; \
00087         PyErr_SetString(PyExc_RuntimeError, "C++ exception!"); \
00088         return NULL; \
00089     }
00090 
00091 static PyObject* wrap_unittype(const unit_type& type);
00092 static PyObject* wrap_attacktype(const attack_type& type);
00093 static PyObject* wrapper_unittype_resistance_against(wesnoth_unittype* type, PyObject* args);
00094 
00095 static PyObject* wrapper_unittype_get_name(wesnoth_unittype* unit, void* /*closure*/)
00096 {
00097     return Py_BuildValue(STRINGVALUE, unit->unit_type_->id().c_str());
00098 }
00099 
00100 #define ut_get_general( t, n, x ) \
00101 static PyObject* wrapper_unittype_get_##n( wesnoth_unittype* type, void* /*closure*/ ) \
00102 {   \
00103     return Py_BuildValue(t,type->unit_type_->x);    \
00104 }
00105 
00106 #define ut_get( x ) \
00107     ut_get_general(INTVALUE, x, x() )
00108 
00109 #define ut_get_ability( x ) \
00110     ut_get_general(INTVALUE, x, has_ability(#x))
00111 
00112 #define ut_get_ability_by_id( x ) \
00113     ut_get_general(INTVALUE, x, has_ability_by_id(#x))
00114 
00115 ut_get_ability( heals )
00116 ut_get_ability( regenerate )
00117 ut_get_ability( leadership )
00118 ut_get_ability( illuminates )
00119 ut_get_ability( skirmisher )
00120 ut_get_ability( teleport )
00121 ut_get_ability_by_id( curing )
00122 ut_get_ability_by_id( steadfast )
00123 ut_get( not_living )
00124 ut_get( can_advance )
00125 ut_get( has_zoc )
00126 ut_get( level )
00127 ut_get( movement )
00128 ut_get( cost )
00129 ut_get( alignment )
00130 ut_get( hitpoints )
00131 static PyObject* wrapper_unittype_get_usage( wesnoth_unittype* type, void* /*closure*/ )
00132 {
00133     return Py_BuildValue(STRINGVALUE, type->unit_type_->usage().c_str());
00134 }
00135 
00136 #define ut_gs( x, doc ) \
00137     {CC(#x), reinterpret_cast<getter>(wrapper_unittype_get_##x), NULL, CC(doc), NULL},
00138 
00139 static PyGetSetDef unittype_getseters[] = {
00140     ut_gs(name, "Name of the unit type." )
00141     ut_gs(heals, "If type can heal others (either healing or curing ability)." )
00142     ut_gs(curing, "If type has curing ability (remove poison from others)." )
00143     ut_gs(regenerate, "If type has regenerate ability." )
00144     ut_gs(leadership, "If type has leadership ability." )
00145     ut_gs(illuminates, "If type has illuminates ability." )
00146     ut_gs(skirmisher, "If type has skirmisher ability." )
00147     ut_gs(teleport, "If type has teleport ability." )
00148     ut_gs(steadfast, "If type has steadfast ability." )
00149     ut_gs(not_living, "If type has not-living ability." )
00150     ut_gs(can_advance, "If type can advance." )
00151     ut_gs(has_zoc, "If type has a ZOC." )
00152     ut_gs(level, "Level of the type." )
00153     ut_gs(hitpoints, "Hitpoints of the type." )
00154     ut_gs(usage, "AI's usage hint of the type, one of: 'archer', 'fighter', "
00155         "'healer', 'mixed fighter', 'scout'." )
00156     ut_gs(movement, "Movement points of the type." )
00157     ut_gs(cost, "Cost of the type." )
00158     ut_gs(alignment, "Alignment of the type: 0=lawful, 1=neutral, 2=chaotic." )
00159     { NULL, NULL, NULL, NULL, NULL }
00160 };
00161 
00162 PyObject* python_ai::unittype_advances_to( wesnoth_unittype* type, PyObject* args )
00163 {
00164     if (!PyArg_ParseTuple(args, NOVALUE))
00165         return NULL;
00166 
00167     PyObject* list = PyList_New(type->unit_type_->advances_to().size());
00168     int r;
00169     for (size_t advance = 0; advance < type->unit_type_->advances_to().size(); advance++)
00170     {
00171         std::map<std::string,unit_type>::const_iterator t =
00172             unit_type_data::types().find(type->unit_type_->advances_to()[advance]);
00173 
00174         assert(t != unit_type_data::types().end());
00175         r = PyList_SetItem(list,advance,wrap_unittype(t->second));
00176     }
00177     return list;
00178 }
00179 
00180 static PyObject* wrapper_unittype_attacks( wesnoth_unittype* type, PyObject* args )
00181 {
00182     if ( !PyArg_ParseTuple( args, NOVALUE ) )
00183         return NULL;
00184 
00185     const std::vector<attack_type>& attacks = type->unit_type_->attacks();
00186     PyObject* list = PyList_New(attacks.size());
00187     for ( size_t attack = 0; attack < attacks.size(); attack++)
00188         PyList_SetItem(list,attack,wrap_attacktype(attacks[attack]));
00189     return list;
00190 }
00191 
00192 #define MDEF(name, func, docs) { CC(name), reinterpret_cast<PyCFunction>(func),\
00193     METH_VARARGS, CC(docs) },
00194 static PyMethodDef unittype_methods[] = {
00195     MDEF("advances_to",
00196         python_ai::unittype_advances_to,
00197         "Returns: unittype[]\n"
00198         "Returns a list of wesnoth.unittype of possible advancements.")
00199     MDEF("attacks",
00200         wrapper_unittype_attacks,
00201         "Returns: attacktype[]\n"
00202         "Returns list of possible attack types.\n")
00203     MDEF( "movement_cost",
00204         python_ai::wrapper_unittype_movement_cost,
00205         "Parameters: location\n"
00206         "Returns: cost\n"
00207         "Returns the cost of moving over the given location.")
00208     MDEF( "defense_modifier",
00209         python_ai::wrapper_unittype_defense_modifier,
00210         "Parameters: location\n"
00211         "Returns: percent\n"
00212         "Returns the defense modifier in % (probability the unit will be hit)"
00213         " on the given location.")
00214     MDEF("damage_from",
00215         wrapper_unittype_resistance_against,
00216         "Parameters: attacktype\n"
00217         "Returns: percent\n"
00218         "Returns the damage in percent a unit of this type receives when "
00219         "attacked with the given attack type. (0 means no damage at all, 100 "
00220         "means full damage, 200 means double damage.)")
00221     { NULL, NULL, 0, NULL }
00222 };
00223 
00224 static int unittype_internal_compare(wesnoth_unittype* left,
00225     wesnoth_unittype* right)
00226 {
00227     return reinterpret_cast<long>(left->unit_type_) -
00228         reinterpret_cast<long>(right->unit_type_);
00229 }
00230 
00231 static PyTypeObject wesnoth_unittype_type = {
00232     PyObject_HEAD_INIT(NULL)
00233     0,                                      /* ob_size*/
00234     CC("wesnoth.unittype"),                 /* tp_name*/
00235     sizeof(wesnoth_unittype),               /* tp_basicsize*/
00236     0,                                      /* tp_itemsize*/
00237     0,                                      /* tp_dealloc*/
00238     0,                                      /* tp_print*/
00239     0,                                      /* tp_getattr*/
00240     0,                                      /* tp_setattr*/
00241     reinterpret_cast<cmpfunc>
00242         (unittype_internal_compare),        /* tp_compare*/
00243     0,                                      /* tp_repr*/
00244     0,                                      /* tp_as_number*/
00245     0,                                      /* tp_as_sequence*/
00246     0,                                      /* tp_as_mapping*/
00247     0,                                      /* tp_hash */
00248     0,                                      /* tp_call*/
00249     0,                                      /* tp_str*/
00250     0,                                      /* tp_getattro*/
00251     0,                                      /* tp_setattro*/
00252     0,                                      /* tp_as_buffer*/
00253     Py_TPFLAGS_DEFAULT,                     /* tp_flags*/
00254     CC("Describes a unit type."),           /* tp_doc */
00255     0,                                      /* tp_traverse */
00256     0,                                      /* tp_clear */
00257     0,                                      /* tp_richcompare */
00258     0,                                      /* tp_weaklistoffset */
00259     0,                                      /* tp_iter */
00260     0,                                      /* tp_iternext */
00261     unittype_methods,                       /* tp_methods */
00262     0,                                      /* tp_members */
00263     unittype_getseters,                     /* tp_getset */
00264     NULL,
00265     NULL,
00266     NULL,
00267     NULL,
00268     0,
00269     NULL,
00270     NULL,
00271     NULL,
00272     NULL,
00273     NULL,
00274     NULL,
00275     NULL,
00276     NULL,
00277     NULL,
00278     NULL,
00279     NULL
00280 };
00281 
00282 static PyObject* wrap_unittype(const unit_type& type)
00283 {
00284     wesnoth_unittype* wrap = reinterpret_cast<wesnoth_unittype*>(PyObject_NEW(wesnoth_unittype, &wesnoth_unittype_type));
00285     if (wrap)
00286         wrap->unit_type_ = &type;
00287     return reinterpret_cast<PyObject*>(wrap);
00288 }
00289 
00290 typedef struct {
00291     PyObject_HEAD
00292     const attack_type* attack_type_;
00293 } wesnoth_attacktype;
00294 
00295 #define at_get_general( t, n, x ) \
00296 static PyObject* attacktype_get_##n(wesnoth_attacktype* type, void* /*closure*/) \
00297 { \
00298     return Py_BuildValue(t,type->attack_type_->x); \
00299 }
00300 
00301 #define at_get( t, x ) \
00302     at_get_general( t, x, x() )
00303 
00304 #define at_get_ability_by_id(x) \
00305     at_get_general(INTVALUE, x, has_special_by_id(#x))
00306 
00307 #define at_get_string_prop( x ) \
00308     at_get_general(STRINGVALUE, x, x().c_str() )
00309 
00310 at_get(INTVALUE, damage )
00311 at_get(INTVALUE, num_attacks )
00312 at_get(DOUBLEVALUE, attack_weight )
00313 at_get(DOUBLEVALUE, defense_weight )
00314 
00315 at_get_ability_by_id(backstab )
00316 at_get_ability_by_id(slow )
00317 at_get_ability_by_id(berserk )
00318 at_get_ability_by_id(stones )
00319 at_get_ability_by_id(plague )
00320 at_get_ability_by_id(marksman )
00321 at_get_ability_by_id(magical )
00322 at_get_ability_by_id(charge )
00323 at_get_ability_by_id(drains )
00324 at_get_ability_by_id(poison )
00325 
00326 at_get_string_prop( name )
00327 at_get_string_prop( range )
00328 
00329 static void wesnoth_attacktype_dealloc(wesnoth_attacktype* self)
00330 {
00331     delete self->attack_type_;
00332     self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
00333 }
00334 
00335 #define at_gs( x, doc ) \
00336     {CC(#x), reinterpret_cast<getter>(attacktype_get_##x), NULL, CC(doc), NULL},
00337 
00338 static PyGetSetDef attacktype_getseters[] = {
00339     at_gs( name,            "Name of the attack." )
00340     at_gs( damage,          "Attack damage." )
00341     at_gs( num_attacks,     "Number of hits." )
00342     at_gs( attack_weight,   "AI setting, floating point number." )
00343     at_gs( defense_weight,  "AI setting, floating point number." )
00344     at_gs( backstab,        "This attack has backstab." )
00345     at_gs( slow,            "This attack causes slow." )
00346     at_gs( berserk,         "This attack uses berserk." )
00347     at_gs( stones,          "This attack has 'stones' special." )
00348     at_gs( plague,          "This attack has 'plague' special." )
00349     at_gs( marksman,        "This attack has 'marksman' special." )
00350     at_gs( magical,         "This attack is magical." )
00351     at_gs( charge,          "This attack has the 'charge' special." )
00352     at_gs( drains,          "This attack has the 'drains' special." )
00353     at_gs( poison,          "This attack has the 'poison' special." )
00354     at_gs( range,           "String with the name of the attack range." )
00355     { NULL, NULL, NULL, NULL, NULL }
00356 };
00357 
00358 static PyMethodDef attacktype_methods[] = {
00359     { NULL, NULL, 0, NULL }
00360 };
00361 
00362 static PyTypeObject wesnoth_attacktype_type = {
00363     PyObject_HEAD_INIT(NULL)
00364     0,                                      /* ob_size*/
00365     CC("wesnoth.attacktype"),               /* tp_name*/
00366     sizeof(wesnoth_attacktype),             /* tp_basicsize*/
00367     0,                                      /* tp_itemsize*/
00368     reinterpret_cast<destructor>
00369         (wesnoth_attacktype_dealloc),       /* tp_dealloc*/
00370     0,                                      /* tp_print*/
00371     0,                                      /* tp_getattr*/
00372     0,                                      /* tp_setattr*/
00373     0,                                      /* tp_compare*/
00374     0,                                      /* tp_repr*/
00375     0, //UniConvert,                        /* tp_as_number*/
00376     0,                                      /* tp_as_sequence*/
00377     0,                                      /* tp_as_mapping*/
00378     0,                                      /* tp_hash */
00379     0,                                      /* tp_call*/
00380     0,                                      /* tp_str*/
00381     0,                                      /* tp_getattro*/
00382     0,                                      /* tp_setattro*/
00383     0,                                      /* tp_as_buffer*/
00384     Py_TPFLAGS_DEFAULT,                     /* tp_flags*/
00385     CC("Describes an attack type."),        /* tp_doc */
00386     0,                                      /* tp_traverse */
00387     0,                                      /* tp_clear */
00388     0,                                      /* tp_richcompare */
00389     0,                                      /* tp_weaklistoffset */
00390     0,                                      /* tp_iter */
00391     0,                                      /* tp_iternext */
00392     attacktype_methods,                     /* tp_methods */
00393     0,                                      /* tp_members */
00394     attacktype_getseters,                   /* tp_getset */
00395     NULL,
00396     NULL,
00397     NULL,
00398     NULL,
00399     0,
00400     NULL,
00401     NULL,
00402     NULL,
00403     NULL,
00404     NULL,
00405     NULL,
00406     NULL,
00407     NULL,
00408     NULL,
00409     NULL,
00410     NULL
00411 };
00412 
00413 static PyObject* wrap_attacktype(const attack_type& type)
00414 {
00415     wesnoth_attacktype* attack;
00416     attack = static_cast<wesnoth_attacktype*>(PyObject_NEW(wesnoth_attacktype, &wesnoth_attacktype_type));
00417 
00418     attack->attack_type_ = new attack_type(type);
00419     return reinterpret_cast<PyObject*>(attack);
00420 }
00421 
00422 #define u_check if (!running_instance->is_unit_valid(unit->unit_)) return_none
00423 
00424 bool python_ai::is_unit_valid(const unit* unit)
00425 {
00426     if (!unit)
00427     {
00428         return false;
00429     }
00430     for(unit_map::const_iterator i = running_instance->get_info().units.begin(); i != running_instance->get_info().units.end(); ++i) {
00431         if (unit == &i->second)
00432             return true;
00433     }
00434     return false;
00435 }
00436 
00437 static PyObject* unit_get_name(wesnoth_unit* unit, void* /*closure*/)
00438 {
00439     u_check;
00440     return Py_BuildValue(STRINGVALUE, unit->unit_->underlying_id().c_str());
00441 }
00442 
00443 static PyObject* unit_is_enemy(wesnoth_unit* unit, void* /*closure*/)
00444 {
00445     u_check;
00446     return Py_BuildValue(INTVALUE,
00447         running_instance->current_team().is_enemy(
00448         unit->unit_->side()) == true ? 1 : 0);
00449 }
00450 
00451 static PyObject* unit_can_recruit(wesnoth_unit* unit, void* /*closure*/)
00452 {
00453     u_check;
00454     return Py_BuildValue(INTVALUE, unit->unit_->can_recruit() == true ? 1 : 0);
00455 }
00456 
00457 static PyObject* unit_side(wesnoth_unit* unit, void* /*closure*/)
00458 {
00459     u_check;
00460     return Py_BuildValue(INTVALUE, unit->unit_->side());
00461 }
00462 
00463 static PyObject* unit_movement_left(wesnoth_unit* unit, void* /*closure*/)
00464 {
00465     u_check;
00466     return Py_BuildValue(INTVALUE, unit->unit_->movement_left());
00467 }
00468 
00469 static PyObject* unit_max_movement(wesnoth_unit* unit, void* /*closure*/)
00470 {
00471     u_check;
00472     return Py_BuildValue(INTVALUE, unit->unit_->total_movement());
00473 }
00474 
00475 static PyObject* unit_can_attack(wesnoth_unit* unit, void* /*closure*/)
00476 {
00477     u_check;
00478     return Py_BuildValue(INTVALUE, unit->unit_->attacks_left());
00479 }
00480 
00481 static PyObject* unit_hitpoints(wesnoth_unit* unit, void* /*closure*/)
00482 {
00483     u_check;
00484     return Py_BuildValue(INTVALUE, static_cast<int>(unit->unit_->hitpoints()));
00485 }
00486 
00487 static PyObject* unit_max_hitpoints(wesnoth_unit* unit, void* /*closure*/)
00488 {
00489     u_check;
00490     return Py_BuildValue(INTVALUE, static_cast<int>(unit->unit_->max_hitpoints()));
00491 }
00492 
00493 static PyObject* unit_experience(wesnoth_unit* unit, void* /*closure*/)
00494 {
00495     u_check;
00496     return Py_BuildValue(INTVALUE, static_cast<int>(unit->unit_->experience()));
00497 }
00498 
00499 static PyObject* unit_max_experience(wesnoth_unit* unit, void* /*closure*/)
00500 {
00501     u_check;
00502     return Py_BuildValue(INTVALUE, static_cast<int>(unit->unit_->max_experience()));
00503 }
00504 
00505 static PyObject* unit_poisoned(wesnoth_unit* unit, void* /*closure*/)
00506 {
00507     u_check;
00508     return Py_BuildValue(INTVALUE, utils::string_bool(unit->unit_->get_state("poisoned")));
00509 }
00510 
00511 static PyObject* unit_slowed(wesnoth_unit* unit, void* /*closure*/)
00512 {
00513     u_check;
00514     return Py_BuildValue(INTVALUE, utils::string_bool(unit->unit_->get_state("slowed")));
00515 }
00516 
00517 static PyObject* unit_stoned(wesnoth_unit* unit, void* /*closure*/)
00518 {
00519     u_check;
00520     return Py_BuildValue(INTVALUE, utils::string_bool(unit->unit_->get_state("stoned")));
00521 }
00522 
00523 static PyObject* unit_query_valid(wesnoth_unit* unit, void* /*closure*/)
00524 {
00525     return Py_BuildValue(INTVALUE, running_instance->is_unit_valid(unit->unit_) == true ? 1 : 0);
00526 }
00527 
00528 static PyGetSetDef unit_getseters[] = {
00529     GSDEF("name",   unit_get_name,
00530         "Name of the unit (''description'' from WML).")
00531     GSDEF("is_enemy",unit_is_enemy,
00532         "True if this is an enemy unit, False if it is allied.")
00533     GSDEF("can_recruit", unit_can_recruit,
00534         "If the unit can recruit.")
00535     GSDEF("hitpoints", unit_hitpoints,
00536         "Current hitpoints of the unit.")
00537     GSDEF("max_hitpoints", unit_max_hitpoints,
00538         "Maximum hitpoints of the unit.")
00539     GSDEF("experience", unit_experience,
00540         "Current experience of the unit.")
00541     GSDEF("max_experience", unit_max_experience,
00542         "Maximum experience of the unit.")
00543     GSDEF("is_valid",unit_query_valid,
00544         "Indicates if the unit is still valid in the game. This is the only accessible "
00545         "field of an invalid unit, all others trigger an exception.")
00546     GSDEF("side",   unit_side,
00547         "The side of the unit, starting with 1.")
00548     GSDEF("movement_left", unit_movement_left,
00549         "How many movement points the unit has left.")
00550     GSDEF("max_movement", unit_max_movement,
00551         "Maximum movement points of the unit.")
00552     GSDEF("can_attack", unit_can_attack,
00553         "If the unit can still attack.")
00554     GSDEF("poisoned", unit_poisoned,
00555         "If the unit is poisoned.")
00556     GSDEF("slowed", unit_slowed,
00557         "If the unit is slowed.")
00558     GSDEF("stoned", unit_stoned,
00559         "If the unit is stoned.")
00560     {NULL, NULL, NULL, NULL, NULL }
00561 };
00562 
00563 static PyObject* wrapper_unit_type( wesnoth_unit* unit, PyObject* args )
00564 {
00565     if (!PyArg_ParseTuple(args, NOVALUE))
00566         return NULL;
00567     u_check;
00568     assert(unit->unit_->type());
00569     return wrap_unittype(*unit->unit_->type());
00570 }
00571 
00572 static PyObject* wrapper_unit_attacks( wesnoth_unit* unit, PyObject* args )
00573 {
00574     if (!PyArg_ParseTuple(args, NOVALUE))
00575         return NULL;
00576     u_check;
00577     PyObject* list = PyList_New(unit->unit_->attacks().size());
00578     for ( size_t attack = 0; attack < unit->unit_->attacks().size(); attack++)
00579         PyList_SetItem(list,attack,wrap_attacktype(unit->unit_->attacks()[attack]));
00580     return list;
00581 }
00582 
00583 static PyObject* wrapper_unit_damage_from( wesnoth_unit* unit, PyObject* args )
00584 {
00585     wesnoth_attacktype* attack;
00586     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_attacktype_type, &attack ) )
00587         return NULL;
00588     u_check;
00589     static gamemap::location no_loc;
00590     return Py_BuildValue(INTVALUE,unit->unit_->damage_from(*attack->attack_type_,true,no_loc));
00591 }
00592 
00593 static PyObject* wrapper_unittype_resistance_against( wesnoth_unittype* type, PyObject* args )
00594 {
00595     wesnoth_attacktype* attack;
00596     if (!PyArg_ParseTuple(args, OBVALUE, &wesnoth_attacktype_type, &attack))
00597         return NULL;
00598     return Py_BuildValue(INTVALUE,type->unit_type_->movement_type().resistance_against(*attack->attack_type_));
00599 }
00600 
00601 
00602 static PyMethodDef unit_methods[] = {
00603     MDEF("type", wrapper_unit_type,
00604         "Returns: unittype\n"
00605         "Returns the type of the unit.")
00606     MDEF("attacks", wrapper_unit_attacks,
00607         "Returns: attacktype[]\n"
00608         "Returns list of possible attack types.\n")
00609     MDEF("movement_cost", python_ai::wrapper_unit_movement_cost,
00610         "Parameters: location\n"
00611         "Returns: cost\n"
00612         "Returns the cost of moving over the given location.")
00613     MDEF("defense_modifier", python_ai::wrapper_unit_defense_modifier,
00614         "Parameters: location\n"
00615         "Returns: percent\n"
00616         "Returns the defense modifier in % (probability the unit will be hit) on the given location.")
00617     MDEF("damage_from", wrapper_unit_damage_from,
00618         "Parameters: attacktype\n"
00619         "Returns: percent\n"
00620         "Returns the damage in percent the unit receives when attacked with "
00621         "the given attack type. (0 means no damage at all, 100 means full "
00622         "damage, 200 means double damage.)")
00623     MDEF("find_path", python_ai::wrapper_unit_find_path,
00624         "Parameters: location from, location to, float max_cost = unit.movement_left\n"
00625         "Returns: location[] path\n"
00626         "Finds a path from 'from' to 'to' costing less than 'max_cost' "
00627         "movement points to reach and returns it as a list of locations. "
00628         "path[0] will be 'from', path[-1] will be 'to'. "
00629         "If no path can be found (for example, if the target is not reachable, "
00630         "or it would cost more than max_cost), an empty list is returned.")
00631     MDEF("attack_statistics", python_ai::wrapper_unit_attack_statistics,
00632         "Parameters: location from, location to, int attack = -1\n"
00633         "Returns: own_hp, enemy_hp\n"
00634         "Returns two dictionaries with the expected battle results when the "
00635         "unit attacks from 'from' to the unit at 'to', optionally using the "
00636         "attack with index 'attack', or if no attack is given the attack which "
00637         "would be presented to the player in the attack dialog. The "
00638         "dictionaries contain the expected hitpoints after "
00639         "the fight, as a mapping from hitpoints to percent, where percent are "
00640         "specified as floating point value from 0 to 1. For example, a return of: "
00641         "{0:1}, {50:0.5, 40:0.5} would mean, the attacking unit "
00642         "is certain to die (probability for 0 hitpoints is 1), and the enemy "
00643         "unit will either remain at 50 or 40 HP after the fight, with equal "
00644         "probability of 0.5.")
00645     {NULL,  NULL, 0, NULL}
00646 };
00647 
00648 
00649 static int unit_internal_compare(wesnoth_unit* left, wesnoth_unit* right)
00650 {
00651     return reinterpret_cast<long>(left->unit_) - reinterpret_cast<long>(right->unit_);
00652 }
00653 
00654 static PyTypeObject wesnoth_unit_type = {
00655     PyObject_HEAD_INIT(NULL)
00656     0,                         /* ob_size*/
00657     CC("wesnoth.unit"),        /* tp_name*/
00658     sizeof(wesnoth_unit),  /* tp_basicsize*/
00659     0,                         /* tp_itemsize*/
00660     0,                         /* tp_dealloc*/
00661     0,                         /* tp_print*/
00662     0,                         /* tp_getattr*/
00663     0,                         /* tp_setattr*/
00664     reinterpret_cast<cmpfunc>(unit_internal_compare),                           /* tp_compare*/
00665     0,                         /* tp_repr*/
00666     0, //UniConvert,             /* tp_as_number*/
00667     0,                         /* tp_as_sequence*/
00668     0,                         /* tp_as_mapping*/
00669     0,                         /* tp_hash */
00670     0,                         /* tp_call*/
00671     0,                         /* tp_str*/
00672     0,   /* tp_getattro*/
00673     0,   /* tp_setattro*/
00674     0,                         /* tp_as_buffer*/
00675     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
00676     CC("Represents a single unit. Trying to use a method or access a property, "
00677     "with the exception of is_valid, will result in an exception if the unit "
00678     "is invalid (was destroyed last move, and so on)."),          /* tp_doc */
00679     0,                         /* tp_traverse */
00680     0,                         /* tp_clear */
00681     0,                         /* tp_richcompare */
00682     0,                         /* tp_weaklistoffset */
00683     0,                         /* tp_iter */
00684     0,                         /* tp_iternext */
00685     unit_methods,             /* tp_methods */
00686     0,                         /* tp_members */
00687     unit_getseters,          /* tp_getset */
00688     NULL,
00689     NULL,
00690     NULL,
00691     NULL,
00692     0,
00693     NULL,
00694     NULL,
00695     NULL,
00696     NULL,
00697     NULL,
00698     NULL,
00699     NULL,
00700     NULL,
00701     NULL,
00702     NULL,
00703     NULL
00704 };
00705 
00706 typedef struct {
00707     PyObject_HEAD
00708     gamemap::location* location_;
00709 } wesnoth_location;
00710 
00711 static void wesnoth_location_dealloc(wesnoth_location* self)
00712 {
00713     delete self->location_;
00714     self->location_ = NULL;
00715     PyObject_Del(reinterpret_cast<PyObject*>(self));
00716 }
00717 
00718 static PyObject* location_get_x(wesnoth_location* location, void* /*closure*/)
00719 {
00720     return Py_BuildValue(INTVALUE, location->location_->x);
00721 }
00722 static PyObject* location_get_y(wesnoth_location* location, void* /*closure*/)
00723 {
00724     return Py_BuildValue(INTVALUE, location->location_->y);
00725 }
00726 
00727 static int location_internal_compare(wesnoth_location* left, wesnoth_location* right)
00728 {
00729     if (*left->location_ == *right->location_)
00730         return 0;
00731     return *left->location_ < *right->location_ ? -1 : 1;
00732 }
00733 
00734 static long location_internal_hash(wesnoth_location* obj)
00735 {
00736     // Never return -1, which is reserved for raising an exception.
00737     // Note that both x and y can get values < 0,
00738     // e.g. when checking all positions in a certain radius at the map border.
00739     unsigned char x = static_cast<unsigned>(obj->location_->x);
00740     unsigned char y = static_cast<unsigned>(obj->location_->y);
00741     return (x << 8) + y;
00742 }
00743 
00744 static PyGetSetDef location_getseters[] = {
00745     GSDEF("x", location_get_x,
00746         "X position, starting with 0 for leftmost column.")
00747     GSDEF("y", location_get_y,
00748         "Y position, starting with 0 for topmost row.")
00749     { NULL, NULL, NULL, NULL, NULL }
00750 };
00751 
00752 static PyObject* wrapper_location_adjacent_to( wesnoth_location* left, PyObject* args );
00753 static PyObject* wrapper_location_distance_to( wesnoth_location* left, PyObject* args );
00754 static PyMethodDef location_methods[] = {
00755     MDEF("adjacent_to", wrapper_location_adjacent_to,
00756         "Parameters: location\n"
00757         "Returns: result\n"
00758         "Returns True if the location is adjacent to this one, False otherwise.")
00759     MDEF("distance_to", wrapper_location_distance_to,
00760         "Parameters: location\n"
00761         "Returns: int distance\n"
00762         "Returns the distance in hexes to the other location.")
00763     { NULL, NULL, 0, NULL }
00764 };
00765 
00766 static PyTypeObject wesnoth_location_type = {
00767     PyObject_HEAD_INIT(NULL)
00768     0,                         /* ob_size*/
00769     CC("wesnoth.location"),        /* tp_name*/
00770     sizeof(wesnoth_location),  /* tp_basicsize*/
00771     0,                         /* tp_itemsize*/
00772     reinterpret_cast<destructor>(wesnoth_location_dealloc),                       /* tp_dealloc*/
00773     0,                         /* tp_print*/
00774     0,                         /* tp_getattr*/
00775     0,                         /* tp_setattr*/
00776     reinterpret_cast<cmpfunc>(location_internal_compare),                           /* tp_compare*/
00777     0,                         /* tp_repr*/
00778     0, //UniConvert,             /* tp_as_number*/
00779     0,                         /* tp_as_sequence*/
00780     0,                         /* tp_as_mapping*/
00781     reinterpret_cast<hashfunc>(location_internal_hash),                       /* tp_hash */
00782     0,                         /* tp_call*/
00783     0,                         /* tp_str*/
00784     0,   /* tp_getattro*/
00785     0,   /* tp_setattro*/
00786     0,                         /* tp_as_buffer*/
00787     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
00788     CC("Represents a single location on the map."),       /* tp_doc */
00789     0,                         /* tp_traverse */
00790     0,                         /* tp_clear */
00791     0,                         /* tp_richcompare */
00792     0,                         /* tp_weaklistoffset */
00793     0,                         /* tp_iter */
00794     0,                         /* tp_iternext */
00795     location_methods,             /* tp_methods */
00796     0,                         /* tp_members */
00797     location_getseters,          /* tp_getset */
00798     NULL,
00799     NULL,
00800     NULL,
00801     NULL,
00802     0,
00803     NULL,
00804     NULL,
00805     NULL,
00806     NULL,
00807     NULL,
00808     NULL,
00809     NULL,
00810     NULL,
00811     NULL,
00812     NULL,
00813     NULL
00814 };
00815 
00816 static PyObject *wrap_location(const gamemap::location& loc)
00817 {
00818     wesnoth_location* location;
00819     location = reinterpret_cast<wesnoth_location*>(PyObject_NEW(wesnoth_location, &wesnoth_location_type));
00820     location->location_ = new gamemap::location(loc.x, loc.y);
00821     return reinterpret_cast<PyObject*>(location);
00822 }
00823 
00824 void recalculate_movemaps()
00825 {
00826     python_ai *ai = running_instance;
00827     ai->src_dst_.clear();
00828     ai->dst_src_.clear();
00829     ai->possible_moves_.clear();
00830     ai->calculate_possible_moves(ai->possible_moves_,
00831         ai->src_dst_, ai->dst_src_, false);
00832 
00833     ai->enemy_src_dst_.clear();
00834     ai->enemy_dst_src_.clear();
00835     ai->enemy_possible_moves_.clear();
00836     ai->calculate_possible_moves(ai->enemy_possible_moves_,
00837         ai->enemy_src_dst_, ai->enemy_dst_src_, true);
00838 }
00839 
00840 static PyObject* wrapper_location_adjacent_to( wesnoth_location* left, PyObject* args )
00841 {
00842     wesnoth_location* right;
00843     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &right ) )
00844         return NULL;
00845     return Py_BuildValue(INTVALUE, tiles_adjacent(*left->location_,*right->location_) ? 1 : 0);
00846 }
00847 
00848 static PyObject* wrapper_location_distance_to( wesnoth_location* left, PyObject* args )
00849 {
00850     wesnoth_location* right;
00851     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &right ) )
00852         return NULL;
00853     return Py_BuildValue(INTVALUE, distance_between(*left->location_,*right->location_));
00854 }
00855 
00856 
00857 
00858 typedef struct {
00859     PyObject_HEAD
00860     const gamemap* map_;
00861 } wesnoth_gamemap;
00862 
00863 static PyObject* gamemap_get_x(wesnoth_gamemap* map, void* /*closure*/)
00864 {
00865     return Py_BuildValue(INTVALUE, map->map_->w());
00866 }
00867 static PyObject* gamemap_get_y(wesnoth_gamemap* map, void* /*closure*/)
00868 {
00869     return Py_BuildValue(INTVALUE, map->map_->h());
00870 }
00871 
00872 static PyGetSetDef gamemap_getseters[] = {
00873     GSDEF("x", gamemap_get_x, "Width of the map in hexes.")
00874     GSDEF("y", gamemap_get_y, "Height of the map in hexes.")
00875     { NULL, NULL,                   NULL,   NULL,   NULL },
00876 };
00877 
00878 static PyObject* wrapper_getmap_is_village( wesnoth_gamemap* map, PyObject* args )
00879 {
00880     wesnoth_location* location;
00881     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &location ) )
00882         return NULL;
00883     return Py_BuildValue(INTVALUE, map->map_->is_village(*location->location_) ? 1 : 0);
00884 }
00885 
00886 static PyObject* wrapper_getmap_is_keep( wesnoth_gamemap* map, PyObject* args )
00887 {
00888     wesnoth_location* location;
00889     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &location ) )
00890         return NULL;
00891     return Py_BuildValue(INTVALUE, map->map_->is_keep(*location->location_) ? 1 : 0);
00892 }
00893 
00894 static PyObject* wrapper_getmap_is_castle( wesnoth_gamemap* map, PyObject* args )
00895 {
00896     wesnoth_location* location;
00897     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &location ) )
00898         return NULL;
00899     return Py_BuildValue(INTVALUE, map->map_->is_castle(*location->location_) ? 1 : 0);
00900 }
00901 
00902 static PyObject* wrapper_getmap_is_border( wesnoth_gamemap* map, PyObject* args )
00903 {
00904     wesnoth_location* location;
00905     bool on_board, on_board_without_borders;
00906     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &location ) )
00907         return NULL;
00908     on_board = map->map_->on_board(*location->location_, true);
00909     on_board_without_borders = map->map_->on_board(*location->location_, false);
00910     return Py_BuildValue(INTVALUE, on_board && !on_board_without_borders ? 1 : 0);
00911 }
00912 
00913 static PyMethodDef gamemap_methods[] = {
00914     MDEF("is_village", wrapper_getmap_is_village,
00915         "Parameters: location\n"
00916         "Returns: result\n"
00917         "Returns True if a village is at the given location, False otherwise.")
00918     MDEF("is_keep", wrapper_getmap_is_keep,
00919         "Parameters: location\n"
00920         "Returns: result\n"
00921         "Returns True if a keep (where a leader must stand to recruit) is at "
00922         "the given location, False otherwise.")
00923     MDEF("is_castle", wrapper_getmap_is_castle,
00924         "Parameters: location\n"
00925         "Returns: result\n"
00926         "Returns True if the given location is a castle tile (where units are "
00927         "recruited to), False otherwise.")
00928     MDEF("is_border", wrapper_getmap_is_border,
00929         "Parameters: location\n"
00930         "Returns: result\n"
00931         "Returns True if the given location is a border tile, False otherwise.")
00932     { NULL, NULL, 0, NULL }
00933 };
00934 
00935 static PyTypeObject wesnoth_gamemap_type = {
00936     PyObject_HEAD_INIT(NULL)
00937     0,                         /* ob_size*/
00938     CC("wesnoth.gamemap"),        /* tp_name*/
00939     sizeof(wesnoth_gamemap),  /* tp_basicsize*/
00940     0,                         /* tp_itemsize*/
00941     0,                         /* tp_dealloc*/
00942     0,                         /* tp_print*/
00943     0,                         /* tp_getattr*/
00944     0,                         /* tp_setattr*/
00945     0,                         /* tp_compare*/
00946     0,                         /* tp_repr*/
00947     0, //UniConvert,             /* tp_as_number*/
00948     0,                         /* tp_as_sequence*/
00949     0,                         /* tp_as_mapping*/
00950     0,                         /* tp_hash */
00951     0,                         /* tp_call*/
00952     0,                         /* tp_str*/
00953     0,   /* tp_getattro*/
00954     0,                          /* tp_setattro*/
00955     0,                         /* tp_as_buffer*/
00956     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
00957     CC("Represents the current map."),       /* tp_doc */
00958     0,                         /* tp_traverse */
00959     0,                         /* tp_clear */
00960     0,                         /* tp_richcompare */
00961     0,                         /* tp_weaklistoffset */
00962     0,                         /* tp_iter */
00963     0,                         /* tp_iternext */
00964     gamemap_methods,             /* tp_methods */
00965     0,                         /* tp_members */
00966     gamemap_getseters,          /* tp_getset */
00967     NULL,
00968     NULL,
00969     NULL,
00970     NULL,
00971     0,
00972     NULL,
00973     NULL,
00974     NULL,
00975     NULL,
00976     NULL,
00977     NULL,
00978     NULL,
00979     NULL,
00980     NULL,
00981     NULL,
00982     NULL
00983 };
00984 
00985 static PyObject* wrapper_team_name(wesnoth_team* team, void* /*closure*/)
00986 {
00987     return Py_BuildValue(STRINGVALUE, team->team_->team_name().c_str());
00988 }
00989 
00990 static PyObject* wrapper_team_income(wesnoth_team* team, void* /*closure*/)
00991 {
00992     return Py_BuildValue(INTVALUE, team->team_->income());
00993 }
00994 
00995 static PyObject* wrapper_team_gold(wesnoth_team* team, void* /*closure*/)
00996 {
00997     return Py_BuildValue(INTVALUE, team->team_->gold());
00998 }
00999 
01000 static PyObject* wrapper_team_is_enemy(wesnoth_team* team, void* /*closure*/)
01001 {
01002     int result;
01003 
01004     // Find side number of team
01005     int side = 0;
01006     for (size_t t = 0; t < running_instance->get_teams().size(); t++) {
01007         if (team->team_ == &running_instance->get_teams()[t]) {
01008             side = 1 + t;
01009             break;
01010         }
01011     }
01012 
01013     result = running_instance->current_team().is_enemy(side) == true ? 1 : 0;
01014     return Py_BuildValue(INTVALUE, result);
01015 }
01016 
01017 static PyObject* wrapper_team_side(wesnoth_team* team, void* /*closure*/)
01018 {
01019     int side = 0;
01020     for (size_t t = 0; t < running_instance->get_teams().size(); t++) {
01021         if (team->team_ == &running_instance->get_teams()[t]) {
01022             side = 1 + t;
01023             break;
01024         }
01025     }
01026     return Py_BuildValue(INTVALUE, side);
01027 }
01028 
01029 static int wrapper_team_internal_compare(wesnoth_team* left, wesnoth_team* right)
01030 {
01031     return reinterpret_cast<long>(left->team_) - reinterpret_cast<long>(right->team_);
01032 }
01033 
01034 static PyObject* wrapper_team_owns_village( wesnoth_team* team, PyObject* args )
01035 {
01036     wesnoth_location* location;
01037     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &location ) )
01038         return NULL;
01039     return Py_BuildValue(INTVALUE, team->team_->owns_village(*location->location_) ? 1 : 0);
01040 }
01041 
01042 PyObject* python_ai::wrapper_team_recruits( wesnoth_team* team, PyObject* args )
01043 {
01044     if ( !PyArg_ParseTuple( args, NOVALUE) )
01045         return NULL;
01046 
01047     PyObject* list = PyList_New(team->team_->recruits().size());
01048     int r;
01049     int idx = 0;
01050     for (std::set<std::string>::const_iterator recruit = team->team_->recruits().begin(); recruit != team->team_->recruits().end(); ++recruit)
01051     {
01052         std::map<std::string,unit_type>::const_iterator t = unit_type_data::types().find(*recruit);
01053         assert(t != unit_type_data::types().end());
01054         r = PyList_SetItem(list,idx++,wrap_unittype(t->second));
01055     }
01056     return list;
01057 }
01058 
01059 PyObject *python_ai::wrapper_team_targets(PyObject *, PyObject *args)
01060 {
01061     if (!PyArg_ParseTuple(args, NOVALUE))
01062         return NULL;
01063 
01064     PyObject* dict = PyDict_New();
01065 
01066     //! @todo FIXME: There should be a C++ method to return all targets instead,
01067     // for now it's just copy&pasted.
01068     std::vector<team::target>& team_targets =
01069         running_instance->current_team().targets();
01070 
01071     unit_map &units = running_instance->get_info().units;
01072 
01073     for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {
01074         // explicit targets for this team
01075         for(std::vector<team::target>::iterator j = team_targets.begin();
01076             j != team_targets.end(); ++j) {
01077             if(u->second.matches_filter(&(j->criteria), u->first)) {
01078                 PyDict_SetItem(dict, wrap_location(u->first),
01079                     PyLong_FromLong(static_cast<int>(j->value)));
01080             }
01081         }
01082     }
01083 
01084     return dict;
01085 }
01086 
01087 static PyMethodDef team_methods[] = {
01088     MDEF("owns_village", wrapper_team_owns_village,
01089         "Parameters: location\n"
01090         "Returns: result\n"
01091         "True if the team owns a village at the given location.")
01092     MDEF("recruits", python_ai::wrapper_team_recruits,
01093         "Returns: recruits\n"
01094         "Returns a list of wesnoth.unittype objects"
01095         " of all possible recruits for this team.")
01096     MDEF("targets", python_ai::wrapper_team_targets,
01097         "Returns: targets\n"
01098         "Returns a dictionary containing all WML targets for the "
01099         "team, mapping their locations to the scores in WML.")
01100     { NULL, NULL, 0, NULL }
01101 };
01102 
01103 static PyGetSetDef team_getseters[] = {
01104     GSDEF("name", wrapper_team_name, "The name of this team.")
01105     GSDEF("gold", wrapper_team_gold, "The current amount of gold this team has.")
01106     GSDEF("income", wrapper_team_income, "The current per-turn income if this team.")
01107     GSDEF("side", wrapper_team_side, "Side number of this team, starting with 1.")
01108     GSDEF("is_enemy", wrapper_team_is_enemy, "Whether this team is an enemy.")
01109     { NULL, NULL, NULL, NULL, NULL }
01110 };
01111 
01112 static PyTypeObject wesnoth_team_type = {
01113     PyObject_HEAD_INIT(NULL)
01114     0,                         /* ob_size*/
01115     CC("wesnoth.team"),        /* tp_name*/
01116     sizeof(wesnoth_team),  /* tp_basicsize*/
01117     0,                         /* tp_itemsize*/
01118     0,                         /* tp_dealloc*/
01119     0,                         /* tp_print*/
01120     0,                         /* tp_getattr*/
01121     0,                         /* tp_setattr*/
01122     reinterpret_cast<cmpfunc>(wrapper_team_internal_compare),                           /* tp_compare*/
01123     0,                         /* tp_repr*/
01124     0, //UniConvert,             /* tp_as_number*/
01125     0,                         /* tp_as_sequence*/
01126     0,                         /* tp_as_mapping*/
01127     0,                         /* tp_hash */
01128     0,                         /* tp_call*/
01129     0,                         /* tp_str*/
01130     0,   /* tp_getattro*/
01131     0,   /* tp_setattro*/
01132     0,                         /* tp_as_buffer*/
01133     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
01134     CC("Represents one team/player/side."),       /* tp_doc */
01135     0,                         /* tp_traverse */
01136     0,                         /* tp_clear */
01137     0,                         /* tp_richcompare */
01138     0,                         /* tp_weaklistoffset */
01139     0,                         /* tp_iter */
01140     0,                         /* tp_iternext */
01141     team_methods,             /* tp_methods */
01142     0,                         /* tp_members */
01143     team_getseters,          /* tp_getset */
01144     NULL,
01145     NULL,
01146     NULL,
01147     NULL,
01148     0,
01149     NULL,
01150     NULL,
01151     NULL,
01152     NULL,
01153     NULL,
01154     NULL,
01155     NULL,
01156     NULL,
01157     NULL,
01158     NULL,
01159     NULL
01160 };
01161 
01162 PyObject* python_ai::wrapper_unit_movement_cost( wesnoth_unit* unit, PyObject* args )
01163 {
01164     gamemap const &map = running_instance->get_info().map;
01165     wesnoth_location* loc;
01166     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &loc ) )
01167         return NULL;
01168     return Py_BuildValue(INTVALUE,unit->unit_->movement_cost(map.get_terrain(*loc->location_)));
01169 }
01170 
01171 PyObject* python_ai::wrapper_unit_defense_modifier( wesnoth_unit* unit, PyObject* args )
01172 {
01173     gamemap const &map = running_instance->get_info().map;
01174     wesnoth_location* loc;
01175     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &loc ) )
01176         return NULL;
01177     return Py_BuildValue(INTVALUE,unit->unit_->defense_modifier(map.get_terrain(*loc->location_)));
01178 }
01179 
01180 PyObject* python_ai::wrapper_unittype_movement_cost( wesnoth_unittype* type, PyObject* args )
01181 {
01182     gamemap const &map = running_instance->get_info().map;
01183     wesnoth_location* loc;
01184     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &loc ) )
01185         return NULL;
01186     return Py_BuildValue(INTVALUE,type->unit_type_->movement_type().movement_cost(
01187         map, map.get_terrain(*loc->location_)));
01188 }
01189 
01190 PyObject* python_ai::wrapper_unittype_defense_modifier( wesnoth_unittype* type, PyObject* args )
01191 {
01192     gamemap const &map = running_instance->get_info().map;
01193     wesnoth_location* loc;
01194     if ( !PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &loc ) )
01195         return NULL;
01196     return Py_BuildValue(INTVALUE,type->unit_type_->movement_type().defense_modifier(
01197         map, map.get_terrain(*loc->location_)));
01198 }
01199 
01200 typedef struct {
01201     PyObject_HEAD
01202     const gamestatus* status_;
01203 } wesnoth_gamestatus;
01204 
01205 static PyMethodDef gamestatus_methods[] = {
01206     { NULL,     NULL,   0, NULL }
01207 };
01208 
01209 static PyObject* wrapper_gamestatus_turn(wesnoth_gamestatus* status, void* /*closure*/)
01210 {
01211     return Py_BuildValue(INTVALUE, status->status_->turn());
01212 }
01213 
01214 static PyObject* wrapper_gamestatus_number_of_turns(wesnoth_gamestatus* status, void* /*closure*/)
01215 {
01216     return Py_BuildValue(INTVALUE, status->status_->number_of_turns());
01217 }
01218 
01219 static PyObject* wrapper_gamestatus_lawful_bonus(wesnoth_gamestatus* status, void* /*closure*/)
01220 {
01221     return Py_BuildValue(INTVALUE, status->status_->get_time_of_day().lawful_bonus);
01222 }
01223 
01224 static PyObject* wrapper_gamestatus_previous_lawful_bonus(wesnoth_gamestatus* status, void* /*closure*/)
01225 {
01226     return Py_BuildValue(INTVALUE, status->status_->get_previous_time_of_day().lawful_bonus);
01227 }
01228 
01229 static PyObject* wrapper_gamestatus_gold_per_village(wesnoth_gamestatus* /*status*/, void* /*closure*/)
01230 {
01231     return Py_BuildValue(INTVALUE, settings::get_village_gold(""));
01232 }
01233 
01234 static PyGetSetDef gamestatus_getseters[] = {
01235     GSDEF("turn", wrapper_gamestatus_turn,  "The current turn.")
01236     GSDEF("number_of_turns", wrapper_gamestatus_number_of_turns,
01237         "The maximum number of turns of the whole game.")
01238     GSDEF("lawful_bonus", wrapper_gamestatus_lawful_bonus,
01239         "The bonus for lawful units in the current turn. This is the percentage to add to "
01240         "the attack damage of lawful units (alignment = 0), and to subtract from chaotic "
01241         "units (alignment = 2). Neutral units (alignment = 1) are not affected.")
01242     GSDEF("previous_lawful_bonus", wrapper_gamestatus_previous_lawful_bonus,
01243         "The value of lawful_bonus in the previous turn.")
01244     GSDEF("gold_per_village", wrapper_gamestatus_gold_per_village,
01245         "The gold a village generates each turn.")
01246     { NULL, NULL, NULL, NULL, NULL }
01247 };
01248 
01249 static PyTypeObject wesnoth_gamestatus_type = {
01250     PyObject_HEAD_INIT(NULL)
01251     0,                         /* ob_size*/
01252     CC("wesnoth.team"),        /* tp_name*/
01253     sizeof(wesnoth_gamestatus),  /* tp_basicsize*/
01254     0,                         /* tp_itemsize*/
01255     0,                         /* tp_dealloc*/
01256     0,                         /* tp_print*/
01257     0,                         /* tp_getattr*/
01258     0,                         /* tp_setattr*/
01259     0,                         /* tp_compare*/
01260     0,                         /* tp_repr*/
01261     0, //UniConvert,             /* tp_as_number*/
01262     0,                         /* tp_as_sequence*/
01263     0,                         /* tp_as_mapping*/
01264     0,                         /* tp_hash */
01265     0,                         /* tp_call*/
01266     0,                         /* tp_str*/
01267     0,   /* tp_getattro*/
01268     0,   /* tp_setattro*/
01269     0,                         /* tp_as_buffer*/
01270     Py_TPFLAGS_DEFAULT,        /* tp_flags*/
01271     CC("This class has information about the game status."),       /* tp_doc */
01272     0,                         /* tp_traverse */
01273     0,                         /* tp_clear */
01274     0,                         /* tp_richcompare */
01275     0,                         /* tp_weaklistoffset */
01276     0,                         /* tp_iter */
01277     0,                         /* tp_iternext */
01278     gamestatus_methods,             /* tp_methods */
01279     0,                         /* tp_members */
01280     gamestatus_getseters,          /* tp_getset */
01281     NULL,
01282     NULL,
01283     NULL,
01284     NULL,
01285     0,
01286     NULL,
01287     NULL,
01288     NULL,
01289     NULL,
01290     NULL,
01291     NULL,
01292     NULL,
01293     NULL,
01294     NULL,
01295     NULL,
01296     NULL
01297 };
01298 
01299 static PyObject* wrap_move_map(const ai_interface::move_map& wrap)
01300 {
01301     PyObject* dict = PyDict_New();
01302     PyObject* list;
01303     PyObject *loc, *loc2;
01304     ai_interface::move_map::const_iterator pos;
01305     for (pos = wrap.begin(); pos != wrap.end(); pos++)
01306     {
01307         loc = wrap_location(pos->first);
01308         list = PyDict_GetItem(dict,loc);
01309         if (!list)
01310         {
01311             list = PyList_New(0);
01312             PyDict_SetItem(dict, loc, list);
01313             Py_DECREF(list);
01314         }
01315         loc2 = wrap_location(pos->second);
01316         PyList_Append(list, loc2);
01317         Py_DECREF(loc2);
01318         Py_DECREF(loc);
01319     }
01320     return dict;
01321 }
01322 
01323 PyObject* python_ai::wrapper_log_message(PyObject* /*self*/, PyObject* args)
01324 {
01325     const char* msg;
01326     if ( !PyArg_ParseTuple( args, STRINGVALUE, &msg ) )
01327         return NULL;
01328     running_instance->log_message(msg);
01329     Py_INCREF(Py_None);
01330     return Py_None;
01331 }
01332 
01333 PyObject* python_ai::wrapper_log(PyObject* /*self*/, PyObject* args)
01334 {
01335     const char *msg;
01336     if (!PyArg_ParseTuple( args, STRINGVALUE, &msg))
01337         return NULL;
01338     LOG_AI << msg;
01339     Py_INCREF(Py_None);
01340     return Py_None;
01341 }
01342 
01343 PyObject* python_ai::wrapper_get_units(PyObject* /*self*/, PyObject* args)
01344 {
01345     if ( !PyArg_ParseTuple( args, NOVALUE) )
01346         return NULL;
01347 
01348     PyObject* dict = PyDict_New();
01349     if ( !dict )
01350     {
01351         Py_INCREF(Py_None);
01352         return Py_None;
01353     }
01354 
01355     PyObject* key;
01356     wesnoth_unit* unit;
01357     int ret;
01358 
01359     for(unit_map::const_iterator i = running_instance->get_info().units.begin(); i != running_instance->get_info().units.end(); ++i) {
01360         key = wrap_location(i->first);
01361         unit = static_cast<wesnoth_unit*>(PyObject_NEW(wesnoth_unit, &wesnoth_unit_type));
01362         unit->unit_ = &i->second;
01363         ret = PyDict_SetItem(dict,reinterpret_cast<PyObject*>(key),reinterpret_cast<PyObject*>(unit));
01364         Py_DECREF(unit);
01365         Py_DECREF(key);
01366     }
01367 
01368     return dict;
01369 }
01370 
01371 PyObject* python_ai::wrapper_get_location(PyObject* /*self*/, PyObject* args)
01372 {
01373     int x, y;
01374     if (!PyArg_ParseTuple( args, CC("ii"), &x, &y ))
01375         return NULL;
01376     if (x < 0 || x >= running_instance->get_info().map.w()) return_none;
01377     if (y < 0 || y >= running_instance->get_info().map.h()) return_none;
01378 
01379     gamemap::location loc(x,y);
01380     return wrap_location(loc);
01381 }
01382 
01383 PyObject* python_ai::wrapper_get_map(PyObject* /*self*/, PyObject* args)
01384 {
01385     if (!PyArg_ParseTuple(args, NOVALUE)) return NULL;
01386 
01387     wesnoth_gamemap* map = static_cast<wesnoth_gamemap*>(PyObject_NEW(wesnoth_gamemap, &wesnoth_gamemap_type));
01388     map->map_ = &running_instance->get_info().map;
01389     return reinterpret_cast<PyObject*>(map);
01390 }
01391 
01392 PyObject* python_ai::wrapper_get_teams(PyObject* /*self*/, PyObject* args)
01393 {
01394     if (!PyArg_ParseTuple(args, NOVALUE)) return NULL;
01395 
01396     PyObject* list = PyList_New(running_instance->get_info().teams.size());
01397     wesnoth_team* the_team;
01398 
01399     for (size_t team = 0; team < running_instance->get_info().teams.size(); team++)
01400     {
01401         the_team = static_cast<wesnoth_team*>(PyObject_NEW(wesnoth_team, &wesnoth_team_type));
01402         the_team->team_ = &running_instance->get_info().teams[team];
01403         PyList_SetItem(list,team,reinterpret_cast<PyObject*>(the_team));
01404     }
01405 
01406     return list;
01407 }
01408 
01409 PyObject* python_ai::wrapper_get_current_team(PyObject* /*self*/, PyObject* /*args*/)
01410 {
01411     wesnoth_team* the_team;
01412     the_team = static_cast<wesnoth_team*>(PyObject_NEW(wesnoth_team, &wesnoth_team_type));
01413     the_team->team_ = &running_instance->current_team();
01414     return reinterpret_cast<PyObject*>(the_team);
01415 }
01416 
01417 PyObject* python_ai::wrapper_get_src_dst(PyObject* /*self*/, PyObject* /*args*/)
01418 {
01419     return wrap_move_map(running_instance->src_dst_);
01420 }
01421 
01422 PyObject* python_ai::wrapper_get_dst_src(PyObject* /*self*/, PyObject* /*args*/)
01423 {
01424     return wrap_move_map(running_instance->dst_src_);
01425 }
01426 
01427 PyObject* python_ai::wrapper_get_enemy_src_dst(PyObject* /*self*/, PyObject* /*args*/)
01428 {
01429     return wrap_move_map(running_instance->enemy_src_dst_);
01430 }
01431 
01432 PyObject* python_ai::wrapper_get_enemy_dst_src(PyObject* /*self*/, PyObject* /*args*/)
01433 {
01434     return wrap_move_map(running_instance->enemy_dst_src_);
01435 }
01436 
01437 PyObject* python_ai::wrapper_move_unit(PyObject* /*self*/, PyObject* args)
01438 {
01439     wesnoth_location* from;
01440     wesnoth_location* to;
01441     if (!PyArg_ParseTuple( args, CC("O!O!"), &wesnoth_location_type, &from,
01442         &wesnoth_location_type, &to ) )
01443         return NULL;
01444 
01445     bool valid = false;
01446     ai_interface::move_map::const_iterator pos;
01447     for (pos = running_instance->src_dst_.begin(); pos != running_instance->src_dst_.end(); pos++)
01448     {
01449         if ( pos->first == ( *from->location_ ) )
01450         {
01451             valid = true;
01452             if ( pos->second == ( *to->location_ ) )
01453                 break;
01454         }
01455     }
01456     if (!valid) return_none;
01457     if (pos == running_instance->src_dst_.end()) return_none;
01458 
01459     location location;
01460     wrap(
01461         location = running_instance->move_unit_partial(
01462             *from->location_, *to->location_, running_instance->possible_moves_);
01463     )
01464 
01465     PyObject* loc = wrap_location(location);
01466 
01467     recalculate_movemaps();
01468 
01469     return loc;
01470 }
01471 
01472 PyObject* python_ai::wrapper_attack_unit(PyObject* /*self*/, PyObject* args)
01473 {
01474     wesnoth_location* from;
01475     wesnoth_location* to;
01476     int weapon = -1; // auto-choose
01477     if (!PyArg_ParseTuple( args, CC("O!O!|i"), &wesnoth_location_type, &from,
01478         &wesnoth_location_type, &to, &weapon ) )
01479         return NULL;
01480 
01481     //! @todo FIXME: Remove this check and let the C++ code do the check
01482     // if the attack is valid at all (there may be ranged attacks or similar later,
01483     // and then the code below will horribly fail).
01484     if (!tiles_adjacent(*from->location_, *to->location_))
01485         return_none;
01486 
01487     // check if units actually exist
01488     bool fromexists = false;
01489     bool toexists  = false;
01490     for(unit_map::const_iterator i = running_instance->get_info().units.begin(); i != running_instance->get_info().units.end(); ++i) {
01491         if (i->first == *from->location_) {
01492             fromexists = true;
01493         }
01494         else if (i->first == *to->location_) {
01495             toexists = true;
01496         }
01497     }
01498 
01499     if (!fromexists or !toexists) return_none;
01500 
01501     info& inf = running_instance->get_info();
01502 
01503     battle_context bc(
01504         inf.map,
01505         inf.teams,
01506         inf.units,
01507         inf.state,
01508         *from->location_,
01509         *to->location_,
01510         weapon);
01511 
01512     wrap(
01513         running_instance->attack_enemy(*from->location_,*to->location_,
01514             bc.get_attacker_stats().attack_num,
01515             bc.get_defender_stats().attack_num);
01516     )
01517 
01518 
01519     recalculate_movemaps();
01520 
01521     Py_INCREF(Py_None);
01522     return Py_None;
01523 }
01524 
01525 PyObject* python_ai::wrapper_get_adjacent_tiles(PyObject* /*self*/, PyObject* args)
01526 {
01527     wesnoth_location* where;
01528     if (!PyArg_ParseTuple( args, OBVALUE, &wesnoth_location_type, &where))
01529          return NULL;
01530 
01531     gamemap const &map = running_instance->get_info().map;
01532     PyObject* list = PyList_New(0);
01533     gamemap::location loc[6];
01534     get_adjacent_tiles(*where->location_,loc);
01535     for ( int tile = 0; tile < 6; tile++ )
01536         if (loc[tile].valid(map.w(), map.h()))
01537         {
01538             PyObject *o = wrap_location(loc[tile]);
01539             PyList_Append(list, o);
01540             Py_DECREF(o);
01541         }
01542     return list;
01543 }
01544 
01545 PyObject* python_ai::wrapper_recruit_unit(PyObject* /*self*/, PyObject* args)
01546 {
01547     wesnoth_location* where;
01548     const char* name;
01549     if (!PyArg_ParseTuple( args, CC("sO!"), &name, &wesnoth_location_type, &where))
01550         return NULL;
01551 
01552     int r;
01553     wrap(
01554         r = running_instance->recruit(name,*where->location_) == true ? 1 : 0;
01555     )
01556 
01557     recalculate_movemaps();
01558 
01559     return Py_BuildValue(INTVALUE, r);
01560 }
01561 
01562 PyObject* python_ai::wrapper_get_gamestatus(PyObject* /*self*/, PyObject* args)
01563 {
01564     if (!PyArg_ParseTuple(args, NOVALUE))
01565         return NULL;
01566     wesnoth_gamestatus* status;
01567     status = static_cast<wesnoth_gamestatus*>(PyObject_NEW(wesnoth_gamestatus, &wesnoth_gamestatus_type));
01568     status->status_ = &running_instance->get_info().state;
01569     return reinterpret_cast<PyObject*>(status);
01570 }
01571 
01572 PyObject* python_ai::wrapper_unit_find_path(wesnoth_unit* self, PyObject* args)
01573 {
01574     wesnoth_location* from;
01575     wesnoth_location* to;
01576     double max_cost = self->unit_->movement_left();
01577     if ( !PyArg_ParseTuple( args, CC("O!O!|d"), &wesnoth_location_type, &from,
01578         &wesnoth_location_type, &to, &max_cost ) )
01579         return NULL;
01580     if (!running_instance->is_unit_valid(self->unit_))
01581         return NULL;
01582 
01583     info& inf = running_instance->get_info();
01584 
01585     max_cost += 1; // should be at least 1
01586 
01587     const shortest_path_calculator calc(*self->unit_,
01588         running_instance->current_team(),
01589         inf.units, inf.teams, inf.map);
01590     const paths::route& route = a_star_search(*from->location_, *to->location_,
01591         max_cost, &calc, inf.map.w(), inf.map.h());
01592 
01593     PyObject* steps = PyList_New(route.steps.size());
01594     for (size_t step = 0; step < route.steps.size(); step++)
01595         PyList_SetItem(steps,step,wrap_location(route.steps[step]));
01596 
01597     return steps;
01598 }
01599 
01600 PyObject* python_ai::wrapper_unit_attack_statistics(wesnoth_unit* self, PyObject* args)
01601 {
01602     wesnoth_location* from;
01603     wesnoth_location* to;
01604     int weapon = -1;
01605 
01606     if (!PyArg_ParseTuple(args, CC("O!O!|i"), &wesnoth_location_type, &from,
01607         &wesnoth_location_type, &to, &weapon))
01608         return NULL;
01609     if (!running_instance->is_unit_valid(self->unit_))
01610         return_none;
01611     if (weapon < -1 || weapon >= static_cast<int>(self->unit_->attacks().size())){
01612         return_none;
01613     }
01614 
01615     info& inf = running_instance->get_info();
01616 
01617     // We need to temporarily move our unit to where the attack calculation
01618     // is supposed to take place.
01619     std::pair<gamemap::location,unit> *temp = inf.units.extract(*from->location_);
01620     std::pair<gamemap::location,unit> *backup = temp;
01621     std::pair<gamemap::location,unit> replace(*from->location_,*self->unit_);
01622     inf.units.add(&replace);
01623 
01624     battle_context bc(
01625         inf.map,
01626         inf.teams,
01627         inf.units,
01628         inf.state,
01629         *from->location_,
01630         *to->location_,
01631         weapon);
01632 
01633     unsigned int i;
01634     std::vector<double>attacker = bc.get_attacker_combatant().hp_dist;
01635     PyObject* adict = PyDict_New();
01636     for (i = 0; i < attacker.size(); i++) {
01637         if (attacker[i] > 0)
01638             PyDict_SetItem(adict, PyInt_FromLong(i), PyFloat_FromDouble(attacker[i]));
01639     }
01640 
01641     std::vector<double>defender = bc.get_defender_combatant().hp_dist;
01642     PyObject* ddict = PyDict_New();
01643     for (i = 0; i < defender.size(); i++) {
01644         if (defender[i] > 0)
01645             PyDict_SetItem(ddict, PyInt_FromLong(i), PyFloat_FromDouble(defender[i]));
01646     }
01647 
01648     // Restore old position again
01649     temp = inf.units.extract(*from->location_);
01650     if (backup)
01651         inf.units.add(backup);
01652 
01653     PyObject *ret = Py_BuildValue(CC("(OO)"), adict, ddict);
01654 
01655     return ret;
01656 }
01657 
01658 PyObject* python_ai::wrapper_set_variable(PyObject* /*self*/, PyObject* args)
01659 {
01660     char const *variable;
01661     PyObject *value;
01662     if (!PyArg_ParseTuple(args, CC("sO"), &variable, &value))
01663         return NULL;
01664     config const &old_memory = running_instance->current_team().ai_memory();
01665     config new_memory(old_memory);
01666     PyObject *so = PyMarshal_WriteObjectToString(value, Py_MARSHAL_VERSION);
01667     if (so)
01668     {
01669         char *cs;
01670         Py_ssize_t len;
01671         PyString_AsStringAndSize(so, &cs, &len);
01672         std::string s;
01673         char const *digits[] = {
01674             "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
01675             "a", "b", "c", "d", "e", "f"};
01676         int i;
01677         for (i = 0; i < len; i++)
01678         {
01679             unsigned char x = cs[i];
01680             s += std::string(digits[x >> 4]) + std::string(digits[x & 15]);
01681         }
01682         new_memory[variable] = s;
01683         running_instance->current_team().set_ai_memory(new_memory);
01684 
01685         Py_DECREF(so);
01686     }
01687     Py_INCREF(Py_None);
01688     return Py_None;
01689 }
01690 
01691 PyObject* python_ai::wrapper_get_variable(PyObject* /*self*/, PyObject* args)
01692 {
01693     char const *variable;
01694     if (!PyArg_ParseTuple(args, STRINGVALUE, &variable))
01695         return NULL;
01696     config const &memory = running_instance->current_team().ai_memory();
01697     char const *s = memory[variable].c_str();
01698     if (s && s[0])
01699     {
01700         int i;
01701         int len = strlen(s);
01702         char *data = new char[len / 2];
01703         for (i = 0; i < len; i += 2)
01704         {
01705             int v1 = s[i] - '0';
01706             int v2 = s[i + 1] - '0';
01707             if (v1 > 9) v1 += 10 + '0' - 'a';
01708             if (v2 > 9) v2 += 10 + '0' - 'a';
01709             char c = v1 * 16 + v2;
01710             data[i / 2] = c;
01711         }
01712         PyObject *ret = PyMarshal_ReadObjectFromString(data, len / 2);
01713         delete[] data;
01714         return ret;
01715     }
01716     Py_INCREF(Py_None);
01717     return Py_None;
01718 }
01719 
01720 PyObject* python_ai::wrapper_get_version(PyObject* /*self*/, PyObject* args)
01721 {
01722     if (!PyArg_ParseTuple(args, NOVALUE))
01723         return NULL;
01724     return Py_BuildValue(STRINGVALUE, game_config::version.c_str());
01725 }
01726 
01727 PyObject* python_ai::wrapper_raise_user_interact(PyObject* /*self*/, PyObject* args)
01728 {
01729     if (!PyArg_ParseTuple(args, NOVALUE))
01730         return NULL;
01731 
01732     wrap(
01733         running_instance->raise_user_interact();
01734     )
01735 
01736     Py_INCREF(Py_None);
01737     return Py_None;
01738 }
01739 
01740 PyObject* python_ai::wrapper_test_move(PyObject* /*self*/, PyObject* args)
01741 {
01742     wesnoth_location* from;
01743     // loc initialises to x=-1000, y=-1000. i.e. temporarily moves the unit off the map
01744     gamemap::location loc;
01745     wesnoth_location* to = reinterpret_cast<wesnoth_location*>(wrap_location(loc));
01746 
01747     if (!PyArg_ParseTuple(args, CC("O!|O!"), &wesnoth_location_type, &from,
01748         &wesnoth_location_type, &to))
01749         return NULL;
01750 
01751     // Trivial case, just return the current move_maps
01752     if (*from->location_ == *to->location_) {
01753         // PyTuple_New creates a new reference (which we will return)
01754         PyObject* ret = PyTuple_New(2);
01755 
01756         /* wrap_move_map creates a new reference, PyTuple_SetItem takes away
01757          * a reference - so this should cancel out and we don't need to
01758          * change reference counts ourselves.
01759          */
01760         PyTuple_SetItem(ret, 0, wrap_move_map(running_instance->enemy_src_dst_));
01761         PyTuple_SetItem(ret, 1, wrap_move_map(running_instance->enemy_dst_src_));
01762 
01763         return ret;
01764     }
01765 
01766     info& inf = running_instance->get_info();
01767 
01768     unit_map::iterator u_it = inf.units.find(*from->location_);
01769     if (u_it == inf.units.end()) return_none;
01770 
01771     // Temporarily move our unit to the specified location, storing any
01772     // unit that might happen to be there already.
01773     std::pair<gamemap::location,unit> *temp = inf.units.extract(*to->location_);
01774     std::pair<gamemap::location,unit> *backup = temp;
01775     std::pair<gamemap::location,unit> *original = inf.units.extract(*from->location_);
01776     std::pair<gamemap::location,unit> test(*to->location_, u_it->second);
01777     inf.units.add(&test);
01778 
01779     ai_interface::move_map test_src_dst;
01780     ai_interface::move_map test_dst_src;
01781     std::map<gamemap::location, paths> possible_moves;
01782 
01783     running_instance->calculate_moves(inf.units, possible_moves, test_src_dst, test_dst_src, true);
01784 
01785     // Restore old positions again
01786     temp = inf.units.extract(*to->location_);
01787     inf.units.add(original);
01788     if (backup)
01789         inf.units.add(backup);
01790 
01791     PyObject* srcdst = wrap_move_map(test_src_dst);
01792     PyObject* dstsrc = wrap_move_map(test_dst_src);
01793     // possible_moves not used
01794 
01795     PyObject* ret = PyTuple_New(2);
01796     PyTuple_SetItem(ret, 0, srcdst);
01797     PyTuple_SetItem(ret, 1, dstsrc);
01798 
01799     return ret;
01800 }
01801 
01802 static PyMethodDef wesnoth_python_methods[] = {
01803     MDEF("log_message", python_ai::wrapper_log_message,
01804         "Parameters: string\n"
01805         "Logs a message, displayed as a chat message, if debug is enabled." )
01806     MDEF("log", python_ai::wrapper_log,
01807         "Parameters: string\n"
01808         "Writes a debug message to the AI log. This should be used instead of "
01809         "print to not clutter stdout if AI logging is disabled.")
01810     MDEF("get_units", python_ai::wrapper_get_units,
01811         "Returns: units\n"
01812         "Returns a dictionary containing (location, unit) pairs.")
01813     MDEF("get_location", python_ai::wrapper_get_location,
01814         "Parameters: x, y\n"
01815         "Returns: location\n"
01816         "Returns a wesnoth.location object pointing to position (x, y) on the map.")
01817     MDEF("get_map", python_ai::wrapper_get_map,
01818         "Returns: map\n"
01819         "Returns a wesnoth.gamemap object representing the current map.")
01820     MDEF("get_teams", python_ai::wrapper_get_teams,
01821         "Returns: teams\n"
01822         "Returns a list containing wesnoth.team objects, representing the current teams in the game.")
01823     MDEF("get_current_team", python_ai::wrapper_get_current_team,
01824         "Returns: team\n"
01825         "Returns a wesnoth.team object representing the current team.")
01826     MDEF("get_destinations_by_unit", python_ai::wrapper_get_src_dst,
01827         "Returns: moves\n"
01828         "Returns a dictionary. Keys are wesnoth.location objects pointing to player's units, "
01829         "values are lists of possible destinations for this unit.")
01830     MDEF("get_units_by_destination", python_ai::wrapper_get_dst_src,
01831         "Returns: moves\n"
01832         "Returns a dictionary. Keys are wesnoth.location objects pointing to positions the "
01833         "player's units can reach, values are lists of locations where units that can reach "
01834         "this position are.")
01835     MDEF("get_enemy_destinations_by_unit", python_ai::wrapper_get_enemy_src_dst,
01836         "Returns: moves\n"
01837         "Returns a dictionary. Keys are wesnoth.location objects pointing to player's units, "
01838         "values are lists of possible destinations for this unit.")
01839     MDEF("get_enemy_units_by_destination", python_ai::wrapper_get_enemy_dst_src,
01840         "Returns: moves\n"
01841         "Returns a dictionary. Keys are wesnoth.location objects pointing to positions the "
01842         "enemie's units can reach, values are lists of locations where units that can reach "
01843         "this position are.")
01844     MDEF("move_unit", python_ai::wrapper_move_unit,
01845         "Parameters: location from, location to\n"
01846         "Returns: new_position\n"
01847         "Moves the unit on 'from' to the location specified by 'to', and "
01848         "returns a wesnoth.location object representing the position the unit was moved to.")
01849     MDEF("attack_unit", python_ai::wrapper_attack_unit,
01850         "Parameters: location attacker, location defender, int weapon = -1\n"
01851         "Unit at position 'attacker' attacks unit at position 'defender' with weapon 'weapon'. "
01852         "The weapon parameter is optional, and the same weapon which would be "
01853         "highlighted for a human player is used if it is omitted.")
01854     MDEF("get_adjacent_tiles", python_ai::wrapper_get_adjacent_tiles,
01855         "Parameters: location\n"
01856         "Returns: positions\n"
01857         "Returns a list of wesnoth.location representing tiles adjacent to the specified location.")
01858     MDEF("recruit_unit", python_ai::wrapper_recruit_unit,
01859         "Parameters: string name, location where\n"
01860         "Returns: result\n"
01861         "Recruits the unit of type 'name', at the location 'where'. Returns 1 on success, 0 on failure.")
01862     MDEF("get_gamestatus", python_ai::wrapper_get_gamestatus,
01863         "Returns: status\n"
01864         "Returns a wesnoth.gamestatus object representing the current game status.")
01865     MDEF("set_variable", python_ai::wrapper_set_variable,
01866         "Parameters: variable, value\n"
01867         "Sets a persistent variable 'variable' to 'value'. This can be "
01868         "used to make the AI save strings (and other python values which can "
01869         "be marshalled) over multiple turns.")
01870     MDEF("get_variable", python_ai::wrapper_get_variable,
01871         "Parameters: variable\n"
01872         "Returns: value\n"
01873         "Retrieves a persistent variable 'variable' from the AI, which has "
01874         "previously been set with set_variable - or None if it can't be found.")
01875     MDEF("get_version", python_ai::wrapper_get_version,
01876         "Returns a string containing current Wesnoth version")
01877     MDEF("raise_user_interact", python_ai::wrapper_raise_user_interact,
01878         "Function which should be called frequently to allow the user to interact with the interface.\n"
01879         "This function will make sure that interaction doesn't occur too often, "
01880         "so there is no problem with calling it very regularly.")
01881     MDEF("test_move", python_ai::wrapper_test_move,
01882         "Parameters: from location, to location (optional)\n"
01883         "Returns: (enemy_destinations_by_unit, enemy_units_by_destination) or None\n"
01884         "Returns two dictionaries, representing the possible enemy moves if "
01885         "the unit were to move from 'from location' to 'test location'. "
01886         "If 'to location' is not given, it returns the possible enemy moves ignoring this unit. "
01887         "The results will be the same as if wesnoth.get_enemy_destinations_by_unit() "
01888         "and wesnoth.get_enemy_units_by_destination() were called after actually "
01889         "performing the move.\n"
01890         "Returns None if there is no unit at 'from location'.")
01891         { NULL, NULL, 0, NULL}
01892 };
01893 
01894 
01895 #define Py_Register( x, n ) { \
01896     PyType_Ready(&x); \
01897     Py_INCREF(&x); \
01898     PyTypeObject *type = &x; \
01899     PyObject* pyob = reinterpret_cast<PyObject *>(type); \
01900     PyModule_AddObject(module, CC(n), pyob); }
01901 
01902 void python_ai::initialize_python()
01903 {
01904     if (init_) return;
01905         init_ = true;
01906 
01907     Py_Initialize( );
01908     PyObject* module = Py_InitModule3(CC("wesnoth"), wesnoth_python_methods,
01909         CC("This is the wesnoth AI module. "
01910         "The python script will be executed once for each turn of the side with the "
01911         "python AI using the script."));
01912     Py_Register(wesnoth_unit_type, "unit");
01913     Py_Register(wesnoth_location_type, "location");
01914     Py_Register(wesnoth_gamemap_type, "gamemap");
01915     Py_Register(wesnoth_unittype_type, "unittype");
01916     Py_Register(wesnoth_team_type, "team");
01917     Py_Register(wesnoth_attacktype_type, "attacktype");
01918     Py_Register(wesnoth_gamestatus_type, "gamestatus");
01919 }
01920 
01921 /***
01922  * Invoke the named python script using Wesnoth's builtin Python interpreter.
01923  */
01924 void python_ai::invoke(std::string name)
01925 {
01926     initialize_python();
01927     PyErr_Clear();
01928     PyObject* globals = PyDict_New();
01929     PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins());
01930     std::string python_code;
01931     python_code +=
01932         "import sys\n"
01933         "backup = sys.path[:]\n"
01934         "sys.path.append(\"" + game_config::path + "/data/ais\")\n"
01935         "sys.path.append(\"" + game_config::path + "/data/tools/wesnoth\")\n"
01936         "try:\n"
01937         "\timport " + name + "\n"
01938         "finally:\n"
01939         "\tsys.path = backup\n";
01940     PyObject *ret = PyRun_String(python_code.c_str(), Py_file_input, globals,
01941         globals);
01942     Py_XDECREF(ret);
01943     Py_DECREF(globals);
01944 }
01945 
01946 python_ai::python_ai(ai_interface::info& info) : ai_interface(info), exception(QUIT)
01947 {
01948     LOG_AI << "Running Python instance.\n";
01949     running_instance = this;
01950     initialize_python();
01951     calculate_possible_moves(possible_moves_,src_dst_,dst_src_,false);
01952     calculate_possible_moves(enemy_possible_moves_,enemy_src_dst_,enemy_dst_src_,true);
01953 }
01954 
01955 python_ai::~python_ai()
01956 {
01957     // This is called whenever the AI is destroyed after its turn -
01958     // the Python interpreter itself will be auto cleaned up at program exit.
01959     LOG_AI << "Closing Python instance.\n";
01960     running_instance = NULL;
01961 }
01962 
01963 void python_ai::play_turn()
01964 {
01965     game_events::fire("ai turn");
01966 
01967     std::string script_name = current_team().ai_parameters()["python_script"];
01968     if (script_name.substr(script_name.length() - 3) != ".py") {
01969         // Make sure the script ends in .py here -
01970         // Wesnoth will not execute any other files.
01971         std::cerr << "\"" << script_name << "\" is not a valid script name.\n";
01972         return;
01973     }
01974     std::string script = get_binary_file_location("data", "ais/" + script_name);
01975 
01976     PyErr_Clear();
01977 
01978     PyObject* globals = PyDict_New();
01979     PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins());
01980 
01981     std::string path(".");
01982     if (!game_config::path.empty()) path = game_config::path;
01983     LOG_AI << "Executing Python script \"" << script << "\".\n";
01984     LOG_AI << "Python path: \"" << path << "/data/ais" << "\"\n";
01985     // Run the python script. We actually execute a short inline python script,
01986     // which sets up the module search path to the data path,
01987     // runs the script, and then resets the path.
01988     std::string python_code;
01989     python_code +=
01990         "err = \"unknown error\"\n"
01991         "try:\n"
01992         "\timport sys, traceback\n"
01993         "\ttry:\n"
01994         "\t\tsys.stderr = file(\"pyerr.txt\", \"wb\")\n"
01995         "\texcept IOError:\n"
01996         "\t\tsys.stderr.write(\"Python: Could not create pyerr.txt in current directory.\\n\")\n"
01997         "\tbackup = sys.path[:]\n"
01998         "\tsys.path.append(\"" + path + "/data/ais\")\n"
01999         "\tsys.path.append(\"data/ais\")\n"
02000         "\ttry:\n"
02001         "\t\timport parse, safe\n"
02002         "\t\tparse.pathes = [\"" + path + "\"]\n"
02003         "\t\tcode, context = parse.parse(\"" + script + "\")\n"
02004         "\t\tsafe.safe_exec(code, context)\n"
02005         "\texcept:\n"
02006         "\t\terr = str(traceback.format_exc())\n"
02007         "\t\traise\n"
02008         "finally:\n"
02009         "\tsys.path = backup\n";
02010     PyObject *ret = PyRun_String(python_code.c_str(), Py_file_input,
02011         globals, globals);
02012 
02013     if (PyErr_Occurred()) {
02014         // RuntimeError is the game-won exception, no need to print it.
02015         // Anything else likely is a mistake by the script author.
02016         if (!PyErr_ExceptionMatches(PyExc_RuntimeError)) {
02017             LOG_AI << "Python script has crashed.\n";
02018             std::cerr << "Python version: " << Py_GetVersion() << "\n";
02019             PyObject *err = PyDict_GetItemString(globals, "err");
02020             std::cerr << (err ? PyString_AsString(err) :
02021                 std::string("internal error (wrong python version?)")) <<
02022                 std::endl;
02023         }
02024         // Otherwise, re-throw the exception here,
02025         // so it will get handled properly further up.
02026         else {
02027             LOG_AI << "Python script has been interrupted.\n";
02028             Py_XDECREF(ret);
02029             Py_DECREF(globals);
02030             throw exception;
02031         }
02032     }
02033     Py_XDECREF(ret);
02034     Py_DECREF(globals);
02035 }
02036 
02037 // Finds all python AI scripts available in the current binary path.
02038 // They have to end with .py, and have #!WPY as first line.
02039 std::vector<std::string> python_ai::get_available_scripts()
02040 {
02041     std::vector<std::string> scripts;
02042     const std::vector<std::string>& paths = get_binary_paths("data");
02043     for(std::vector<std::string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
02044         std::vector<std::string> files;
02045         get_files_in_dir(*i + "ais", &files, NULL, ENTIRE_FILE_PATH);
02046         for(std::vector<std::string>::const_iterator j = files.begin(); j != files.end(); ++j) {
02047             // file ends with .py
02048             if (j->substr(j->length() - 3) == ".py") {
02049                 std::string name(j->substr(j->rfind("/") + 1)); // extract name
02050                 // read first line
02051                 std::ifstream s(j->c_str()); std::string mark; s >> mark; s.close();
02052                 if (mark == "#!WPY" &&
02053                     std::find(scripts.begin(), scripts.end(), name) == scripts.end())
02054                     scripts.push_back(name);
02055             }
02056         }
02057     }
02058     return scripts;
02059 }
02060 
02061 #endif // HAVE_PYTHON

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs