00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
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
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* )
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* ) \
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* )
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,
00234 CC("wesnoth.unittype"),
00235 sizeof(wesnoth_unittype),
00236 0,
00237 0,
00238 0,
00239 0,
00240 0,
00241 reinterpret_cast<cmpfunc>
00242 (unittype_internal_compare),
00243 0,
00244 0,
00245 0,
00246 0,
00247 0,
00248 0,
00249 0,
00250 0,
00251 0,
00252 0,
00253 Py_TPFLAGS_DEFAULT,
00254 CC("Describes a unit type."),
00255 0,
00256 0,
00257 0,
00258 0,
00259 0,
00260 0,
00261 unittype_methods,
00262 0,
00263 unittype_getseters,
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* ) \
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,
00365 CC("wesnoth.attacktype"),
00366 sizeof(wesnoth_attacktype),
00367 0,
00368 reinterpret_cast<destructor>
00369 (wesnoth_attacktype_dealloc),
00370 0,
00371 0,
00372 0,
00373 0,
00374 0,
00375 0,
00376 0,
00377 0,
00378 0,
00379 0,
00380 0,
00381 0,
00382 0,
00383 0,
00384 Py_TPFLAGS_DEFAULT,
00385 CC("Describes an attack type."),
00386 0,
00387 0,
00388 0,
00389 0,
00390 0,
00391 0,
00392 attacktype_methods,
00393 0,
00394 attacktype_getseters,
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* )
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* )
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* )
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* )
00458 {
00459 u_check;
00460 return Py_BuildValue(INTVALUE, unit->unit_->side());
00461 }
00462
00463 static PyObject* unit_movement_left(wesnoth_unit* unit, void* )
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* )
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* )
00476 {
00477 u_check;
00478 return Py_BuildValue(INTVALUE, unit->unit_->attacks_left());
00479 }
00480
00481 static PyObject* unit_hitpoints(wesnoth_unit* unit, void* )
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* )
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* )
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* )
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* )
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* )
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* )
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* )
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
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,
01252 CC("wesnoth.team"),
01253 sizeof(wesnoth_gamestatus),
01254 0,
01255 0,
01256 0,
01257 0,
01258 0,
01259 0,
01260 0,
01261 0,
01262 0,
01263 0,
01264 0,
01265 0,
01266 0,
01267 0,
01268 0,
01269 0,
01270 Py_TPFLAGS_DEFAULT,
01271 CC("This class has information about the game status."),
01272 0,
01273 0,
01274 0,
01275 0,
01276 0,
01277 0,
01278 gamestatus_methods,
01279 0,
01280 gamestatus_getseters,
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* , 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* , 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* , 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* , 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* , 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* , 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* , PyObject* )
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* , PyObject* )
01418 {
01419 return wrap_move_map(running_instance->src_dst_);
01420 }
01421
01422 PyObject* python_ai::wrapper_get_dst_src(PyObject* , PyObject* )
01423 {
01424 return wrap_move_map(running_instance->dst_src_);
01425 }
01426
01427 PyObject* python_ai::wrapper_get_enemy_src_dst(PyObject* , PyObject* )
01428 {
01429 return wrap_move_map(running_instance->enemy_src_dst_);
01430 }
01431
01432 PyObject* python_ai::wrapper_get_enemy_dst_src(PyObject* , PyObject* )
01433 {
01434 return wrap_move_map(running_instance->enemy_dst_src_);
01435 }
01436
01437 PyObject* python_ai::wrapper_move_unit(PyObject* , 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* , PyObject* args)
01473 {
01474 wesnoth_location* from;
01475 wesnoth_location* to;
01476 int weapon = -1;
01477 if (!PyArg_ParseTuple( args, CC("O!O!|i"), &wesnoth_location_type, &from,
01478 &wesnoth_location_type, &to, &weapon ) )
01479 return NULL;
01480
01481
01482
01483
01484 if (!tiles_adjacent(*from->location_, *to->location_))
01485 return_none;
01486
01487
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* , 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* , 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* , 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;
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
01618
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
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* , 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* , 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* , 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* , 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* , PyObject* args)
01741 {
01742 wesnoth_location* from;
01743
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
01752 if (*from->location_ == *to->location_) {
01753
01754 PyObject* ret = PyTuple_New(2);
01755
01756
01757
01758
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
01772
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
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
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
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
01958
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
01970
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
01986
01987
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
02015
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
02025
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
02038
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
02048 if (j->substr(j->length() - 3) == ".py") {
02049 std::string name(j->substr(j->rfind("/") + 1));
02050
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