dungeoncoder 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dungeoncoder/__init__.py +4 -0
- dungeoncoder/__main__.py +254 -0
- dungeoncoder/_api_config.py +6 -0
- dungeoncoder/_generated/__init__.py +8 -0
- dungeoncoder/_generated/api/__init__.py +1 -0
- dungeoncoder/_generated/api/game/__init__.py +1 -0
- dungeoncoder/_generated/api/game/get_statistics.py +158 -0
- dungeoncoder/_generated/api/game/get_version.py +166 -0
- dungeoncoder/_generated/api/hero/__init__.py +1 -0
- dungeoncoder/_generated/api/hero/ask_oracle.py +192 -0
- dungeoncoder/_generated/api/hero/configure.py +207 -0
- dungeoncoder/_generated/api/hero/drop.py +207 -0
- dungeoncoder/_generated/api/hero/get_inventory.py +188 -0
- dungeoncoder/_generated/api/hero/get_items_at_position.py +188 -0
- dungeoncoder/_generated/api/hero/interact.py +188 -0
- dungeoncoder/_generated/api/hero/is_abyss_in_front.py +188 -0
- dungeoncoder/_generated/api/hero/is_at_goal.py +192 -0
- dungeoncoder/_generated/api/hero/is_collision_in_front.py +188 -0
- dungeoncoder/_generated/api/hero/is_enemy_in_front.py +188 -0
- dungeoncoder/_generated/api/hero/is_facing_north.py +188 -0
- dungeoncoder/_generated/api/hero/is_switch_in_front.py +188 -0
- dungeoncoder/_generated/api/hero/is_torch_in_front.py +188 -0
- dungeoncoder/_generated/api/hero/move.py +188 -0
- dungeoncoder/_generated/api/hero/peek_item_value.py +211 -0
- dungeoncoder/_generated/api/hero/pickup.py +207 -0
- dungeoncoder/_generated/api/hero/read_item_value.py +188 -0
- dungeoncoder/_generated/api/hero/sense_goal.py +192 -0
- dungeoncoder/_generated/api/hero/set_pace.py +207 -0
- dungeoncoder/_generated/api/hero/turn_left.py +188 -0
- dungeoncoder/_generated/api/level/__init__.py +1 -0
- dungeoncoder/_generated/api/level/load_level.py +190 -0
- dungeoncoder/_generated/api/level/reset_level.py +158 -0
- dungeoncoder/_generated/client.py +271 -0
- dungeoncoder/_generated/errors.py +14 -0
- dungeoncoder/_generated/models/__init__.py +47 -0
- dungeoncoder/_generated/models/api_error_response.py +98 -0
- dungeoncoder/_generated/models/api_error_response_status.py +7 -0
- dungeoncoder/_generated/models/boolean_result.py +96 -0
- dungeoncoder/_generated/models/boolean_result_status.py +7 -0
- dungeoncoder/_generated/models/configure_params.py +84 -0
- dungeoncoder/_generated/models/distance_param.py +76 -0
- dungeoncoder/_generated/models/name_param.py +76 -0
- dungeoncoder/_generated/models/nullable_integer_result.py +104 -0
- dungeoncoder/_generated/models/nullable_integer_result_status.py +7 -0
- dungeoncoder/_generated/models/nullable_string_result.py +104 -0
- dungeoncoder/_generated/models/nullable_string_result_status.py +7 -0
- dungeoncoder/_generated/models/pace_params.py +76 -0
- dungeoncoder/_generated/models/statistics.py +217 -0
- dungeoncoder/_generated/models/statistics_result.py +104 -0
- dungeoncoder/_generated/models/statistics_result_status.py +7 -0
- dungeoncoder/_generated/models/string_array_result.py +100 -0
- dungeoncoder/_generated/models/string_array_result_status.py +7 -0
- dungeoncoder/_generated/models/tiled_level.py +67 -0
- dungeoncoder/_generated/models/version_result.py +104 -0
- dungeoncoder/_generated/models/version_result_result.py +84 -0
- dungeoncoder/_generated/models/version_result_status.py +7 -0
- dungeoncoder/_generated/types.py +53 -0
- dungeoncoder/asciimap.py +824 -0
- dungeoncoder/dungeoncoder.py +482 -0
- dungeoncoder/generator.py +213 -0
- dungeoncoder/packs/gelaende.json +1 -0
- dungeoncoder/packs/items.json +67 -0
- dungeoncoder/packs/station.json +1 -0
- dungeoncoder/packs/studio.json +1 -0
- dungeoncoder/packs/tilesets.json +1 -0
- dungeoncoder/packs/werkstatt.json +1 -0
- dungeoncoder/replay.py +80 -0
- dungeoncoder/requirements.txt +2 -0
- dungeoncoder/sim.py +792 -0
- dungeoncoder/testing.py +63 -0
- dungeoncoder/trace.py +92 -0
- dungeoncoder/variants.py +112 -0
- dungeoncoder-0.2.0.dist-info/METADATA +37 -0
- dungeoncoder-0.2.0.dist-info/RECORD +77 -0
- dungeoncoder-0.2.0.dist-info/WHEEL +5 -0
- dungeoncoder-0.2.0.dist-info/licenses/LICENSE.txt +25 -0
- dungeoncoder-0.2.0.dist-info/top_level.txt +1 -0
dungeoncoder/__init__.py
ADDED
dungeoncoder/__main__.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""`python -m dungeoncoder` (in the exercise folder, with the Python that runs your programs).
|
|
2
|
+
|
|
3
|
+
python -m dungeoncoder setup check (below)
|
|
4
|
+
python -m dungeoncoder load LEVEL [--seed N] load a level (.json or .txt) into the game
|
|
5
|
+
python -m dungeoncoder variants PROG LEVEL... run a program on every level in the simulator
|
|
6
|
+
(--traces DIR keeps a trace of every failed run)
|
|
7
|
+
python -m dungeoncoder replay TRACE [--run N] [--pace X] watch a trace in the game
|
|
8
|
+
python -m dungeoncoder trace TRACE [--run N] list a trace with the hero's field after each call
|
|
9
|
+
|
|
10
|
+
The setup check prints what a program of yours will use: the Python, this package, where the game
|
|
11
|
+
is expected, whether it answers and fits, the asset packs, and whether the simulator runs. Each line
|
|
12
|
+
starts with ok, note or PROBLEM; a PROBLEM line says what to do.
|
|
13
|
+
"""
|
|
14
|
+
import argparse
|
|
15
|
+
import glob
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def line(state, text):
|
|
24
|
+
print(f"{state:8} {text}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _manifest(folder):
|
|
28
|
+
"""A pack's pack.json (name, version, engine: the oldest Dungeon Coder it needs, ...), or {}."""
|
|
29
|
+
try:
|
|
30
|
+
with open(os.path.join(folder, "pack.json"), encoding="utf-8") as f:
|
|
31
|
+
data = json.load(f)
|
|
32
|
+
return data if isinstance(data, dict) else {}
|
|
33
|
+
except (OSError, ValueError):
|
|
34
|
+
return {}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _at_least(have, need):
|
|
38
|
+
"""Version have >= need ("major.minor.patch", missing parts count as 0)."""
|
|
39
|
+
def parts(version):
|
|
40
|
+
return [int(p) if p.isdigit() else 0 for p in str(version).split(".")]
|
|
41
|
+
a, b = parts(have), parts(need)
|
|
42
|
+
width = max(len(a), len(b))
|
|
43
|
+
return a + [0] * (width - len(a)) >= b + [0] * (width - len(b))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def check():
|
|
47
|
+
problems = 0
|
|
48
|
+
version = sys.version.split()[0]
|
|
49
|
+
if sys.version_info >= (3, 10):
|
|
50
|
+
line("ok", f"Python {version} ({sys.executable})")
|
|
51
|
+
else:
|
|
52
|
+
problems += 1
|
|
53
|
+
line("PROBLEM", f"Python {version} is too old: install Python 3.15 from python.org ({sys.executable})")
|
|
54
|
+
|
|
55
|
+
import dungeoncoder
|
|
56
|
+
from dungeoncoder import dungeoncoder as core
|
|
57
|
+
here = os.path.dirname(os.path.abspath(dungeoncoder.__file__))
|
|
58
|
+
copied = os.path.dirname(here) == os.getcwd()
|
|
59
|
+
line("ok", f"dungeoncoder {dungeoncoder.__version__}, {'copied into this folder' if copied else 'installed'}: {here}")
|
|
60
|
+
|
|
61
|
+
import httpx
|
|
62
|
+
url = core.DEFAULT_BASE_URL
|
|
63
|
+
try:
|
|
64
|
+
answer = httpx.get(url + "/version", timeout=2)
|
|
65
|
+
except httpx.HTTPError:
|
|
66
|
+
answer = None
|
|
67
|
+
if answer is None:
|
|
68
|
+
line("note", f"no game answers at {url}: start it in VS Code with \"Dungeon Coder: Enter the dungeon\" "
|
|
69
|
+
"(only needed for the game; the simulator works without)")
|
|
70
|
+
elif answer.status_code == 404:
|
|
71
|
+
problems += 1
|
|
72
|
+
line("PROBLEM", f"the game at {url} is an older Dungeon Coder without versions: update the extension "
|
|
73
|
+
"(Extensions view), or close the other Dungeon Coder")
|
|
74
|
+
else:
|
|
75
|
+
extension = answer.json().get("result", {}).get("extension", "?")
|
|
76
|
+
if extension.split(".")[:2] == dungeoncoder.__version__.split(".")[:2]:
|
|
77
|
+
line("ok", f"the game answers at {url}: Dungeon Coder {extension}, fits")
|
|
78
|
+
else:
|
|
79
|
+
problems += 1
|
|
80
|
+
line("PROBLEM", f"the game at {url} is Dungeon Coder {extension}, this package is "
|
|
81
|
+
f"{dungeoncoder.__version__}: run \"Dungeon Coder: Copy Python API to workspace\" again")
|
|
82
|
+
|
|
83
|
+
packs = [p for p in os.environ.get("DC_ASSET_PACKS", "").split(os.pathsep) if p]
|
|
84
|
+
if packs:
|
|
85
|
+
for pack in packs:
|
|
86
|
+
if os.path.isdir(pack):
|
|
87
|
+
manifest = _manifest(pack)
|
|
88
|
+
need = manifest.get("engine")
|
|
89
|
+
if isinstance(need, str) and not _at_least(dungeoncoder.__version__, need):
|
|
90
|
+
problems += 1
|
|
91
|
+
line("PROBLEM", f"asset pack {pack} needs Dungeon Coder {need} or newer, this is "
|
|
92
|
+
f"{dungeoncoder.__version__}: update the extension and the Python package")
|
|
93
|
+
else:
|
|
94
|
+
about = ", ".join(f"{k} {manifest[k]}" for k in ("name", "version") if manifest.get(k))
|
|
95
|
+
line("ok", f"asset pack {pack}" + (f" ({about})" if about else ""))
|
|
96
|
+
else:
|
|
97
|
+
problems += 1
|
|
98
|
+
line("PROBLEM", f"asset pack {pack} doesn't exist: clone it there (see the exercise sheet)")
|
|
99
|
+
else:
|
|
100
|
+
line("note", "no asset packs in this terminal (DC_ASSET_PACKS); only text maps of the course need them, "
|
|
101
|
+
"the game finds them through its settings")
|
|
102
|
+
|
|
103
|
+
from dungeoncoder.testing import spiel
|
|
104
|
+
import contextlib
|
|
105
|
+
import io
|
|
106
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
107
|
+
# a style that ships with the package (the course styles come with the course's asset pack)
|
|
108
|
+
_, hero = spiel("start: east\nstyle: station\n---\n#####\n#H.Z#\n#####\n")
|
|
109
|
+
ok = hero.move() and hero.move() and hero.is_at_goal()
|
|
110
|
+
line("ok" if ok else "PROBLEM", "the simulator plays a small map" if ok else "the simulator failed on a small map")
|
|
111
|
+
problems += not ok
|
|
112
|
+
|
|
113
|
+
print()
|
|
114
|
+
print("Everything is ready." if not problems else f"{problems} problem(s): see the lines above.")
|
|
115
|
+
return 1 if problems else 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def load(args):
|
|
119
|
+
from dungeoncoder import Game
|
|
120
|
+
Game(args.level, seed=args.seed)
|
|
121
|
+
print(f"loaded {args.level}")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _runs(path, number):
|
|
126
|
+
from dungeoncoder import trace
|
|
127
|
+
try:
|
|
128
|
+
runs = trace.read(path)
|
|
129
|
+
except (OSError, ValueError) as err:
|
|
130
|
+
print(f"Error: {err}")
|
|
131
|
+
return None
|
|
132
|
+
if not runs:
|
|
133
|
+
print(f"Error: {path} holds no run")
|
|
134
|
+
return None
|
|
135
|
+
if number is None:
|
|
136
|
+
return runs
|
|
137
|
+
if not 1 <= number <= len(runs):
|
|
138
|
+
print(f"Error: {path} has {len(runs)} run(s), not {number}")
|
|
139
|
+
return None
|
|
140
|
+
return [runs[number - 1]]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def replay_command(args):
|
|
144
|
+
from dungeoncoder.replay import replay
|
|
145
|
+
runs = _runs(args.trace, args.run)
|
|
146
|
+
if runs is None:
|
|
147
|
+
return 1
|
|
148
|
+
differs = 0
|
|
149
|
+
for n, run in enumerate(runs, start=1):
|
|
150
|
+
print(f"run {n}: {run.get('level_file') or 'level'}, {len(run['calls'])} calls")
|
|
151
|
+
differs += replay(run, args.pace) is not None
|
|
152
|
+
return 1 if differs else 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def trace_command(args):
|
|
156
|
+
from dungeoncoder.replay import describe, positions
|
|
157
|
+
runs = _runs(args.trace, args.run)
|
|
158
|
+
if runs is None:
|
|
159
|
+
return 1
|
|
160
|
+
for n, run in enumerate(runs, start=1):
|
|
161
|
+
print(f"run {n}: {run.get('level_file') or 'level'} (fields: column,row of the level)")
|
|
162
|
+
for number, method, params, result, (col, row), direction in positions(run):
|
|
163
|
+
if number == "end":
|
|
164
|
+
stats = result
|
|
165
|
+
print(f" end field {col},{row} facing {direction}: "
|
|
166
|
+
+ ", ".join(f"{k} {stats[k]}" for k in ("moves", "turns", "bumps", "level_complete") if k in stats)
|
|
167
|
+
+ (f", missing {', '.join(stats['missing'])}" if stats.get("missing") else ""))
|
|
168
|
+
else:
|
|
169
|
+
print(f"{number:>8} {describe(method, params):32} -> {result!r:8} field {col},{row} facing {direction}")
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def variants_command(args):
|
|
174
|
+
"""Runs the program once per level in the simulator: every level it loads is replaced by
|
|
175
|
+
that one (DUNGEONCODER_LEVEL), so the program itself stays unchanged."""
|
|
176
|
+
from dungeoncoder.replay import positions
|
|
177
|
+
from dungeoncoder import trace
|
|
178
|
+
levels = [p for pattern in args.levels for p in sorted(glob.glob(pattern)) or [pattern]]
|
|
179
|
+
if args.traces:
|
|
180
|
+
os.makedirs(args.traces, exist_ok=True)
|
|
181
|
+
package = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
182
|
+
failed = 0
|
|
183
|
+
for level in levels:
|
|
184
|
+
record = os.path.join(tempfile.mkdtemp(prefix="dungeoncoder-variant-"), "trace.json")
|
|
185
|
+
env = {**os.environ, "DUNGEONCODER_SIM": "1", "DUNGEONCODER_LEVEL": os.path.abspath(level),
|
|
186
|
+
"DUNGEONCODER_TRACE": record,
|
|
187
|
+
"PYTHONPATH": os.pathsep.join([package] + [p for p in [os.environ.get("PYTHONPATH")] if p])}
|
|
188
|
+
process = subprocess.Popen([sys.executable, os.path.abspath(args.program)],
|
|
189
|
+
cwd=os.path.dirname(os.path.abspath(args.program)) or ".", env=env,
|
|
190
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
191
|
+
try:
|
|
192
|
+
code = process.wait(timeout=args.timeout)
|
|
193
|
+
note = "" if code == 0 else f"the program stopped with an error (exit {code})"
|
|
194
|
+
except subprocess.TimeoutExpired:
|
|
195
|
+
process.terminate() # SIGTERM: the program still writes its trace
|
|
196
|
+
try:
|
|
197
|
+
process.wait(timeout=10)
|
|
198
|
+
except subprocess.TimeoutExpired:
|
|
199
|
+
process.kill()
|
|
200
|
+
note = f"no end after {args.timeout} s"
|
|
201
|
+
try:
|
|
202
|
+
run = trace.read(record)[-1]
|
|
203
|
+
stats = positions(run)[-1][3]
|
|
204
|
+
except (OSError, ValueError, IndexError):
|
|
205
|
+
run, stats = None, {}
|
|
206
|
+
ok = bool(stats.get("level_complete")) and not note
|
|
207
|
+
failed += not ok
|
|
208
|
+
detail = (f"{stats.get('moves', 0)} moves, {stats.get('turns', 0)} turns, {stats.get('bumps', 0)} bumps"
|
|
209
|
+
if stats else "no result")
|
|
210
|
+
if stats.get("missing"):
|
|
211
|
+
detail += ", missing " + ", ".join(stats["missing"])
|
|
212
|
+
print(f"{'done' if ok else 'FAILED':7} {level}: {detail}" + (f"; {note}" if note else ""))
|
|
213
|
+
if not ok and args.traces and run is not None:
|
|
214
|
+
name = os.path.join(args.traces, os.path.splitext(os.path.basename(level))[0] + ".trace.json")
|
|
215
|
+
trace.write(name, [run])
|
|
216
|
+
print(f" watch it: python -m dungeoncoder replay {name}")
|
|
217
|
+
print(f"{len(levels) - failed} of {len(levels)} level(s) done")
|
|
218
|
+
return 1 if failed else 0
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def main(argv=None):
|
|
222
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
223
|
+
if not argv:
|
|
224
|
+
return check()
|
|
225
|
+
parser = argparse.ArgumentParser(prog="python -m dungeoncoder", description=__doc__,
|
|
226
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
227
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
228
|
+
p = sub.add_parser("check", help="setup check")
|
|
229
|
+
p.set_defaults(func=lambda _: check())
|
|
230
|
+
p = sub.add_parser("load", help="load a level into the game")
|
|
231
|
+
p.add_argument("level")
|
|
232
|
+
p.add_argument("--seed", type=int)
|
|
233
|
+
p.set_defaults(func=load)
|
|
234
|
+
p = sub.add_parser("variants", help="run a program on several levels in the simulator")
|
|
235
|
+
p.add_argument("program")
|
|
236
|
+
p.add_argument("levels", nargs="+")
|
|
237
|
+
p.add_argument("--traces", help="folder for the traces of failed runs")
|
|
238
|
+
p.add_argument("--timeout", type=int, default=60)
|
|
239
|
+
p.set_defaults(func=variants_command)
|
|
240
|
+
p = sub.add_parser("replay", help="watch a trace in the game")
|
|
241
|
+
p.add_argument("trace")
|
|
242
|
+
p.add_argument("--run", type=int)
|
|
243
|
+
p.add_argument("--pace", type=float)
|
|
244
|
+
p.set_defaults(func=replay_command)
|
|
245
|
+
p = sub.add_parser("trace", help="list a trace")
|
|
246
|
+
p.add_argument("trace")
|
|
247
|
+
p.add_argument("--run", type=int)
|
|
248
|
+
p.set_defaults(func=trace_command)
|
|
249
|
+
args = parser.parse_args(argv)
|
|
250
|
+
return args.func(args)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if __name__ == "__main__":
|
|
254
|
+
sys.exit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" Contains methods for accessing the API """
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" Contains endpoint functions for accessing the API """
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any, cast
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...types import Response, UNSET
|
|
9
|
+
from ... import errors
|
|
10
|
+
|
|
11
|
+
from ...models.api_error_response import ApiErrorResponse
|
|
12
|
+
from ...models.statistics_result import StatisticsResult
|
|
13
|
+
from typing import cast
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_kwargs(
|
|
18
|
+
|
|
19
|
+
) -> dict[str, Any]:
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_kwargs: dict[str, Any] = {
|
|
27
|
+
"method": "get",
|
|
28
|
+
"url": "/game/statistics",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
return _kwargs
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> ApiErrorResponse | StatisticsResult | None:
|
|
37
|
+
if response.status_code == 200:
|
|
38
|
+
response_200 = StatisticsResult.from_dict(response.json())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
return response_200
|
|
43
|
+
|
|
44
|
+
if response.status_code == 500:
|
|
45
|
+
response_500 = ApiErrorResponse.from_dict(response.json())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
return response_500
|
|
50
|
+
|
|
51
|
+
if client.raise_on_unexpected_status:
|
|
52
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
53
|
+
else:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[ApiErrorResponse | StatisticsResult]:
|
|
58
|
+
return Response(
|
|
59
|
+
status_code=HTTPStatus(response.status_code),
|
|
60
|
+
content=response.content,
|
|
61
|
+
headers=response.headers,
|
|
62
|
+
parsed=_parse_response(client=client, response=response),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def sync_detailed(
|
|
67
|
+
*,
|
|
68
|
+
client: AuthenticatedClient | Client,
|
|
69
|
+
|
|
70
|
+
) -> Response[ApiErrorResponse | StatisticsResult]:
|
|
71
|
+
""" Counters of the current level (moves, turns, bumps, ...) and whether it is complete.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
75
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Response[ApiErrorResponse | StatisticsResult]
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
kwargs = _get_kwargs(
|
|
83
|
+
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
response = client.get_httpx_client().request(
|
|
87
|
+
**kwargs,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return _build_response(client=client, response=response)
|
|
91
|
+
|
|
92
|
+
def sync(
|
|
93
|
+
*,
|
|
94
|
+
client: AuthenticatedClient | Client,
|
|
95
|
+
|
|
96
|
+
) -> ApiErrorResponse | StatisticsResult | None:
|
|
97
|
+
""" Counters of the current level (moves, turns, bumps, ...) and whether it is complete.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
101
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
ApiErrorResponse | StatisticsResult
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
return sync_detailed(
|
|
109
|
+
client=client,
|
|
110
|
+
|
|
111
|
+
).parsed
|
|
112
|
+
|
|
113
|
+
async def asyncio_detailed(
|
|
114
|
+
*,
|
|
115
|
+
client: AuthenticatedClient | Client,
|
|
116
|
+
|
|
117
|
+
) -> Response[ApiErrorResponse | StatisticsResult]:
|
|
118
|
+
""" Counters of the current level (moves, turns, bumps, ...) and whether it is complete.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
122
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Response[ApiErrorResponse | StatisticsResult]
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
kwargs = _get_kwargs(
|
|
130
|
+
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
response = await client.get_async_httpx_client().request(
|
|
134
|
+
**kwargs
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return _build_response(client=client, response=response)
|
|
138
|
+
|
|
139
|
+
async def asyncio(
|
|
140
|
+
*,
|
|
141
|
+
client: AuthenticatedClient | Client,
|
|
142
|
+
|
|
143
|
+
) -> ApiErrorResponse | StatisticsResult | None:
|
|
144
|
+
""" Counters of the current level (moves, turns, bumps, ...) and whether it is complete.
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
148
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
ApiErrorResponse | StatisticsResult
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
return (await asyncio_detailed(
|
|
156
|
+
client=client,
|
|
157
|
+
|
|
158
|
+
)).parsed
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any, cast
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...types import Response, UNSET
|
|
9
|
+
from ... import errors
|
|
10
|
+
|
|
11
|
+
from ...models.version_result import VersionResult
|
|
12
|
+
from typing import cast
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_kwargs(
|
|
17
|
+
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_kwargs: dict[str, Any] = {
|
|
26
|
+
"method": "get",
|
|
27
|
+
"url": "/version",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
return _kwargs
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> VersionResult | None:
|
|
36
|
+
if response.status_code == 200:
|
|
37
|
+
response_200 = VersionResult.from_dict(response.json())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
return response_200
|
|
42
|
+
|
|
43
|
+
if client.raise_on_unexpected_status:
|
|
44
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
45
|
+
else:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[VersionResult]:
|
|
50
|
+
return Response(
|
|
51
|
+
status_code=HTTPStatus(response.status_code),
|
|
52
|
+
content=response.content,
|
|
53
|
+
headers=response.headers,
|
|
54
|
+
parsed=_parse_response(client=client, response=response),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def sync_detailed(
|
|
59
|
+
*,
|
|
60
|
+
client: AuthenticatedClient | Client,
|
|
61
|
+
|
|
62
|
+
) -> Response[VersionResult]:
|
|
63
|
+
""" The versions of the extension and of this API, so a client can tell whether it fits.
|
|
64
|
+
|
|
65
|
+
Answered by the server itself (x-host), not by the game. The Python
|
|
66
|
+
package compares it with its own version once per program and warns if
|
|
67
|
+
a copied package doesn't fit the running extension.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
71
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Response[VersionResult]
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
kwargs = _get_kwargs(
|
|
79
|
+
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
response = client.get_httpx_client().request(
|
|
83
|
+
**kwargs,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return _build_response(client=client, response=response)
|
|
87
|
+
|
|
88
|
+
def sync(
|
|
89
|
+
*,
|
|
90
|
+
client: AuthenticatedClient | Client,
|
|
91
|
+
|
|
92
|
+
) -> VersionResult | None:
|
|
93
|
+
""" The versions of the extension and of this API, so a client can tell whether it fits.
|
|
94
|
+
|
|
95
|
+
Answered by the server itself (x-host), not by the game. The Python
|
|
96
|
+
package compares it with its own version once per program and warns if
|
|
97
|
+
a copied package doesn't fit the running extension.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
101
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
VersionResult
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
return sync_detailed(
|
|
109
|
+
client=client,
|
|
110
|
+
|
|
111
|
+
).parsed
|
|
112
|
+
|
|
113
|
+
async def asyncio_detailed(
|
|
114
|
+
*,
|
|
115
|
+
client: AuthenticatedClient | Client,
|
|
116
|
+
|
|
117
|
+
) -> Response[VersionResult]:
|
|
118
|
+
""" The versions of the extension and of this API, so a client can tell whether it fits.
|
|
119
|
+
|
|
120
|
+
Answered by the server itself (x-host), not by the game. The Python
|
|
121
|
+
package compares it with its own version once per program and warns if
|
|
122
|
+
a copied package doesn't fit the running extension.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
126
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Response[VersionResult]
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
kwargs = _get_kwargs(
|
|
134
|
+
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
response = await client.get_async_httpx_client().request(
|
|
138
|
+
**kwargs
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return _build_response(client=client, response=response)
|
|
142
|
+
|
|
143
|
+
async def asyncio(
|
|
144
|
+
*,
|
|
145
|
+
client: AuthenticatedClient | Client,
|
|
146
|
+
|
|
147
|
+
) -> VersionResult | None:
|
|
148
|
+
""" The versions of the extension and of this API, so a client can tell whether it fits.
|
|
149
|
+
|
|
150
|
+
Answered by the server itself (x-host), not by the game. The Python
|
|
151
|
+
package compares it with its own version once per program and warns if
|
|
152
|
+
a copied package doesn't fit the running extension.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
156
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
VersionResult
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
return (await asyncio_detailed(
|
|
164
|
+
client=client,
|
|
165
|
+
|
|
166
|
+
)).parsed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
""" Contains endpoint functions for accessing the API """
|