gitquest 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {gitquest-0.1.0 → gitquest-0.2.0}/PKG-INFO +21 -3
- {gitquest-0.1.0 → gitquest-0.2.0}/README.md +20 -2
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/__init__.py +1 -1
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/cli.py +43 -16
- gitquest-0.2.0/gitquest/demo.py +79 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/generator.py +15 -10
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/PKG-INFO +21 -3
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/SOURCES.txt +2 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/pyproject.toml +1 -1
- gitquest-0.2.0/tests/test_demo.py +51 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/LICENSE +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/__main__.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/combat.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/data/items.json +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/data/monsters.json +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/data/ranks.json +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/engine.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/entities.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/flexcard.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/git_parser.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest/renderer.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/dependency_links.txt +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/entry_points.txt +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/requires.txt +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/gitquest.egg-info/top_level.txt +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/setup.cfg +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/tests/test_combat.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/tests/test_generator.py +0 -0
- {gitquest-0.1.0 → gitquest-0.2.0}/tests/test_git_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitquest
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Turn your git commit history into a playable ASCII dungeon roguelike.
|
|
5
5
|
Author-email: jithin-jz <jithinjzx@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -126,13 +126,30 @@ pip install .
|
|
|
126
126
|
|
|
127
127
|
## 🎮 Play
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Two commands — works on any machine, even with no repo around:
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
|
+
pip install gitquest
|
|
132
133
|
gitquest
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
If you run `gitquest` **inside a git repository**, it builds the dungeon from
|
|
137
|
+
*your* commit history. If you run it anywhere else, it automatically launches a
|
|
138
|
+
built-in **demo dungeon** so you can play right away.
|
|
139
|
+
|
|
140
|
+
`python -m gitquest` works too. Want your own repo? `cd` into it first, or pass
|
|
141
|
+
`--path`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
cd path/to/your/repo && gitquest
|
|
145
|
+
gitquest --path ../some-other-repo
|
|
146
|
+
gitquest --demo # force the built-in demo dungeon
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Combat controls
|
|
150
|
+
|
|
151
|
+
During a fight, type one key + Enter: `a` attack · `d` defend · `i` item ·
|
|
152
|
+
`r` run.
|
|
136
153
|
|
|
137
154
|
### Flags
|
|
138
155
|
|
|
@@ -140,6 +157,7 @@ gitquest
|
|
|
140
157
|
| ---------------- | --------------------------------------------------------- |
|
|
141
158
|
| `--path <repo>` | Play on another repository (default: current directory). |
|
|
142
159
|
| `--seed <n>` | Force a specific dungeon seed (default: derived from repo).|
|
|
160
|
+
| `--demo` | Play the built-in demo dungeon (no git repo required). |
|
|
143
161
|
| `--fast` | Skip animations and "press Enter" pauses. |
|
|
144
162
|
| `--stats-only` | Skip gameplay and print just the flex card. |
|
|
145
163
|
| `--max-commits` | Cap/sample commits for huge repos (default: 300). |
|
|
@@ -98,13 +98,30 @@ pip install .
|
|
|
98
98
|
|
|
99
99
|
## 🎮 Play
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Two commands — works on any machine, even with no repo around:
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
|
+
pip install gitquest
|
|
104
105
|
gitquest
|
|
105
106
|
```
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
If you run `gitquest` **inside a git repository**, it builds the dungeon from
|
|
109
|
+
*your* commit history. If you run it anywhere else, it automatically launches a
|
|
110
|
+
built-in **demo dungeon** so you can play right away.
|
|
111
|
+
|
|
112
|
+
`python -m gitquest` works too. Want your own repo? `cd` into it first, or pass
|
|
113
|
+
`--path`:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
cd path/to/your/repo && gitquest
|
|
117
|
+
gitquest --path ../some-other-repo
|
|
118
|
+
gitquest --demo # force the built-in demo dungeon
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Combat controls
|
|
122
|
+
|
|
123
|
+
During a fight, type one key + Enter: `a` attack · `d` defend · `i` item ·
|
|
124
|
+
`r` run.
|
|
108
125
|
|
|
109
126
|
### Flags
|
|
110
127
|
|
|
@@ -112,6 +129,7 @@ gitquest
|
|
|
112
129
|
| ---------------- | --------------------------------------------------------- |
|
|
113
130
|
| `--path <repo>` | Play on another repository (default: current directory). |
|
|
114
131
|
| `--seed <n>` | Force a specific dungeon seed (default: derived from repo).|
|
|
132
|
+
| `--demo` | Play the built-in demo dungeon (no git repo required). |
|
|
115
133
|
| `--fast` | Skip animations and "press Enter" pauses. |
|
|
116
134
|
| `--stats-only` | Skip gameplay and print just the flex card. |
|
|
117
135
|
| `--max-commits` | Cap/sample commits for huge repos (default: 300). |
|
|
@@ -68,6 +68,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
68
68
|
metavar="N",
|
|
69
69
|
help="cap/sample commits for very large repos (default: 300)",
|
|
70
70
|
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--demo",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help="play a built-in demo dungeon (no git repo required)",
|
|
75
|
+
)
|
|
71
76
|
parser.add_argument(
|
|
72
77
|
"--version",
|
|
73
78
|
action="version",
|
|
@@ -103,29 +108,51 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
103
108
|
|
|
104
109
|
console = Console()
|
|
105
110
|
|
|
106
|
-
# --- Parse the repository
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
# --- Parse the repository (or fall back to the built-in demo) --------- #
|
|
112
|
+
demo_notice = False
|
|
113
|
+
if args.demo:
|
|
114
|
+
from .demo import build_demo_repo
|
|
115
|
+
|
|
116
|
+
repo = build_demo_repo()
|
|
117
|
+
demo_notice = True
|
|
118
|
+
else:
|
|
119
|
+
try:
|
|
120
|
+
repo = parse_repo(args.path, max_commits=args.max_commits)
|
|
121
|
+
except (NotAGitRepoError, EmptyHistoryError):
|
|
122
|
+
# Zero-friction: if there's no repo to read, play the demo dungeon
|
|
123
|
+
# so `pip install gitquest && gitquest` always works anywhere.
|
|
124
|
+
from .demo import build_demo_repo
|
|
125
|
+
|
|
126
|
+
repo = build_demo_repo()
|
|
127
|
+
demo_notice = True
|
|
128
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
129
|
+
console.print(f"[bold red]✗ Failed to read repository:[/bold red] {exc}")
|
|
130
|
+
return 1
|
|
131
|
+
|
|
132
|
+
if demo_notice and not args.stats_only:
|
|
116
133
|
console.print(
|
|
117
|
-
"[
|
|
118
|
-
"
|
|
134
|
+
"[yellow]ℹ No git repository here — launching the built-in demo "
|
|
135
|
+
"dungeon.[/yellow]\n"
|
|
136
|
+
"[grey62] To play your own commits, run gitquest inside a git repo "
|
|
137
|
+
"(or pass --path <repo>).[/grey62]\n"
|
|
119
138
|
)
|
|
120
|
-
return 2
|
|
121
|
-
except Exception as exc: # pragma: no cover - defensive
|
|
122
|
-
console.print(f"[bold red]✗ Failed to read repository:[/bold red] {exc}")
|
|
123
|
-
return 1
|
|
124
139
|
|
|
125
140
|
# --- Generate the dungeon --------------------------------------------- #
|
|
126
141
|
dungeon = generate_dungeon(repo, user_seed=args.seed)
|
|
127
142
|
player = Player(name=repo.head.author_name or "Hero")
|
|
128
143
|
|
|
144
|
+
# Every hero starts with a healing draught so the first room is survivable.
|
|
145
|
+
from .entities import Item, ItemKind
|
|
146
|
+
|
|
147
|
+
player.inventory.append(
|
|
148
|
+
Item(
|
|
149
|
+
name="Starter Health Draught",
|
|
150
|
+
kind=ItemKind.POTION,
|
|
151
|
+
value=15,
|
|
152
|
+
description="Restores 15 HP.",
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
129
156
|
# --- Stats-only: silent auto run, then just the flex card ------------- #
|
|
130
157
|
if args.stats_only:
|
|
131
158
|
engine = GameEngine(dungeon, player, interactive=False)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""A built-in demo repository so gitquest is playable anywhere.
|
|
2
|
+
|
|
3
|
+
When ``gitquest`` is run outside a git repository (e.g. right after
|
|
4
|
+
``pip install gitquest`` on a fresh machine), the CLI falls back to this
|
|
5
|
+
fictional commit history so there's always a dungeon to play. It is fully
|
|
6
|
+
deterministic, so the demo dungeon is identical on every device.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import datetime, timedelta, timezone
|
|
12
|
+
|
|
13
|
+
from .git_parser import CommitInfo, RepoData
|
|
14
|
+
|
|
15
|
+
_BASE = datetime(2024, 1, 1, 9, 0, 0, tzinfo=timezone.utc)
|
|
16
|
+
|
|
17
|
+
# (author, summary, insertions, deletions, files, is_merge)
|
|
18
|
+
_SCRIPT: list[tuple[str, str, int, int, list[str], bool]] = [
|
|
19
|
+
("Ada Lovelace", "feat: scaffold the Dragon-Slayer API", 180, 0,
|
|
20
|
+
["app.py", "server.py", "requirements.txt"], False),
|
|
21
|
+
("Ada Lovelace", "feat: add /quests endpoint", 96, 4,
|
|
22
|
+
["app.py", "routes/quests.py"], False),
|
|
23
|
+
("Grace Hopper", "fix: null pointer when quest list is empty", 12, 8,
|
|
24
|
+
["routes/quests.py"], False),
|
|
25
|
+
("Grace Hopper", "test: add coverage for quest routes", 140, 2,
|
|
26
|
+
["tests/test_quests.py"], False),
|
|
27
|
+
("Linus T.", "refactor: extract quest service layer", 210, 160,
|
|
28
|
+
["routes/quests.py", "services/quest_service.py"], False),
|
|
29
|
+
("Margaret H.", "feat: shiny new front-end dashboard", 320, 10,
|
|
30
|
+
["web/index.html", "web/app.js", "web/styles.css"], False),
|
|
31
|
+
("Margaret H.", "fix: race condition in websocket handler", 24, 18,
|
|
32
|
+
["web/app.js"], False),
|
|
33
|
+
("Linus T.", "docs: write the README and API guide", 90, 1,
|
|
34
|
+
["README.md", "docs/api.md"], False),
|
|
35
|
+
("Grace Hopper", "Merge branch 'feature/realtime'", 6, 0,
|
|
36
|
+
["web/app.js"], True),
|
|
37
|
+
("Dennis R.", "feat: add authentication middleware", 150, 12,
|
|
38
|
+
["middleware/auth.py", "app.py"], False),
|
|
39
|
+
("Dennis R.", "fix: hotfix expired token crash", 30, 22,
|
|
40
|
+
["middleware/auth.py"], False),
|
|
41
|
+
("Ada Lovelace", "refactor: tidy config and add type hints", 88, 96,
|
|
42
|
+
["config.py", "app.py"], False),
|
|
43
|
+
("Barbara L.", "build: bump deps and dockerize", 64, 9,
|
|
44
|
+
["Dockerfile", "requirements.txt", "docker-compose.yml"], False),
|
|
45
|
+
("Grace Hopper", "Merge pull request #42 from feature/auth", 8, 0,
|
|
46
|
+
["app.py"], True),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_demo_repo() -> RepoData:
|
|
51
|
+
"""Return a deterministic, fictional :class:`RepoData` for demo play."""
|
|
52
|
+
commits: list[CommitInfo] = []
|
|
53
|
+
for i, (author, summary, ins, dels, files, is_merge) in enumerate(_SCRIPT):
|
|
54
|
+
sha = f"{i:02d}" + "d" * 38 # stable, fake-but-valid-looking 40-char sha
|
|
55
|
+
email = author.lower().replace(" ", ".").replace(".", "") + "@gitquest.dev"
|
|
56
|
+
commits.append(
|
|
57
|
+
CommitInfo(
|
|
58
|
+
sha=sha,
|
|
59
|
+
short_sha=sha[:7],
|
|
60
|
+
author_name=author,
|
|
61
|
+
author_email=email,
|
|
62
|
+
timestamp=_BASE + timedelta(days=i, hours=i % 5),
|
|
63
|
+
summary=summary,
|
|
64
|
+
message=summary,
|
|
65
|
+
insertions=ins,
|
|
66
|
+
deletions=dels,
|
|
67
|
+
files=list(files),
|
|
68
|
+
is_merge=is_merge,
|
|
69
|
+
parent_count=2 if is_merge else 1,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return RepoData(
|
|
74
|
+
name="dragon-slayer-api (demo)",
|
|
75
|
+
path="<built-in demo>",
|
|
76
|
+
commits=commits,
|
|
77
|
+
sampled=False,
|
|
78
|
+
total_commits=len(commits),
|
|
79
|
+
)
|
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import hashlib
|
|
12
12
|
import json
|
|
13
|
+
import math
|
|
13
14
|
import random
|
|
14
15
|
from functools import lru_cache
|
|
15
16
|
from importlib import resources
|
|
@@ -110,19 +111,23 @@ def _make_monster(
|
|
|
110
111
|
name = rng.choice(spec["names"])
|
|
111
112
|
art = spec.get("art", "(o_o)")
|
|
112
113
|
|
|
114
|
+
# Compress churn with sqrt so a huge initial commit isn't 10x a small one,
|
|
115
|
+
# and apply an early-game ramp so the first rooms are gentle (the player
|
|
116
|
+
# has no levels or loot yet). Difficulty reaches full strength by ~room 10.
|
|
113
117
|
capped = min(churn, 600)
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
cf = math.sqrt(capped)
|
|
119
|
+
ramp = min(1.0, 0.45 + depth * 0.06)
|
|
120
|
+
jitter = rng.uniform(0.9, 1.15)
|
|
116
121
|
|
|
117
|
-
hp = int((
|
|
118
|
-
attack = int((
|
|
119
|
-
defense = int(
|
|
120
|
-
xp_reward = int(9 +
|
|
121
|
-
gold_reward = int(rng.randint(2, 8) +
|
|
122
|
+
hp = int((7 + cf * 0.9 + depth * 0.7) * ramp * jitter) + 1
|
|
123
|
+
attack = int((2 + cf * 0.22 + depth * 0.25) * ramp * jitter) + 1
|
|
124
|
+
defense = int((cf * 0.06 + depth * 0.10) * ramp)
|
|
125
|
+
xp_reward = int(9 + cf * 1.1 + depth * 1.6)
|
|
126
|
+
gold_reward = int(rng.randint(2, 8) + cf * 0.4 + depth * 0.5)
|
|
122
127
|
|
|
123
128
|
if is_boss:
|
|
124
|
-
hp = int(hp * 2.
|
|
125
|
-
attack = int(attack * 1.
|
|
129
|
+
hp = int(hp * 2.2) + 10
|
|
130
|
+
attack = int(attack * 1.35) + 2
|
|
126
131
|
defense = int(defense * 1.3) + 1
|
|
127
132
|
xp_reward = int(xp_reward * 3)
|
|
128
133
|
gold_reward = int(gold_reward * 3) + 10
|
|
@@ -214,7 +219,7 @@ def _build_room(
|
|
|
214
219
|
monsters.append(_make_monster(rng, "Boss", churn, index, depth_total, True))
|
|
215
220
|
else:
|
|
216
221
|
monsters.append(_make_monster(rng, archetype, churn, index, depth_total, False))
|
|
217
|
-
if churn >=
|
|
222
|
+
if churn >= 200 and index > 3 and rng.random() < 0.45:
|
|
218
223
|
monsters.append(
|
|
219
224
|
_make_monster(rng, archetype, churn // 2, index, depth_total, False)
|
|
220
225
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitquest
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Turn your git commit history into a playable ASCII dungeon roguelike.
|
|
5
5
|
Author-email: jithin-jz <jithinjzx@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -126,13 +126,30 @@ pip install .
|
|
|
126
126
|
|
|
127
127
|
## 🎮 Play
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Two commands — works on any machine, even with no repo around:
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
|
+
pip install gitquest
|
|
132
133
|
gitquest
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
If you run `gitquest` **inside a git repository**, it builds the dungeon from
|
|
137
|
+
*your* commit history. If you run it anywhere else, it automatically launches a
|
|
138
|
+
built-in **demo dungeon** so you can play right away.
|
|
139
|
+
|
|
140
|
+
`python -m gitquest` works too. Want your own repo? `cd` into it first, or pass
|
|
141
|
+
`--path`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
cd path/to/your/repo && gitquest
|
|
145
|
+
gitquest --path ../some-other-repo
|
|
146
|
+
gitquest --demo # force the built-in demo dungeon
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Combat controls
|
|
150
|
+
|
|
151
|
+
During a fight, type one key + Enter: `a` attack · `d` defend · `i` item ·
|
|
152
|
+
`r` run.
|
|
136
153
|
|
|
137
154
|
### Flags
|
|
138
155
|
|
|
@@ -140,6 +157,7 @@ gitquest
|
|
|
140
157
|
| ---------------- | --------------------------------------------------------- |
|
|
141
158
|
| `--path <repo>` | Play on another repository (default: current directory). |
|
|
142
159
|
| `--seed <n>` | Force a specific dungeon seed (default: derived from repo).|
|
|
160
|
+
| `--demo` | Play the built-in demo dungeon (no git repo required). |
|
|
143
161
|
| `--fast` | Skip animations and "press Enter" pauses. |
|
|
144
162
|
| `--stats-only` | Skip gameplay and print just the flex card. |
|
|
145
163
|
| `--max-commits` | Cap/sample commits for huge repos (default: 300). |
|
|
@@ -5,6 +5,7 @@ gitquest/__init__.py
|
|
|
5
5
|
gitquest/__main__.py
|
|
6
6
|
gitquest/cli.py
|
|
7
7
|
gitquest/combat.py
|
|
8
|
+
gitquest/demo.py
|
|
8
9
|
gitquest/engine.py
|
|
9
10
|
gitquest/entities.py
|
|
10
11
|
gitquest/flexcard.py
|
|
@@ -21,5 +22,6 @@ gitquest/data/items.json
|
|
|
21
22
|
gitquest/data/monsters.json
|
|
22
23
|
gitquest/data/ranks.json
|
|
23
24
|
tests/test_combat.py
|
|
25
|
+
tests/test_demo.py
|
|
24
26
|
tests/test_generator.py
|
|
25
27
|
tests/test_git_parser.py
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Tests for the built-in demo repo and a full auto playthrough."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from gitquest.demo import build_demo_repo
|
|
8
|
+
from gitquest.engine import GameEngine
|
|
9
|
+
from gitquest.entities import Item, ItemKind, Player
|
|
10
|
+
from gitquest.generator import generate_dungeon
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_demo_repo_is_valid():
|
|
14
|
+
repo = build_demo_repo()
|
|
15
|
+
assert len(repo.commits) >= 10
|
|
16
|
+
assert repo.merge_count >= 1
|
|
17
|
+
assert repo.head.is_merge # last demo commit is a merge -> boss finale
|
|
18
|
+
assert "Python" in repo.language_totals
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_demo_is_deterministic():
|
|
22
|
+
d1 = generate_dungeon(build_demo_repo())
|
|
23
|
+
d2 = generate_dungeon(build_demo_repo())
|
|
24
|
+
assert d1 == d2
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_auto_playthrough_completes_and_is_winnable():
|
|
28
|
+
"""The demo should be beatable by the auto strategy (with a starter potion)."""
|
|
29
|
+
dungeon = generate_dungeon(build_demo_repo())
|
|
30
|
+
player = Player(name="Hero")
|
|
31
|
+
player.inventory.append(
|
|
32
|
+
Item("Starter Health Draught", ItemKind.POTION, 15, description="Restores 15 HP.")
|
|
33
|
+
)
|
|
34
|
+
engine = GameEngine(
|
|
35
|
+
dungeon, player, rng=random.Random(dungeon.seed ^ 0x5EED), interactive=False
|
|
36
|
+
)
|
|
37
|
+
engine.run()
|
|
38
|
+
|
|
39
|
+
assert player.is_alive, "auto playthrough should survive the demo dungeon"
|
|
40
|
+
assert player.rooms_cleared == dungeon.size
|
|
41
|
+
assert player.bosses_slain >= 1
|
|
42
|
+
assert player.level > 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_first_room_is_survivable():
|
|
46
|
+
"""Early rooms must be gentle: the first monster should be weaker than the hero."""
|
|
47
|
+
dungeon = generate_dungeon(build_demo_repo())
|
|
48
|
+
first_monster = dungeon.rooms[0].monsters[0]
|
|
49
|
+
fresh = Player(name="Hero")
|
|
50
|
+
# The opening monster shouldn't out-stat a level-1 hero on both axes.
|
|
51
|
+
assert not (first_monster.hp > fresh.max_hp and first_monster.attack > fresh.attack)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|