Remove Python AI support, addressing CVE-2009-0367

This commit is contained in:
Daniel Franke 2009-02-24 06:53:37 +00:00
parent 98b9afca18
commit dd1d5064e3
16 changed files with 17 additions and 3472 deletions

View file

@ -15,3 +15,14 @@ The release team should empty this file after each release.
Replays work again for single-player campaign scenarios.
***
This release contains an important security update, fixing a vulnerability
that could allow third-party content (such as campaigns downloaded from the
add-on server) to execute arbitrary code with user account privileges. Consult
CVE-2009-0367 for details. All content currently on the official add-on server
has been inspected to confirm that none of it exploits this vulnerability,
and the add-on server itself has been patched to ensure that exploits can no
longer be uploaded. Therefore, users of previous versions of Battle for Wesnoth
who have received user-made content through the official add-on server and
no other distribution channel need not fear that they have been compromised.

View file

@ -1,4 +1,7 @@
Version 1.4.7+svn:
* campaigns:
* Descent into Darkness:
* Removed the custom python AI used in 'A Haunting in Winter'
* language and i18n:
* updated translations: French, Italian, Japanese
* multiplayer:
@ -9,6 +12,7 @@ Version 1.4.7+svn:
* Additional screenmodes for when SDL can't guess them (patch #1108)
* Fixed bug #12896: Map generator does not sync between clients when
advancing in MP campaigns
* Removed support for PythonAI to handle CVE-2009-0367
Version 1.4.7:
* campaigns:

View file

