1
0
forked from mirrors/0ad

Lint and format Python files using ruff

To improve quality und uniformity of the included Python code this
lints and formats the included Python files with ruff.
This commit is contained in:
Dunedan
2024-08-22 09:18:20 +02:00
parent 8519eb9b86
commit c49d4eedd0
34 changed files with 2051 additions and 1474 deletions
@@ -4,31 +4,36 @@ import zero_ad
# First, we will define some helper functions we will use later.
import math
def dist (p1, p2):
def dist(p1, p2):
return math.sqrt(sum((math.pow(x2 - x1, 2) for (x1, x2) in zip(p1, p2))))
def center(units):
sum_position = map(sum, zip(*map(lambda u: u.position(), units)))
return [x/len(units) for x in sum_position]
return [x / len(units) for x in sum_position]
def closest(units, position):
dists = (dist(unit.position(), position) for unit in units)
index = 0
min_dist = next(dists)
for (i, d) in enumerate(dists):
for i, d in enumerate(dists):
if d < min_dist:
index = i
min_dist = d
return units[index]
# Connect to a 0 AD game server listening at localhost:6000
game = zero_ad.ZeroAD('http://localhost:6000')
game = zero_ad.ZeroAD("http://localhost:6000")
# Load the Arcadia map
samples_dir = path.dirname(path.realpath(__file__))
scenario_config_path = path.join(samples_dir, 'arcadia.json')
with open(scenario_config_path, 'r') as f:
scenario_config_path = path.join(samples_dir, "arcadia.json")
with open(scenario_config_path, "r") as f:
arcadia_config = f.read()
state = game.reset(arcadia_config)
@@ -37,15 +42,15 @@ state = game.reset(arcadia_config)
state = game.step()
# Units can be queried from the game state
citizen_soldiers = state.units(owner=1, type='infantry')
citizen_soldiers = state.units(owner=1, type="infantry")
# (including gaia units like trees or other resources)
nearby_tree = closest(state.units(owner=0, type='tree'), center(citizen_soldiers))
nearby_tree = closest(state.units(owner=0, type="tree"), center(citizen_soldiers))
# Action commands can be created using zero_ad.actions
collect_wood = zero_ad.actions.gather(citizen_soldiers, nearby_tree)
female_citizens = state.units(owner=1, type='female_citizen')
house_tpl = 'structures/spart/house'
female_citizens = state.units(owner=1, type="female_citizen")
house_tpl = "structures/spart/house"
x = 680
z = 640
build_house = zero_ad.actions.construct(female_citizens, house_tpl, x, z, autocontinue=True)
@@ -58,20 +63,24 @@ female_id = female_citizens[0].id()
female_citizen = state.unit(female_id)
# A variety of unit information can be queried from the unit:
print('female citizen\'s max health is', female_citizen.max_health())
print("female citizen's max health is", female_citizen.max_health())
# Raw data for units and game states are available via the data attribute
print(female_citizen.data)
# Units can be built using the "train action"
civic_center = state.units(owner=1, type="civil_centre")[0]
spearman_type = 'units/spart/infantry_spearman_b'
spearman_type = "units/spart/infantry_spearman_b"
train_spearmen = zero_ad.actions.train([civic_center], spearman_type)
state = game.step([train_spearmen])
# Let's step the engine until the house has been built
is_unit_busy = lambda state, unit_id: len(state.unit(unit_id).data['unitAIOrderData']) > 0
def is_unit_busy(state, unit_id):
return len(state.unit(unit_id).data["unitAIOrderData"]) > 0
while is_unit_busy(state, female_id):
state = game.step()
@@ -85,14 +94,16 @@ for _ in range(150):
state = game.step()
# Let's attack with our entire military
state = game.step([zero_ad.actions.chat('An attack is coming!')])
state = game.step([zero_ad.actions.chat("An attack is coming!")])
while len(state.units(owner=2, type='unit')) > 0:
attack_units = [ unit for unit in state.units(owner=1, type='unit') if 'female' not in unit.type() ]
target = closest(state.units(owner=2, type='unit'), center(attack_units))
while len(state.units(owner=2, type="unit")) > 0:
attack_units = [
unit for unit in state.units(owner=1, type="unit") if "female" not in unit.type()
]
target = closest(state.units(owner=2, type="unit"), center(attack_units))
state = game.step([zero_ad.actions.attack(attack_units, target)])
while state.unit(target.id()):
state = game.step()
game.step([zero_ad.actions.chat('The enemies have been vanquished. Our home is safe again.')])
game.step([zero_ad.actions.chat("The enemies have been vanquished. Our home is safe again.")])
+12 -11
View File
@@ -1,13 +1,14 @@
import os
from setuptools import setup
setup(name='zero_ad',
version='0.0.1',
description='Python client for 0 AD',
url='https://code.wildfiregames.com',
author='Brian Broll',
author_email='brian.broll@gmail.com',
install_requires=[],
license='MIT',
packages=['zero_ad'],
zip_safe=False)
setup(
name="zero_ad",
version="0.0.1",
description="Python client for 0 AD",
url="https://code.wildfiregames.com",
author="Brian Broll",
author_email="brian.broll@gmail.com",
install_requires=[],
license="MIT",
packages=["zero_ad"],
zip_safe=False,
)
@@ -1,35 +1,38 @@
import zero_ad
import json
import math
from os import path
game = zero_ad.ZeroAD('http://localhost:6000')
game = zero_ad.ZeroAD("http://localhost:6000")
scriptdir = path.dirname(path.realpath(__file__))
with open(path.join(scriptdir, '..', 'samples', 'arcadia.json'), 'r') as f:
with open(path.join(scriptdir, "..", "samples", "arcadia.json"), "r") as f:
config = f.read()
def dist (p1, p2):
def dist(p1, p2):
return math.sqrt(sum((math.pow(x2 - x1, 2) for (x1, x2) in zip(p1, p2))))
def center(units):
sum_position = map(sum, zip(*map(lambda u: u.position(), units)))
return [x/len(units) for x in sum_position]
return [x / len(units) for x in sum_position]
def closest(units, position):
dists = (dist(unit.position(), position) for unit in units)
index = 0
min_dist = next(dists)
for (i, d) in enumerate(dists):
for i, d in enumerate(dists):
if d < min_dist:
index = i
min_dist = d
return units[index]
def test_construct():
state = game.reset(config)
female_citizens = state.units(owner=1, type='female_citizen')
house_tpl = 'structures/spart/house'
female_citizens = state.units(owner=1, type="female_citizen")
house_tpl = "structures/spart/house"
house_count = len(state.units(owner=1, type=house_tpl))
x = 680
z = 640
@@ -39,21 +42,23 @@ def test_construct():
while len(state.units(owner=1, type=house_tpl)) == house_count:
state = game.step()
def test_gather():
state = game.reset(config)
female_citizen = state.units(owner=1, type='female_citizen')[0]
trees = state.units(owner=0, type='tree')
nearby_tree = closest(state.units(owner=0, type='tree'), female_citizen.position())
female_citizen = state.units(owner=1, type="female_citizen")[0]
state.units(owner=0, type="tree")
nearby_tree = closest(state.units(owner=0, type="tree"), female_citizen.position())
collect_wood = zero_ad.actions.gather([female_citizen], nearby_tree)
state = game.step([collect_wood])
while len(state.unit(female_citizen.id()).data['resourceCarrying']) == 0:
while len(state.unit(female_citizen.id()).data["resourceCarrying"]) == 0:
state = game.step()
def test_train():
state = game.reset(config)
civic_centers = state.units(owner=1, type="civil_centre")
spearman_type = 'units/spart/infantry_spearman_b'
spearman_type = "units/spart/infantry_spearman_b"
spearman_count = len(state.units(owner=1, type=spearman_type))
train_spearmen = zero_ad.actions.train(civic_centers, spearman_type)
@@ -61,9 +66,10 @@ def test_train():
while len(state.units(owner=1, type=spearman_type)) == spearman_count:
state = game.step()
def test_walk():
state = game.reset(config)
female_citizens = state.units(owner=1, type='female_citizen')
female_citizens = state.units(owner=1, type="female_citizen")
x = 680
z = 640
initial_distance = dist(center(female_citizens), [x, z])
@@ -73,13 +79,14 @@ def test_walk():
distance = initial_distance
while distance >= initial_distance:
state = game.step()
female_citizens = state.units(owner=1, type='female_citizen')
female_citizens = state.units(owner=1, type="female_citizen")
distance = dist(center(female_citizens), [x, z])
def test_attack():
state = game.reset(config)
unit = state.units(owner=1, type='cavalry')[0]
target = state.units(owner=2, type='female_citizen')[0]
unit = state.units(owner=1, type="cavalry")[0]
target = state.units(owner=2, type="female_citizen")[0]
initial_health_target = target.health()
initial_health_unit = unit.health()
@@ -87,11 +94,13 @@ def test_attack():
attack = zero_ad.actions.attack([unit], target)
state = game.step([attack])
while (state.unit(target.id()).health() >= initial_health_target
) and (state.unit(unit.id()).health() >= initial_health_unit):
while (state.unit(target.id()).health() >= initial_health_target) and (
state.unit(unit.id()).health() >= initial_health_unit
):
state = game.step()
def test_chat():
state = game.reset(config)
chat = zero_ad.actions.chat('hello world!!')
state = game.step([chat])
game.reset(config)
chat = zero_ad.actions.chat("hello world!!")
game.step([chat])
@@ -1,44 +1,48 @@
import zero_ad
import json
import math
from os import path
game = zero_ad.ZeroAD('http://localhost:6000')
game = zero_ad.ZeroAD("http://localhost:6000")
scriptdir = path.dirname(path.realpath(__file__))
with open(path.join(scriptdir, '..', 'samples', 'arcadia.json'), 'r') as f:
with open(path.join(scriptdir, "..", "samples", "arcadia.json"), "r") as f:
config = f.read()
with open(path.join(scriptdir, 'fastactions.js'), 'r') as f:
with open(path.join(scriptdir, "fastactions.js"), "r") as f:
fastactions = f.read()
def test_return_object():
state = game.reset(config)
game.reset(config)
result = game.evaluate('({"hello": "world"})')
assert type(result) is dict
assert result['hello'] == 'world'
assert result["hello"] == "world"
def test_return_null():
result = game.evaluate('null')
assert result == None
result = game.evaluate("null")
assert result is None
def test_return_string():
state = game.reset(config)
game.reset(config)
result = game.evaluate('"cat"')
assert result == 'cat'
assert result == "cat"
def test_fastactions():
state = game.reset(config)
game.evaluate(fastactions)
female_citizens = state.units(owner=1, type='female_citizen')
house_tpl = 'structures/spart/house'
house_count = len(state.units(owner=1, type=house_tpl))
female_citizens = state.units(owner=1, type="female_citizen")
house_tpl = "structures/spart/house"
len(state.units(owner=1, type=house_tpl))
x = 680
z = 640
build_house = zero_ad.actions.construct(female_citizens, house_tpl, x, z, autocontinue=True)
# Check that they start building the house
state = game.step([build_house])
step_count = 0
new_house = lambda _=None: state.units(owner=1, type=house_tpl)[0]
def new_house(_=None):
return state.units(owner=1, type=house_tpl)[0]
initial_health = new_house().health(ratio=True)
while new_house().health(ratio=True) == initial_health:
state = game.step()
@@ -1,4 +1,5 @@
from . import actions
from . import actions # noqa: F401
from . import environment
ZeroAD = environment.ZeroAD
GameState = environment.GameState
+36 -42
View File
@@ -1,63 +1,57 @@
def construct(units, template, x, z, angle=0, autorepair=True, autocontinue=True, queued=False):
unit_ids = [ unit.id() for unit in units ]
unit_ids = [unit.id() for unit in units]
return {
'type': 'construct',
'entities': unit_ids,
'template': template,
'x': x,
'z': z,
'angle': angle,
'autorepair': autorepair,
'autocontinue': autocontinue,
'queued': queued,
"type": "construct",
"entities": unit_ids,
"template": template,
"x": x,
"z": z,
"angle": angle,
"autorepair": autorepair,
"autocontinue": autocontinue,
"queued": queued,
}
def gather(units, target, queued=False):
unit_ids = [ unit.id() for unit in units ]
unit_ids = [unit.id() for unit in units]
return {
'type': 'gather',
'entities': unit_ids,
'target': target.id(),
'queued': queued,
"type": "gather",
"entities": unit_ids,
"target": target.id(),
"queued": queued,
}
def train(entities, unit_type, count=1):
entity_ids = [ unit.id() for unit in entities ]
entity_ids = [unit.id() for unit in entities]
return {
'type': 'train',
'entities': entity_ids,
'template': unit_type,
'count': count,
"type": "train",
"entities": entity_ids,
"template": unit_type,
"count": count,
}
def chat(message):
return {
'type': 'aichat',
'message': message
}
return {"type": "aichat", "message": message}
def reveal_map():
return {
'type': 'reveal-map',
'enable': True
}
return {"type": "reveal-map", "enable": True}
def walk(units, x, z, queued=False):
ids = [ unit.id() for unit in units ]
return {
'type': 'walk',
'entities': ids,
'x': x,
'z': z,
'queued': queued
}
ids = [unit.id() for unit in units]
return {"type": "walk", "entities": ids, "x": x, "z": z, "queued": queued}
def attack(units, target, queued=False, allow_capture=True):
unit_ids = [ unit.id() for unit in units ]
unit_ids = [unit.id() for unit in units]
return {
'type': 'attack',
'entities': unit_ids,
'target': target.id(),
'allowCapture': allow_capture,
'queued': queued
"type": "attack",
"entities": unit_ids,
"target": target.id(),
"allowCapture": allow_capture,
"queued": queued,
}
+12 -12
View File
@@ -1,33 +1,33 @@
import urllib
from urllib import request
import json
class RLAPI():
class RLAPI:
def __init__(self, url):
self.url = url
def post(self, route, data):
response = request.urlopen(url=f'{self.url}/{route}', data=bytes(data, 'utf8'))
response = request.urlopen(url=f"{self.url}/{route}", data=bytes(data, "utf8"))
return response.read()
def step(self, commands):
post_data = '\n'.join((f'{player};{json.dumps(action)}' for (player, action) in commands))
return self.post('step', post_data)
post_data = "\n".join((f"{player};{json.dumps(action)}" for (player, action) in commands))
return self.post("step", post_data)
def reset(self, scenario_config, player_id, save_replay):
path = 'reset?'
path = "reset?"
if save_replay:
path += 'saveReplay=1&'
path += "saveReplay=1&"
if player_id:
path += f'playerID={player_id}&'
path += f"playerID={player_id}&"
return self.post(path, scenario_config)
def get_templates(self, names):
post_data = '\n'.join(names)
response = self.post('templates', post_data)
return zip(names, response.decode().split('\n'))
post_data = "\n".join(names)
response = self.post("templates", post_data)
return zip(names, response.decode().split("\n"))
def evaluate(self, code):
response = self.post('evaluate', code)
response = self.post("evaluate", code)
return json.loads(response.decode())
@@ -1,11 +1,11 @@
from .api import RLAPI
import json
import math
from xml.etree import ElementTree
from itertools import cycle
class ZeroAD():
def __init__(self, uri='http://localhost:6000'):
class ZeroAD:
def __init__(self, uri="http://localhost:6000"):
self.api = RLAPI(uri)
self.current_state = None
self.cache = {}
@@ -20,7 +20,7 @@ class ZeroAD():
self.current_state = GameState(json.loads(state_json), self)
return self.current_state
def reset(self, config='', save_replay=False, player_id=1):
def reset(self, config="", save_replay=False, player_id=1):
state_json = self.api.reset(config, player_id, save_replay)
self.current_state = GameState(json.loads(state_json), self)
return self.current_state
@@ -33,7 +33,7 @@ class ZeroAD():
def get_templates(self, names):
templates = self.api.get_templates(names)
return [ (name, EntityTemplate(content)) for (name, content) in templates ]
return [(name, EntityTemplate(content)) for (name, content) in templates]
def update_templates(self, types=[]):
all_types = list(set([unit.type() for unit in self.current_state.units()]))
@@ -41,54 +41,60 @@ class ZeroAD():
template_pairs = self.get_templates(all_types)
self.cache = {}
for (name, tpl) in template_pairs:
for name, tpl in template_pairs:
self.cache[name] = tpl
return template_pairs
class GameState():
class GameState:
def __init__(self, data, game):
self.data = data
self.game = game
self.mapSize = self.data['mapSize']
self.mapSize = self.data["mapSize"]
def units(self, owner=None, type=None):
filter_fn = lambda e: (owner is None or e['owner'] == owner) and \
(type is None or type in e['template'])
return [ Entity(e, self.game) for e in self.data['entities'].values() if filter_fn(e) ]
def filter_fn(e):
return (owner is None or e["owner"] == owner) and (
type is None or type in e["template"]
)
return [Entity(e, self.game) for e in self.data["entities"].values() if filter_fn(e)]
def unit(self, id):
id = str(id)
return Entity(self.data['entities'][id], self.game) if id in self.data['entities'] else None
return (
Entity(self.data["entities"][id], self.game) if id in self.data["entities"] else None
)
class Entity():
class Entity:
def __init__(self, data, game):
self.data = data
self.game = game
self.template = self.game.cache.get(self.type(), None)
def type(self):
return self.data['template']
return self.data["template"]
def id(self):
return self.data['id']
return self.data["id"]
def owner(self):
return self.data['owner']
return self.data["owner"]
def max_health(self):
template = self.get_template()
return float(template.get('Health/Max'))
return float(template.get("Health/Max"))
def health(self, ratio=False):
if ratio:
return self.data['hitpoints']/self.max_health()
return self.data["hitpoints"] / self.max_health()
return self.data['hitpoints']
return self.data["hitpoints"]
def position(self):
return self.data['position']
return self.data["position"]
def get_template(self):
if self.template is None:
@@ -97,9 +103,10 @@ class Entity():
return self.template
class EntityTemplate():
class EntityTemplate:
def __init__(self, xml):
self.data = ElementTree.fromstring(f'<Entity>{xml}</Entity>')
self.data = ElementTree.fromstring(f"<Entity>{xml}</Entity>")
def get(self, path):
node = self.data.find(path)
@@ -113,4 +120,4 @@ class EntityTemplate():
return node is not None
def __str__(self):
return ElementTree.tostring(self.data).decode('utf-8')
return ElementTree.tostring(self.data).decode("utf-8")