@ -120,11 +120,6 @@ AC_ARG_ENABLE([static],
[static=$enableval],
[static=no])
AC_ARG_ENABLE([python],
AS_HELP_STRING([--disable-python], [disable Python support]),
[python=$enableval],
[python=yes])
AC_ARG_ENABLE([python_install],
AS_HELP_STRING([--enable-python-install], [enable installation of Python developer tools]),
[python_install=$enableval],
@ -309,12 +304,6 @@ AC_ARG_ENABLE([display-revision],
[svnrev=no])
if test "x$game" = "xno"
then
python=no
AC_MSG_WARN([*** Game build disabled, suppressing Python support.])
fi
if test "x$python" = "xno"
then
python_install=no
@ -480,19 +469,6 @@ if test "x$python" = "xyes"; then
if test "x$pythonfound" = "xyes"; then
AC_MSG_RESULT(yes)
PYTHON_CFLAGS="-DHAVE_PYTHON -I$PYTHON_PREFIX/include/python$PYTHON_VERSION"
OLD_CPPFLAGS="$CPPFLAGS"
OLD_CXXFLAGS="$CXXFLAGS"
CPPFLAGS="$CPPFLAGS $PYTHON_CFLAGS"
CXXFLAGS="$CXXFLAGS $PYTHON_CFLAGS"
AC_CHECK_HEADER([Python.h],
[],
[AC_MSG_WARN([*** Python include files not found! You should install Python development package. Python support disabled]); pythonfound=no])
CPPFLAGS="$OLD_CPPFLAGS"
CXXFLAGS="$OLD_CXXFLAGS"
if test "x$pythonfound" = "xyes"; then
AC_SUBST([PYTHON_CFLAGS])

View file

@ -1,531 +0,0 @@
#!WPY
import wesnoth, random
## Copyright 2006 by Michael Schmahl
## This code is available under the latest version of the GNU Public License.
## See COPYING for details. Some inspiration and code derived from "sample.py"
## by allefant.
##
## This is my attempt at a 'chess-like' AI. All moves are motivated by
## an underlying evaluation function. The actual eval function doesn't
## need to be coded, because moves can be scored and ranked based on the
## incremental change in the evaluation. Unlike a chess-playing program,
## though, this program does no lookahead, because the branching factor
## is prohibitively high (potentially in the thousands), and because then
## the script would have to create an internal model of the game state.
##
## Despite the lack of any lookahead, I still consider this AI to be
## chess-like because it evaluates every possible move and attack, even
## those that are obviously (to a human) bad. How can a computer know
## that these are bad moves unless it actually checks?
##
## The evaluation function is:
##
## (1) side_score = village_score
## + sum(unit_score, over all units)
## + positional_score
##
## The value of a unit can be highly subjective, but to simplify, assume
## that any level-1 unit is just as valuable as any other level-1 unit.
## Specifically, the value of a unit will be:
##
## (2) unit_score = (1 + level + %xp)(1 + %hp)
##
## Leaders are be considered three levels higher than their actual level.
## So a freshly-recruited level-1 unit is worth 4.0 points. And a level-2
## unit with half its hitpoints remaining, but halfway to level 3, is
## worth 6.75 points.
##
## One question is: How much is a village worth, compared to a (typical)
## unit? A typical unit is worth 15 to 20 gold, because that is how much
## we paid for it. A village is worth two or three gold *per turn* as
## long as it is held. (The village is worth three gold when it offsets
## a unit's upkeep.) So we must make some assumptions as to the value of
## a present gold piece, compared to a future gold piece. Assume a decay
## rate of 1.5 (i.e. a gold piece one turn from now is worth two-thirds
## of a gold piece now). This makes the present value of a village equal
## to twice its income. If we set the value of a typical unit at 16 gold,
## we get that an upkeep-offsetting village is worth 1.5 points, and a
## supernumerary village is worth 1.0 points. For simplicity, the value
## of each village is set at 1.0.
##
## (3) village_score = number of villages
##
## The positional score is the most interesting term of equation (1),
## because that, more than anything else, will guide the AI's behavior.
##
## First, we want the AI to expand to capture villages. So, for each unit,
## it is scored based on how far it is from the nearest unowned or enemy
## village. If the distance is zero, the unit has actually captured the
## village, so in that limit, the value should be equal to the village
## value. As the distance approaces infinity, the score should tend
## toward zero. This suggests something like:
##
## (4) village_proximity = c / (c + distance)
##
## I have selected c to be equal to equal to the unit's movement. This
## means that (approximately) a unit one turn away from capturing a village
## gets 0.5 points; two turns, 0.33 points, etc. Although an exponential
## relationship would be more accurate, exponentiation is expensive, and
## better avoided, since thousands of moves are evaluated per turn.
##
## Second, we want units to stand on defensive terrain when within range
## of the enemy. The 'right' way to do this would be to count up all the
## potential attackers at the destination square, see how much damage they
## might do, and score the move based on how much damage would be dealt/
## prevented. Again, this is much too slow. I have found a reasonable
## approximation is:
##
## (5) exposure_penalty = -defense_modifier / 10
##
## Maybe much too simple, but easy to calculate! In future editions, perhaps
## I should take into account how damaged the unit is, or at least make some
## attempt to count the attackers.
##
## Third, we want units to heal when damaged or poisoned. Referring to
## equation (2), we can see that the value of healing is:
##
## (6) healing_score = healing / max_hitpoints * (1 + level + %xp)
##
## We consider poison, which does 8 damage *per turn*, to be equivalent to
## 16 points of actual damage, for the same reason a village's real value is
## twice its income (see above).
##
## Fourth, we want units to guard villages if the enemy is in range to take
## them. If, by stationing a unit on a village, we prevent the enemy from
## taking it, we have prevented a 2-point swing in the enemy's favor. Again
## considering a decay rate of 2/3 per turn, this means the garrison value
## is 4/3. But since there is no guarantee that our garrison will be
## successful (perhaps the enemy will take the village anyway; perhaps it is
## not possible to garrison all threatened villages), we will cut this in half.
##
## (7) garrison_score = 2/3
##
## Fifth, we want our leader to stay near a keep. Otherwise, any gold we
## might have accumulated will be wasted. And finally, we want units to move
## toward the enemy leader. These are accomplished by treating keeps as
## if they were unowned villages (for our leader), and the enemy leader
## as if it were a village (for everyone else).
##
## This should be all that is required to play a decent game of Wesnoth.
## This AI scores quite well against the Wesnoth default AI, which may be
## surprising, because it uses no sophisticated tools. There is no attempt
## to use any of the path-finding tools made available by the API (which
## would be too slow to be used thousands of times every turn). There is
## no attempt to use combination attacks (meaning, that even though none of
## several units can favorably attack a certain target, if they all attack
## in the same turn, the result is likely to be favorable). No attempt is
## made to assign units individually to targets.
##
## Some bad behaviors may result from these shortcomings:
##
## If the map is maze-like, or simply has a few corners surrounded by
## impassable terrain, units may get stuck. On Cynsaun Battlefield, for
## example, a group of units got stuck in the middle of the river, trying
## to capture a village on the other side of the deep-water hexes.
##
## An enemy unit may get completely surrounded by friendly units, who are
## weak in comparison to the enemy, and our AI will make no attempt to kill
## the enemy unit. (Think six Wolf Riders surrounding an Orcish Grunt.)
## Usually one or more of these units will find something else to do, allowing
## a few Archers to take their place and start to wear down the Grunt. Or
## the Grunt will attack, getting damaged in the process, and creating a
## chance-to-kill for one of the Wolves.
##
## If there is an unoccupied village in a corner of the map, our AI will
## send every unit that is closer to the village than any other, to that
## village. Often, only one unit is necessary. Thus, harassing villages
## with scouts may be a much more viable strategy against this AI than
## against human players, or against the default AI.
##
## For those interested in results, I have set up a tournament between my
## AI and the default AI. The tournament consists of one match on each of
## the mainline two-player maps (except Wesbowl, naturally). In each map,
## each opponent is allowed to be player 1 once. If there is no decision
## after two games, two more games are played, repeating as necessary until
## one opponent has won the match. All games are played with a 50-turn
## limit, 2 gold per village, 70% experience, and no fog. (I think there
## is a bug (feature?) that AIs ignore fog, so I disabled it to improve the
## observer's (my) experience.) Factions are chosen randomly.
##
## Map W-L-D %Win Match result
## Blitz 2-0-0 100 Win
## Caves of the Basilisk 4-2-0 67 Win
## Charge 3-1-0 75 Win
## Cynsaun Battlefield (1gpv) 2-0-0 100 Win
## Den of Onis 4-2-0 67 Win
## Hamlets 2-0-0 100 Win
## Hornshark Island 0-2-0 0 Loss
## Meteor Lake 2-0-0 100 Win
## Sablestone Delta 2-0-0 100 Win
## Silverhead Crossing 3-1-0 75 Win
## Sulla's Ruins 2-0-0 100 Win
## ** Overall 25-8-0 76 10 Wins, 1 Loss (91%)
# UNIT SCORE MODIFIERS
BASE_UNIT_SCORE = 1 # Base worth of a unit
LEVEL_SCORE = 1 # Worth/level
LEADER_SCORE = 3 # Leader worth
FULL_XP_SCORE = 1 # How much is partial XP worth (1 is 100% XP = 1 pt)
# This score is then multiplied by a factor dependant on the price of the unit
# this makes expensive units worth more to the AI
COST_SCORE = 0 #
BASE_COST_SCORE = 1 #
# Formula:
# Base_Score = BASE_UNIT_SCORE + level * LEVEL_SCORE + is_leader * LEADER_SCORE + xp/max_xp * FULL_XP_SCORE
# Cost_Modifier = BASE_COST_SCORE + price * COST_SCORE
# Unit_Score(unit_k) = Base_Score * Cost_Modifier
# POSITION SCORE MODIFIERS
NO_MOVE_PEN = 0 # Penalty for not moving (doesn't quite work)
NEXT_TO_ENEMY_PEN = 0 # Penalty for moving next to an enemy and not attacking
STAND_NEXT_TO_ENEMY_PEN = 0 # Penalty for standing next to an enemy without moving or attacking
# MISC SCORE MODIFIERS
LEVEL_CHANCE_BONUS = 0 # How much a level-up is worth
VILLAGE_SCORE = 1 # How much capturing a village is worth
ENEMY_VILLAGE_BONUS = 1 # How much extra is an enemy village worth
GARRISON_SCORE = 2.0/3 # How much defending a village is worth
DEFENSE_FACTOR = 1.0/1000 # How much to penalize a unit for being in an attackable position
HEAL_FACTOR = 1 # How much is healing worth
HEAL_ATTACKABLE = .5 # How much relative to healing is healing when attackable worth
HEAL_POISON = 16 # How much is healing from poison worth
HP_SCALE = .1 # Unit HP/turn (for recruitment)
def pos(p):
if p==None: return ("Nowhere")
return ("(%s,%s)"%(p.x+1,p.y+1))
class AI:
def __init__(self):
self.get_villages()
self.get_keeps()
self.mapsize = max((wesnoth.get_map().x,wesnoth.get_map().y)) / 30.0
self.stats = [0,0]
def report_stats(self):
wesnoth.log_message("%d moves, %d fights evaluated" % (self.stats[0],self.stats[1]))
def get_villages(self):
self.notmyvillages = []
m = wesnoth.get_map()
for x in range(m.x):
for y in range(m.y):
loc = wesnoth.get_location(x,y)
if m.is_village(loc):
for team in wesnoth.get_teams():
if team.owns_village(loc) and not team.is_enemy:
break
else:
self.notmyvillages.append(loc)
def get_keeps(self):
self.keeps = []
m = wesnoth.get_map()
for x in range(m.x):
for y in range(m.y):
loc = wesnoth.get_location(x,y)
if m.is_keep(loc):
# If the enemy is occupying the keep, it is "off-limits" to our leader.
# Otherwise, if our leader has strayed too far, it might attempt to go
# to the enemy keep, which basically means we lose.
if loc not in wesnoth.get_enemy_destinations_by_unit().keys():
self.keeps.append(loc)
def recruit(self):
# I haven't discussed this at all. Perhaps a few paragraphs would be in order.
if wesnoth.get_current_team().gold < 16: return
# find our leader
leaderpos = None
for location,unit in wesnoth.get_units().iteritems():
if unit.can_recruit and unit.side == wesnoth.get_current_team().side:
leaderpos = location
break
# no leader? can't recruit
if leaderpos == None: return
# is our leader on a keep? If not, move to a keep
# Maybe should always go to nearest keep
if not leaderpos in self.keeps:
for dest in wesnoth.get_destinations_by_unit().get(leaderpos,[]):
if dest in self.keeps:
leaderpos = wesnoth.move_unit(leaderpos,dest)
break
# is our leader on a keep now? If not, can't recruit
if leaderpos not in self.keeps: return
# build up a list of recruits and scores for each
recruit_list = []
sumweights = 0
for recruit in wesnoth.get_current_team().recruits():
weight = self.recruit_score(recruit)
if weight < 0.01: weight = 0.01
recruit_list.append((recruit.name,weight))
sumweights += weight
# repeatedly recruit until we fail
while 1:
# pick a random recruit in proportion to the weights
r = random.uniform(0,sumweights)
for recruit,weight in recruit_list:
r -= weight
if r < 0: break
# just use leaderpos for the location; wesnoth will always
# recruit on the nearest adjacent tile
if not wesnoth.recruit_unit(recruit,leaderpos): break
def map_score(self,recruit):
# calculate average speed in hexes/turn
# and average defense in effective hp
m = wesnoth.get_map()
n = m.x * m.y
speed = 0.0
defense = 0.0
for x in range(m.x):
for y in range(m.y):
loc = wesnoth.get_location(x,y)
speed += 1.0 / recruit.movement_cost(loc)
defense += 100.0 / recruit.defense_modifier(loc) - 1
# speed is more important on larger maps
speed *= self.mapsize * recruit.movement / n
# scaled down because effective hp is over the lifetime of the unit,
# while other scores are based on per-turn quantities
defense *= HP_SCALE * recruit.hitpoints / n
return speed,defense
def combat_score(self,recruit):
# combat advantage, in hp/turn, averaged over all enemy units
tot = 0.0
n = 0
for loc,enem in wesnoth.get_units().iteritems():
if not enem.is_enemy: continue
n += 1
tot += self.combat_advantage(recruit,enem)
tot -= self.combat_advantage(enem,recruit)
return tot/n
def combat_advantage(self,attacker,defender):
# combat advantage for attacker attacking defender
best = 0.0
for weapon in attacker.attacks():
damage = weapon.damage * weapon.num_attacks * defender.damage_from(weapon) / 100.0
best_retal = 0.0
for retaliation in defender.attacks():
if weapon.range == retaliation.range:
retal = retaliation.damage * retaliation.num_attacks * attacker.damage_from(retaliation) / 100.0
if retal > best_retal: best_retal = retal
damage -= best_retal
if damage > best: best = damage
# scale down because not every attack hits
return best/2
def recruit_score(self,recruit):
speed,defense = self.map_score(recruit)
combat = self.combat_score(recruit)
rval = (speed + defense + combat)/recruit.cost
# only report "interesting" results
if rval > 0:
wesnoth.log_message("%s: (%.2f + %.2f + %.2f) / %d = %.3f" % (recruit.name,speed,defense,combat,recruit.cost,rval))
return rval
def do_one_move(self):
enemlocs = wesnoth.get_enemy_destinations_by_unit().keys()
self.enemdests = wesnoth.get_enemy_units_by_destination().keys()
bestmove = (0,None,None,None) # score,orig,dest,target
# find the best move
for orig in wesnoth.get_destinations_by_unit().keys():
# get a baseline score for this unit "standing pat"
base_score = self.eval_move(orig,orig)
for dest in wesnoth.get_destinations_by_unit()[orig]:
# Bug workaround -- if we have recruited this turn,
# get_destinations_by_unit() is incorrect
if dest in wesnoth.get_units().keys() and dest != orig: continue
score = self.eval_move(orig,dest) - base_score
if score > bestmove[0]:
bestmove = (score,orig,dest,dest)
for target in wesnoth.get_adjacent_tiles(dest):
if target in enemlocs:
fight = self.eval_fight(wesnoth.get_units()[orig],dest,target)+score
if orig == dest:
fight += STAND_NEXT_TO_ENEMY_PEN + NO_MOVE_PEN
else:
fight += NEXT_TO_ENEMY_PEN
if fight > bestmove[0]:
bestmove = (fight,orig,dest,target)
if bestmove[1] == None:
# no move improved the position, therefore we are done
return False
score,orig,dest,target = bestmove
wesnoth.log_message("%.3f: %s->%s@%s"%(score,pos(orig),pos(dest),pos(target)))
if dest != orig: wesnoth.move_unit(orig,dest)
if dest in self.notmyvillages: self.notmyvillages.remove(dest)
if target != dest: wesnoth.attack_unit(dest,target)
return True
def eval_fight(self,unit,dest,target):
self.stats[1] += 1
enem = wesnoth.get_units().get(target,None)
if not enem: return 0
# the base value for each unit:
# I should give more weight to defeating a garrison
unit_k = (LEVEL_SCORE*unit.type().level + BASE_UNIT_SCORE + LEADER_SCORE*unit.can_recruit\
+ FULL_XP_SCORE * unit.experience * 1.0 / unit.max_experience) * (BASE_COST_SCORE + unit.type().cost * COST_SCORE)
enem_k = (LEVEL_SCORE*enem.type().level + BASE_UNIT_SCORE + LEADER_SCORE*enem.can_recruit\
+ FULL_XP_SCORE * enem.experience * 1.0 / enem.max_experience) * (BASE_COST_SCORE + enem.type().cost * COST_SCORE)
unit_hp,enem_hp = unit.attack_statistics(dest,target)
score = 0.0
for hp,p in enem_hp.iteritems():
score += p * (enem.hitpoints - hp) * enem_k / enem.max_hitpoints
if hp<=0: score += p * enem_k
for hp,p in unit_hp.iteritems():
score -= p * (unit.hitpoints - hp) * unit_k / unit.max_hitpoints
if hp<=0: score -= p * unit_k
enem_xp = 8*enem.type().level
if enem.type().level == 0:
enem_xp = 4
unit_xp = 8*unit.type().level
if unit.type().level == 0:
unit_xp = 4
if enem.type().level >= unit.max_experience - unit.experience:
for hp, p in unit_hp.iteritems():
if hp > 0: score += LEVEL_CHANCE_BONUS * p * unit_k
elif enem_xp >= unit.max_experience - unit.experience:
for hp, p in enem_hp.iteritems():
if hp <= 0: score += LEVEL_CHANCE_BONUS * p * unit_k
if unit.type().level >= enem.max_experience - enem.experience:
for hp, p in enem_hp.iteritems():
if hp > 0: score -= LEVEL_CHANCE_BONUS * p * enem_k
elif unit_xp >= enem.max_experience - enem.experience:
for hp, p in unit_hp.iteritems():
if hp <= 0: score += LEVEL_CHANCE_BONUS * p * enem_k
return score
def eval_move(self,orig,dest):
enemlocs = wesnoth.get_enemy_destinations_by_unit().keys()
self.stats[0] += 1
score = 0.0
unit = wesnoth.get_units().get(orig,None)
if not unit: return
unit_k = (LEVEL_SCORE*unit.type().level + BASE_UNIT_SCORE + LEADER_SCORE*unit.can_recruit\
+ FULL_XP_SCORE * unit.experience * 1.0 / unit.max_experience) * (BASE_COST_SCORE + unit.type().cost * COST_SCORE)
# subtract 1 because terrain might be a factor
speed = unit.type().movement - 1
attackable=False
if dest in self.enemdests:
attackable = True
else:
for adj in wesnoth.get_adjacent_tiles(dest):
if adj in self.enemdests:
attackable = True
break
# capture villages
if dest in self.notmyvillages:
score += VILLAGE_SCORE
for team in wesnoth.get_teams():
if team.owns_village(dest) and team.is_enemy:
score += ENEMY_VILLAGE_BONUS
bestdist=100
if unit.can_recruit:
# leader stays near keep
for keep in self.keeps:
dist=dest.distance_to(keep)
if dist<bestdist:
bestdist=dist
if dist<=1: break
else:
# everyone else moves toward enemy leader
for loc,enem in wesnoth.get_units().iteritems():
if enem.is_enemy and enem.can_recruit:
dist=dest.distance_to(loc)
if dist<bestdist:
bestdist=dist
if dist<=1: break
if bestdist > 1:
for vil in self.notmyvillages:
if dest==vil: continue
dist=dest.distance_to(vil)
if dist<bestdist:
bestdist=dist
if dist<=1: break
score += (1.0 * speed) / (bestdist + speed)
# healing
# I am ignoring the value of healers, and regenerating units. I don't think unit abilities
# are correctly reported by the API, anyway.
if (unit.poisoned or unit.hitpoints<unit.max_hitpoints) and wesnoth.get_map().is_village(dest):
if unit.poisoned: healing = HEAL_POISON
else:
healing = unit.max_hitpoints-unit.hitpoints
if healing > 8: healing = 8
# reduce the healing bonus if we might get killed first
if attackable: healing *= HEAL_ATTACKABLE
score += HEAL_FACTOR * healing * unit_k / unit.max_hitpoints
if attackable:
# defense
score -= unit.defense_modifier(dest) * DEFENSE_FACTOR
# garrison
if wesnoth.get_map().is_village(dest): score += GARRISON_SCORE
# reduce chances of standing next to a unit without attacking for a whole turn
if dest == orig:
score -= NO_MOVE_PEN
for target in wesnoth.get_adjacent_tiles(dest):
if target in enemlocs:
score -= STAND_NEXT_TO_ENEMY_PEN
break
else:
for target in wesnoth.get_adjacent_tiles(dest):
if target in enemlocs:
score -= NEXT_TO_ENEMY_PEN
break
# end mod
return score
ai = AI()
ai.recruit()
while 1:
if not ai.do_one_move():
break
ai.recruit()
ai.report_stats()

View file

@ -1,91 +0,0 @@
#!/usr/bin/python
# This is *not* a python AI, it's just run as AI so it can get access to
# Python's runtime documentation. This documentation then simply is dumped to
# stdout in a format ready to be pasted to the wiki.
def myhelp(topic, topics):
"""Collect all the help topics into the given list."""
doc = getattr(eval(topic), "__doc__")
subtopics = []
for subtopic in getattr(eval(topic), "__dict__", []):
if subtopic.startswith("_"): continue
myhelp(topic + "." + subtopic, subtopics)
tc = getattr(eval(topic), "__class__", None)
tt = getattr(tc, "__name__", None)
if topic != "wesnoth.error":
topics.append((topic, tt, doc, subtopics))
def output(topics, level):
"""Output the given topics in wiki format, in a given heading level."""
color = 0
topics.sort()
for topic, tt, doc, subtopics in topics:
dot = topic.rfind(".")
if level == 1:
print "==", topic[dot + 1:], "module reference =="
print "''This is an automatically generated reference, but feel " +\
"free to edit it - changes will not be overwritten but " +\
"instead reviewed and included in the next version.''"
print doc or "..."
if subtopics:
funcs = []
others = []
for s in subtopics:
if s[1] == "builtin_function_or_method":
funcs.append(s)
else:
others.append(s)
if funcs:
print "=== Functions ==="
print "{|"
output(funcs, 3)
print "|}"
output(others, 2)
elif level == 2:
print "===", topic[dot + 1:], "==="
print doc or "..."
if subtopics:
print "{|"
output(subtopics, 3)
print "|}"
elif level == 3:
options = " valign='top'"
if color: options += " bgcolor='#FBF5EA'"
print "|-" + options
color = not color
if tt in ["method_descriptor", "builtin_function_or_method"]:
suffix = ""
prefix = ""
if doc and doc.startswith("Parameters:"):
l = doc.find("\n")
if l == -1: l = len(doc) - 1
suffix = "(" + doc[11:l].strip() + ")"
doc = doc[l + 1:]
else:
suffix = "()"
if doc and doc.startswith("Returns:"):
l = doc.find("\n")
if l == -1: l = len(doc) - 1
prefix = doc[8:l].strip() + " = "
doc = doc[l + 1:]
print "|'''%s()'''" % topic[dot + 1:]
print "|<code>%s%s%s</code>\n\n" % (prefix, topic[dot + 1:], suffix) +\
(doc and doc.replace("\n", "\n\n") or "...")
else:
print "|'''%s'''\n|%s" % (topic[dot + 1:],
(doc and doc.replace("\n", "\n\n") or "..."))
if __name__ == "__main__":
import os
# If we are run as script, run wesnoth with the --python-api switch.
os.system("src/wesnoth --python-api")
else:
# If we are run as a python script, output the documentation to stdout.
import wesnoth
topics = []
myhelp("wesnoth", topics)
output(topics, 1)

View file

@ -1,50 +0,0 @@
import re, os, safe
whitelisted = ["wesnoth", "heapq", "random", "math", "string", "re"]
rex = re.compile(r"^import\s+(.*)", re.M)
modules = {}
def include(matchob):
"""
Regular expression callback. Handles a single import statement, returning
the included code.
"""
names = [x.strip() for x in matchob.group(1).split(",")]
r = ""
for name in names:
if name in whitelisted:
modules[name] = __import__(name)
continue
for path in pathes:
includefile = os.path.join(path, name)
try:
code = parse_file(includefile + ".py")
break
except IOError:
pass
else:
raise safe.SafeException("Could not include %s." % name)
return None
r += code
return r
def parse_file(name):
"""
Simple pre-parsing of scripts, all it does is allow importing other scripts.
"""
abspath = os.path.abspath(name)
if abspath in already: return ""
already[abspath] = 1
code = file(abspath).read().replace(chr(13), "")
code = rex.sub(include, code)
return code
# If you want to disable safe python, use this instead:
#
# def parse(name): return open(name).read(), {}
def parse(name):
global already, modules
already = {}
modules = {}
return parse_file(name), modules

View file

@ -1,134 +0,0 @@
"""An attempt at creating a safe_exec for python.
This file is public domain and is not suited for any serious purpose.
This code is not guaranteed to work. Use at your own risk!
Beware! Trust no one!
Please e-mail philhassey@yahoo.com if you find any security holes.
svn://www.imitationpickles.org/pysafe/trunk
See README.txt, NOTES.txt, CHANGES.txt for more details.
"""
import compiler
import __builtin__
class SafeException(Exception):
"""Base class for Safe Exceptions"""
def __init__(self,*value):
self.value = str(value)
def __str__(self):
return self.value
class CheckNodeException(SafeException):
"""AST Node class is not in the whitelist."""
pass
class CheckStrException(SafeException):
"""A string in the AST looks insecure."""
pass
class RunBuiltinException(SafeException):
"""During the run a non-whitelisted builtin was called."""
pass
_NODE_CLASS_OK = [
'Add', 'And', 'AssAttr', 'AssList', 'AssName', 'AssTuple',
'Assert', 'Assign','AugAssign', 'Bitand', 'Bitor', 'Bitxor', 'Break',
'CallFunc', 'Class', 'Compare', 'Const', 'Continue',
'Dict', 'Discard', 'Div', 'Ellipsis', 'Expression', 'FloorDiv',
'For', 'Function', 'Getattr', 'If', 'Keyword',
'LeftShift', 'List', 'ListComp', 'ListCompFor', 'ListCompIf', 'Mod',
'Module', 'Mul', 'Name', 'Node', 'Not', 'Or', 'Pass', 'Power',
'Print', 'Printnl', 'Return', 'RightShift', 'Slice', 'Sliceobj',
'Stmt', 'Sub', 'Subscript', 'Tuple', 'UnaryAdd', 'UnarySub', 'While',
]
_NODE_ATTR_OK = []
_STR_OK = ['__init__']
_STR_NOT_CONTAIN = ['__']
_STR_NOT_BEGIN = ['im_','func_','tb_','f_','co_',]
## conservative settings
#_NODE_ATTR_OK = ['flags']
#_STR_NOT_CONTAIN = ['_']
#_STR_NOT_BEGIN = []
def _check_node(node):
if node.__class__.__name__ not in _NODE_CLASS_OK:
raise CheckNodeException(node.lineno,node.__class__.__name__)
for k,v in node.__dict__.items():
if k in _NODE_ATTR_OK: continue
if v in _STR_OK: continue
if type(v) not in [str,unicode]: continue
for s in _STR_NOT_CONTAIN:
if s in v: raise CheckStrException(node.lineno,k,v)
for s in _STR_NOT_BEGIN:
if v[:len(s)] == s: raise CheckStrException(node.lineno,k,v)
for child in node.getChildNodes():
_check_node(child)
def _check_ast(code):
ast = compiler.parse(code)
_check_node(ast)
_BUILTIN_OK = [
'__debug__','quit','exit',
'Warning',
'None','True','False',
'abs', 'bool', 'callable', 'cmp', 'complex', 'dict', 'divmod', 'filter',
'float', 'frozenset', 'hex', 'int', 'isinstance', 'issubclass', 'len',
'list', 'long', 'map', 'max', 'min', 'object', 'oct', 'pow', 'range',
'repr', 'round', 'set', 'slice', 'str', 'sum', 'tuple', 'xrange', 'zip',
]
_BUILTIN_STR = [
'copyright','credits','license','__name__','__doc__',
]
def _builtin_fnc(k):
def fnc(*vargs,**kargs):
raise RunBuiltinException(k)
return fnc
_builtin_globals = None
_builtin_globals_r = None
def _builtin_init():
global _builtin_globals, _builtin_globals_r
if _builtin_globals != None: return
_builtin_globals_r = __builtin__.__dict__.copy()
r = _builtin_globals = {}
for k in __builtin__.__dict__.keys():
v = None
if k in _BUILTIN_OK: v = __builtin__.__dict__[k]
elif k in _BUILTIN_STR: v = ''
else: v = _builtin_fnc(k)
r[k] = v
def _builtin_destroy():
_builtin_init()
for k,v in _builtin_globals.items():
__builtin__.__dict__[k] = v
def _builtin_restore():
for k,v in _builtin_globals_r.items():
__builtin__.__dict__[k] = v
def safe_check(code):
"""Check the code to be safe."""
return _check_ast(code)
def safe_run(code,context=None):
"""Exec code with only safe builtins on."""
if context == None: context = {}
_builtin_destroy()
try:
#exec code in _builtin_globals,context
context['__builtins__'] = _builtin_globals
exec code in context
_builtin_restore()
except:
_builtin_restore()
raise
# If you want to disable safe python, use this instead:
#
# def safe_exec(code, context = None): exec code in context
def safe_exec(code, context = None):
"""Check the code to be safe, then run it with only safe builtins on."""
safe_check(code)
safe_run(code,context)

View file

@ -1,396 +0,0 @@
#!WPY
"""This is a rather simple minded example of a python AI."""
import wesnoth, heapq, random
def pos(location):
"""Just a helper function for printing positions in debug messages."""
return "(%d, %d)" % (1 + location.x, 1 + location.y)
def debug(string):
pass
class AI:
"""A class representing our AI."""
def __init__(self):
"""This class is constructed once for each turn of the AI. To get
persistent variables across terms, which also are saved when the game is
saved, use set_variable and get_variable."""
self.team = wesnoth.get_current_team()
self.village_radius = 25
self.scout_villages = 3
self.recruit()
self.fight()
self.conquer()
def conquer(self):
"""Try to capture villages."""
villages = self.find_villages()
units = wesnoth.get_destinations_by_unit().keys()
# Construct a sorted list of (distance, unit, village) triples.
queue = []
for village in villages:
for unit in units:
d = self.get_distance(unit, village)
if d != None: heapq.heappush(queue, (d, unit, village))
# Now assign units to villages, and move them.
while queue:
d, unit, village = heapq.heappop(queue)
if unit in units and village in villages:
units.remove(unit)
villages.remove(village)
self.go_to(unit, village)
if not units: break
if not villages: break
def cumulate_damage(self, cumulated, hitpoints, new_damage):
cumulated2 = {}
for already, ap in cumulated.iteritems():
for hp, probability in new_damage.iteritems():
damage = int(already + hitpoints - hp)
cumulated2[damage] = cumulated2.get(damage, 0) + ap * probability
return cumulated2
def danger_estimate(self, unit, where, enemy):
"""Get some crude indication about how unsafe it is for unit to get
attacked by enemy at where."""
scores = []
u = wesnoth.get_units()[unit]
e = wesnoth.get_units()[enemy]
u_defense = u.defense_modifier(wesnoth.get_map(), where)
e_defense = e.defense_modifier(wesnoth.get_map(), enemy)
u_bonus = 100 - (u.type().alignment - 1) * wesnoth.get_gamestatus().lawful_bonus
e_bonus = 100 - (e.type().alignment - 1) * wesnoth.get_gamestatus().lawful_bonus
for attack in e.attacks():
score = attack.damage * attack.num_attacks * e_bonus / 100
score *= u_defense
score *= u.damage_against(attack) / 100
back = []
for retaliation in u.attacks():
if attack.range == retaliation.range:
x = retaliation.damage * retaliation.num_attacks * u_bonus / 100
x *= e_defense
x *= e.damage_against(retaliation) / 100
back.append(x)
if back:
r = max(back)
score -= r
heapq.heappush(scores, score)
return scores[0]
def danger(self, unit, location):
"""Try to estimate danger of moving unit to location."""
attackers = []
for enemy, destinations in wesnoth.get_enemy_destinations_by_unit():
for tile in wesnoth.get_adjacent_tiles(unit):
if tile in destinations:
heuristic = danger_estimate(unitm, location, enemy)
if heuristic > 0:
heapq.heappush(attackers, (-heuristic, enemy, tile))
result = 0
already = {}
while attackers:
danger, enemy, tile = heapq.heappop(attackers)
if not already[enemy] and not already[tile]:
danger = -danger
result += danger
already[enemy] = 1
already[tile] = 1
return result
def fight(self):
"""Attack enemies."""
enemies = wesnoth.get_enemy_destinations_by_unit().keys()
units = wesnoth.get_destinations_by_unit().keys()
# Get a list of all units we can possibly kill and their chance to kill.
# This is just a heuristic, ignoring ZoC and unit placement.
kills = []
for enemy in enemies:
e = wesnoth.get_units()[enemy]
k = {0: 1.0}
for unit, destinations in wesnoth.get_destinations_by_unit().iteritems():
u = wesnoth.get_units()[unit]
for tile in wesnoth.get_adjacent_tiles(enemy):
if tile in destinations:
own_hp, enemy_hp = u.attack_statistics(tile, enemy)
k = self.cumulate_damage(k, e.hitpoints, enemy_hp)
ctk = 0
for damage, p in k.iteritems():
if damage >= e.hitpoints:
ctk += p
if ctk:
heapq.heappush(kills, (-ctk, enemy))
# Now find positions from where own units can attack the to be killed
# enemies.
attacks = []
while kills:
ctk, enemy = heapq.heappop(kills)
e = wesnoth.get_units()[enemy]
ctk = -ctk
for tile in wesnoth.get_adjacent_tiles(enemy):
for unit in wesnoth.get_units_by_destination().get(tile, []):
u = wesnoth.get_units()[unit]
own_hp, enemy_hp = u.attack_statistics(tile, enemy)
score = e.hitpoints - sum([x[0] * x[1] for x in enemy_hp.iteritems()])
score -= u.hitpoints - sum([x[0] * x[1] for x in own_hp.iteritems()])
# This is so if there are two equally good attack
# possibilities, we chose the one on better terrain.
score *= 50 / u.defense_modifier(tile)
heapq.heappush(attacks, (-score, unit, tile, enemy))
#print own_hp, enemy_hp
debug("Score for %s at %s: %s<->%s: %f [%s]" % (u.name,
pos(unit), pos(tile), pos(enemy), score, e.name))
# Now assign units to enemies, and move and attack.
while attacks:
score, unit, tile, enemy = heapq.heappop(attacks)
score = -score
if unit in units and enemy in enemies:
#try:
loc = wesnoth.move_unit(unit, tile)
#except ValueError:
# loc = None
if loc == tile:
e = wesnoth.get_units()[enemy]
wesnoth.attack_unit(tile, enemy)
if not e.is_valid:
enemies.remove(enemy)
units.remove(unit)
if not units: break
def recruit(self):
"""Recruit units."""
# Check if there is any gold left first.
cheapest = min([x.cost for x in self.team.recruits()])
if self.team.gold < cheapest: return
# Find all keeps in the map.
keeps = self.find_keeps()
# Find our leader.
leader = None
for location, unit in wesnoth.get_units().iteritems():
if unit.side == self.team.side and unit.can_recruit:
leader = location
break
# Get number of villages to capture near to the leader.
villages = len([x for x in self.find_villages()
if leader.distance_to(x) < self.village_radius])
units_recruited = int(wesnoth.get_variable("units_recruited") or 0)
def attack_score(u1, u2):
"""Some crude score of u1 attacking u2."""
maxdeal = 0
for attack in u1.attacks():
deal = attack.damage * attack.num_attacks
deal *= u2.damage_from(attack) / 100.0
for defense in u2.attacks():
if attack.range == defense.range:
receive = defense.damage * defense.num_attacks
receive *= u1.damage_from(defense) / 100.0
deal -= receive
if deal > maxdeal: maxdeal = deal
return maxdeal
def recruit_score(recruit, speed, defense, aggression, resistance):
"""Score for recruiting the given unit type."""
need_for_speed = 3 * (villages / self.scout_villages -
units_recruited)
if need_for_speed < 0: need_for_speed = 0
v = speed * need_for_speed + defense * 0.1 + aggression + resistance
v += 1
if v < 1: v = 1
return v
# Try to figure out which units are good in this map.
map = wesnoth.get_map()
recruits = self.team.recruits()
recruits_list = []
for recruit in recruits:
speed = 0.0
defense = 0.0
n = map.x * map.y
for y in range(map.y):
for x in range(map.x):
location = wesnoth.get_location(x, y)
speed += recruit.movement_cost(location)
defense += 100 - recruit.defense_modifier(location)
speed = recruit.movement * n / speed
defense /= n
aggression = 0.0
resistance = 0.0
enemies = wesnoth.get_enemy_destinations_by_unit().keys()
n = len(enemies)
for location in enemies:
enemy = wesnoth.get_units()[location]
aggression += attack_score(recruit, enemy)
resistance -= attack_score(enemy, recruit)
aggression /= n
resistance /= n
debug("%s: speed: %f, defense: %f, aggression: %f, resistance: %f" %
(recruit.name, speed, defense, aggression, resistance))
recruits_list.append((recruit, speed, defense, aggression, resistance))
# Now recruit.
for location, unit in wesnoth.get_units().iteritems():
if unit.side == self.team.side and unit.can_recruit:
keepsort = []
for keep in keeps:
heapq.heappush(keepsort, (location.distance_to(keep), keep))
keep = keepsort[0][1]
self.go_to(location, keep)
for i in range(6): # up to 6 units (TODO: can be more)
# Get a random, weighted unit type from the available.
heap = []
total_v = 0
for r in recruits_list:
v = recruit_score(*r)
v *= v * v
total_v += v
heapq.heappush(heap, (-v, r[0]))
r = random.uniform(0, total_v)
while 1:
v, recruit = heapq.heappop(heap)
debug("%d %d" % (r, v))
r += v
if r <= 0: break
# Try to recruit it on the adjacent tiles
# TODO: actually, it should just use the nearest possible
# location
for position in wesnoth.get_adjacent_tiles(location):
if wesnoth.recruit_unit(recruit.name, position):
break
else:
# was not possible -> we're done
break
units_recruited += 1
wesnoth.set_variable("units_recruited", str(units_recruited))
def find_villages(self):
"""Find all villages which are unowned or owned by enemies."""
villages = []
m = wesnoth.get_map()
for x in range(m.x):
for y in range(m.y):
location = wesnoth.get_location(x, y)
if wesnoth.get_map().is_village(location):
for team in wesnoth.get_teams():
# does it alreadey belong to use or an ally?
if team.owns_village(location) and not team.is_enemy:
break
else:
# no, either it belongs to an enemy or to nobody
villages.append(location)
return villages
def find_keeps(self):
"""Find keep locations."""
keeps = []
m = wesnoth.get_map()
for x in range(m.x):
for y in range(m.y):
location = wesnoth.get_location(x, y)
if wesnoth.get_map().is_keep(location):
keeps.append(location)
return keeps
def get_distance(self, location, target, must_reach = False):
"""Find out how many turns it takes the unit at location to reach target."""
if location == target: return 0
unit = wesnoth.get_units()[location]
path = unit.find_path(location, target, 100)
extra = 0
if not path:
extra = 1
if must_reach: return None
for adjacent in wesnoth.get_adjacent_tiles(target):
# Consider 5 turns worth of movement of this unit.
path = unit.find_path(location, adjacent,
unit.type().movement * 5)
if path: break
else:
return None
l = 0
for location in path:
l += unit.movement_cost(location)
l -= unit.movement_left
l /= unit.type().movement
l += 1 + extra
return l
def attack(self, location, enemy):
"""Attack an enemy unit."""
wesnoth.attack_unit(location, enemy)
def go_to(self, location, target, must_reach = False):
"""Make a unit at the given location go to the given target.
Returns the reached position.
"""
if location == target: return location
# If target is occupied, try to go near it
unit_locations = wesnoth.get_units().keys()
if target in unit_locations:
if must_reach: return location
adjacent = wesnoth.get_adjacent_tiles(target)
targets = [x for x in adjacent if not x in unit_locations]
if targets:
target = targets[0]
else:
return location
# find a path
for l, unit in wesnoth.get_units().iteritems():
if location == l:
path = unit.find_path(location, target, unit.type().movement * 5)
break
else:
return location
if path:
possible_destinations = wesnoth.get_destinations_by_unit().get(location, [])
if must_reach:
if not target in path: return location
if not target in possible_destinations: return location
# find first reachable position in reversed path
path.reverse()
for p in path:
if p in possible_destinations and not p in unit_locations:
location = wesnoth.move_unit(location, p)
return location
return location
AI()

View file

@ -1,68 +0,0 @@
#!WPY
import wesnoth
class AI:
def __init__(self):
self.do()
def do(self):
# loop over all enemy units
for enemy_loc, ed in wesnoth.get_enemy_destinations_by_unit().iteritems():
target_unit = wesnoth.get_units()[enemy_loc]
# see if unit is the leader of player's side
if target_unit.side == 1 and target_unit.can_recruit == 1:
# if so, get adjacent locations
for unit_loc, destinations in wesnoth.get_destinations_by_unit().iteritems():
attacked_flag = False
for destination in destinations:
if destination.adjacent_to(enemy_loc):
wesnoth.move_unit(unit_loc, destination)
wesnoth.attack_unit(destination, enemy_loc)
attacked_flag = True
break
if (not attacked_flag):
new_loc = self.go_to(unit_loc, enemy_loc, False)
if new_loc.adjacent_to(enemy_loc):
wesnoth.attack_unit(new_loc, enemy_loc)
def go_to(self, location, target, must_reach = False):
"""Make a unit at the given location go to the given target.
Returns the reached position.
"""
if location == target: return location
# If target is occupied, try to go near it
unit_locations = wesnoth.get_units().keys()
if target in unit_locations:
if must_reach: return location
adjacent = wesnoth.get_adjacent_tiles(target)
targets = [x for x in adjacent if not x in unit_locations]
if targets:
target = targets[0]
else:
return location
# find a path
for l, unit in wesnoth.get_units().iteritems():
if location == l:
path = unit.find_path(location, target, 1000)
break
else:
return location
if path:
possible_destinations = wesnoth.get_destinations_by_unit().get(location, [])
if must_reach:
if not target in path: return location
if not target in possible_destinations: return location
# find first reachable position in reversed path
path.reverse()
for p in path:
if p in possible_destinations and not p in unit_locations:
location = wesnoth.move_unit(location, p)
return location
return location
AI()

View file

@ -77,11 +77,6 @@
no_leader=yes
fog=no
shroud=no
# kamikaze.py heads straight for the leader of side 1 and attacks
[ai]
ai_algorithm=python_ai
python_script="../campaigns/Descent_Into_Darkness/ais/kamikaze.py"
[/ai]
[/side]
[event]

View file

@ -54,7 +54,6 @@ wesnoth_source = \
ai_dfool.cpp \
ai_attack.cpp \
ai_move.cpp \
ai_python.cpp \
ai_village.cpp \
animated_game.cpp \
attack_prediction.cpp \
@ -325,11 +324,6 @@ AM_CXXFLAGS = -I $(srcdir)/sdl_ttf -I../intl -I$(top_srcdir)/intl @SDL_CFLAGS@
AM_CFLAGS = -I $(srcdir)/sdl_ttf -I../intl -I$(top_srcdir)/intl @SDL_CFLAGS@ -DWESNOTH_PATH=\"$(pkgdatadir)\" \
-DLOCALEDIR=\"$(LOCALEDIR)\" -DHAS_RELATIVE_LOCALEDIR=$(HAS_RELATIVE_LOCALEDIR)
if PYTHON
AM_CXXFLAGS += @PYTHON_CFLAGS@
AM_CFLAGS += @PYTHON_CFLAGS@
endif
if FRIBIDI
AM_CXXFLAGS += -DHAVE_FRIBIDI @FRIBIDI_CFLAGS@
AM_CFLAGS += -DHAVE_FRIBIDI @FRIBIDI_CFLAGS@

View file

@ -20,9 +20,6 @@
#include "ai.hpp"
#include "ai2.hpp"
#include "ai_dfool.hpp"
#ifdef HAVE_PYTHON
#include "ai_python.hpp"
#endif
#include "actions.hpp"
#include "dialogs.hpp"
#include "game_config.hpp"
@ -184,10 +181,6 @@ std::vector<std::string> get_available_ais()
ais.push_back("sample_ai");
//ais.push_back("idle_ai");
ais.push_back("dfool_ai");
#ifdef HAVE_PYTHON
std::vector<std::string> scripts = python_ai::get_available_scripts();
ais.insert(ais.end(), scripts.begin(), scripts.end());
#endif
return ais;
}
@ -208,18 +201,8 @@ ai_interface* create_ai(const std::string& name, ai_interface::info& info)
// return new advanced_ai(info);
else if(name == "ai2")
return new ai2(info);
else if(name == "python_ai")
#ifdef HAVE_PYTHON
return new python_ai(info);
#else
{
LOG_STREAM(err, ai) << "No Python AI support available in this Wesnoth build!\n";
return new ai2(info);
}
#endif
else if(name != "")
LOG_STREAM(err, ai) << "AI not found: '" << name << "'\n";
return new ai(info);
}

File diff suppressed because it is too large Load diff

View file

@ -1,101 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2007 - 2008
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
//! @file ai_python.hpp
//!
#ifndef AI_PYTHON_HPP_INCLUDED
#define AI_PYTHON_HPP_INCLUDED
#include "ai_interface.hpp"
#include "menu_events.hpp"
#undef _POSIX_C_SOURCE // avoids a spurious compiler warning
#include <Python.h>
typedef struct {
PyObject_HEAD
const unit_type* unit_type_;
} wesnoth_unittype;
typedef struct {
PyObject_HEAD
const team* team_;
} wesnoth_team;
typedef struct {
PyObject_HEAD
const unit* unit_;
} wesnoth_unit;
#define W(name) static PyObject *wrapper_##name(PyObject* self, PyObject* args)
class python_ai : public ai_interface
{
public:
python_ai(ai_interface::info& info);
virtual ~python_ai();
virtual void play_turn();
static PyObject* wrapper_unit_movement_cost(wesnoth_unit*, PyObject* args);
static PyObject* wrapper_unit_defense_modifier(wesnoth_unit*, PyObject* args);
static PyObject* wrapper_unittype_movement_cost(wesnoth_unittype*, PyObject* args);
static PyObject* wrapper_unittype_defense_modifier(wesnoth_unittype*, PyObject* args);
W(team_targets);
W(get_units);
W(log_message);
W(get_location);
W(get_map);
W(get_teams);
W(get_current_team);
W(get_src_dst);
W(get_dst_src);
W(get_enemy_src_dst);
W(get_enemy_dst_src);
W(move_unit);
W(attack_unit);
W(get_adjacent_tiles);
W(recruit_unit);
W(get_gamestatus);
W(set_variable);
W(get_variable);
W(get_version);
W(raise_user_interact);
W(test_move);
static PyObject* unittype_advances_to( wesnoth_unittype* type, PyObject* args );
static PyObject* wrapper_team_recruits( wesnoth_team* team, PyObject* args );
static PyObject* wrapper_unit_find_path( wesnoth_unit* unit, PyObject* args );
static PyObject* wrapper_unit_attack_statistics(wesnoth_unit* unit, PyObject* args);
static bool is_unit_valid(const unit* unit);
std::vector<team>& get_teams() { return get_info().teams; }
static std::vector<std::string> get_available_scripts();
static void initialize_python();
static void invoke(std::string name);
friend void recalculate_movemaps();
private:
static bool init_;
end_level_exception exception;
ai_interface::move_map src_dst_;
ai_interface::move_map dst_src_;
std::map<location,paths> possible_moves_;
ai_interface::move_map enemy_src_dst_;
ai_interface::move_map enemy_dst_src_;
std::map<location,paths> enemy_possible_moves_;
};
#undef W
#endif

View file

@ -58,10 +58,6 @@
#include "serialization/string_utils.hpp"
#include "sha1.hpp"
#ifdef HAVE_PYTHON
#include "ai_python.hpp"
#endif
#include "wesconfig.h"
#include <clocale>
@ -1876,9 +1872,6 @@ void game_controller::reset_defines_map()
defines_map_["SMALL_GUI"] = preproc_define();
#endif
#ifdef HAVE_PYTHON
defines_map_["PYTHON"] = preproc_define();
#endif
}
void game_controller::play_game(RELOAD_GAME_DATA reload)
@ -2007,9 +2000,6 @@ static int play_game(int argc, char** argv)
<< " --max-fps the maximum fps the game tries to run at the value\n"
<< " should be between the 1 and 1000, the default is 50.\n"
<< " --path prints the name of the game data directory and exits.\n"
#ifdef HAVE_PYTHON
<< " --python-api prints the runtime documentation for the python API.\n"
#endif
<< " -r, --resolution XxY sets the screen resolution. Example: -r 800x600\n"
<< " -t, --test runs the game in a small test scenario.\n"
<< " -v, --version prints the game's version number and exits.\n"
@ -2040,11 +2030,6 @@ static int play_game(int argc, char** argv)
std::cout << game_config::path
<< "\n";
return 0;
#ifdef HAVE_PYTHON
} else if(val == "--python-api") {
python_ai::invoke("documentation.py");
return 0;
#endif
} else if (val.substr(0, 6) == "--log-") {
size_t p = val.find('=');
if (p == std::string::npos) {

View file

@ -579,17 +579,8 @@ config connect::side::get_config() const
{
config *ai = res.child("ai");
if (!ai) ai = &res.add_child("ai");
#ifdef HAVE_PYTHON
if (ai_algorithm_.substr(ai_algorithm_.length() - 3) == ".py") {
(*ai)["ai_algorithm"] = "python_ai";
(*ai)["python_script"] = ai_algorithm_;
}
else
#endif
{
if (ai_algorithm_ != "default")
(*ai)["ai_algorithm"] = ai_algorithm_;
}
if (ai_algorithm_ != "default")
(*ai)["ai_algorithm"] = ai_algorithm_;
}
description = N_("Computer player");
break;