Use new extension
This commit is contained in:
parent
dde4bd71ea
commit
1e177058e0
44 changed files with 65 additions and 65 deletions
|
|
@ -56,7 +56,7 @@ Each variant is a hypothesis: change a few cards, then measure what it does to l
|
||||||
|
|
||||||
```
|
```
|
||||||
decks/<variant>/
|
decks/<variant>/
|
||||||
decklist.txt the 100 cards, "N Card Name" per line
|
decklist.mgt the 100 cards, "N Card Name" per line
|
||||||
bracket.txt bracket-3 + house-rule check
|
bracket.txt bracket-3 + house-rule check
|
||||||
combos.txt combos already in the deck, and one card away
|
combos.txt combos already in the deck, and one card away
|
||||||
sim/ Forge runs, one triple per batch:
|
sim/ Forge runs, one triple per batch:
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-08 02:07Z by `tools/variant.py`_
|
_generated 2026-09-08 02:07Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:52Z by `tools/variant.py`_
|
_generated 2026-09-07 15:52Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: FAIL** (two-card vacuum combos: 7)
|
- **bracket 3: FAIL** (two-card vacuum combos: 7)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 15:56Z by `tools/variant.py`_
|
_generated 2026-09-07 15:56Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
||||||
|
|
||||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||||
- 86 lines in `decklist.txt`
|
- 86 lines in `decklist.mgt`
|
||||||
|
|
||||||
## files
|
## files
|
||||||
|
|
||||||
| file | what it is |
|
| file | what it is |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `decklist.txt` | the deck, `N Card Name` per line |
|
| `decklist.mgt` | the deck, `N Card Name` per line |
|
||||||
| `bracket.txt` | bracket-3 + house-rule check |
|
| `bracket.txt` | bracket-3 + house-rule check |
|
||||||
| `combos.txt` | combos in the deck, and one card away |
|
| `combos.txt` | combos in the deck, and one card away |
|
||||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ Card selection is offline and instant. Simulation is Forge, and it's the slow st
|
||||||
```
|
```
|
||||||
make data (once/per set) ──► data/*.jsonl, variants.json, AtomicCards.json
|
make data (once/per set) ──► data/*.jsonl, variants.json, AtomicCards.json
|
||||||
│
|
│
|
||||||
decklist.txt ─────────────────────────► │ blacklist.txt ──┐
|
decklist.mgt ─────────────────────────► │ blacklist.txt ──┐
|
||||||
│ │ │
|
│ │ │
|
||||||
├─► bracket_check.py ── game changers, MLD, vacuum combos, blacklist ──┐
|
├─► bracket_check.py ── game changers, MLD, vacuum combos, blacklist ──┐
|
||||||
├─► combos.py ── included / almostIncluded (offline) ──────────┤
|
├─► combos.py ── included / almostIncluded (offline) ──────────┤
|
||||||
|
|
@ -313,13 +313,13 @@ Card selection is offline and instant. Simulation is Forge, and it's the slow st
|
||||||
|
|
||||||
Every simulation run writes a timestamped triple into `out/`: the full Forge `.log`,
|
Every simulation run writes a timestamped triple into `out/`: the full Forge `.log`,
|
||||||
a human `.txt` report, and a `.json` artifact — and the report footer names
|
a human `.txt` report, and a `.json` artifact — and the report footer names
|
||||||
`decklist.txt`, the log, the JSON, and itself, so any number is traceable.
|
`decklist.mgt`, the log, the JSON, and itself, so any number is traceable.
|
||||||
|
|
||||||
## 4. Repo layout
|
## 4. Repo layout
|
||||||
|
|
||||||
```
|
```
|
||||||
reaper-king-no-more/
|
reaper-king-no-more/
|
||||||
decklist.txt # working deck, "1 Card Name" per line
|
decklist.mgt # working deck, "1 Card Name" per line
|
||||||
blacklist.txt # cards I won't play, "Card Name | reason"
|
blacklist.txt # cards I won't play, "Card Name | reason"
|
||||||
plan.md
|
plan.md
|
||||||
data/ # ~1.2GB, all gitignored
|
data/ # ~1.2GB, all gitignored
|
||||||
|
|
@ -327,7 +327,7 @@ reaper-king-no-more/
|
||||||
variants.json AtomicCards.json embeddings.npz (generated)
|
variants.json AtomicCards.json embeddings.npz (generated)
|
||||||
decks/ # RESULTS, one dir per archetype variant
|
decks/ # RESULTS, one dir per archetype variant
|
||||||
README.md
|
README.md
|
||||||
no-mikaeus/ # decklist.txt, bracket.txt, combos.txt,
|
no-mikaeus/ # decklist.mgt, bracket.txt, combos.txt,
|
||||||
mikaeus/ # README.md, sim/<stamp>.{log,txt,json}
|
mikaeus/ # README.md, sim/<stamp>.{log,txt,json}
|
||||||
tools/
|
tools/
|
||||||
cards.py # shared index: oracle+tags+edhrec+combos+blacklist
|
cards.py # shared index: oracle+tags+edhrec+combos+blacklist
|
||||||
|
|
@ -363,7 +363,7 @@ insists Commander decks live).
|
||||||
2. ~~`cards.py`~~ — **done.** oracle + 4,369 tags + edhrecRank + combos + blacklist.
|
2. ~~`cards.py`~~ — **done.** oracle + 4,369 tags + edhrecRank + combos + blacklist.
|
||||||
3. ~~`bracket_check.py`~~ — **done and passing.**
|
3. ~~`bracket_check.py`~~ — **done and passing.**
|
||||||
4. ~~`embed.py` / `similar.py` / `similar_text.py`~~ — **done** (Layer B).
|
4. ~~`embed.py` / `similar.py` / `similar_text.py`~~ — **done** (Layer B).
|
||||||
5. ~~Draft the 100~~ — **done**, `decklist.txt`.
|
5. ~~Draft the 100~~ — **done**, `decklist.mgt`.
|
||||||
6. ~~Forge installed + `to_dck.py` + `run_forge.sh` + `forge_report.py`~~ — **done.**
|
6. ~~Forge installed + `to_dck.py` + `run_forge.sh` + `forge_report.py`~~ — **done.**
|
||||||
4-player pod vs precons, reproducible by seed.
|
4-player pod vs precons, reproducible by seed.
|
||||||
7. Iterate on the list using Forge results + the offline combo/tag tools.
|
7. Iterate on the list using Forge results + the offline combo/tag tools.
|
||||||
|
|
@ -375,7 +375,7 @@ with the real one is worse than having one. Forge is the simulator. (`goldfish.p
|
||||||
and its `out/goldfish-*` runs are removed.)
|
and its `out/goldfish-*` runs are removed.)
|
||||||
|
|
||||||
**Report convention** (every tool): a timestamped `.txt` human report plus a `.json`
|
**Report convention** (every tool): a timestamped `.txt` human report plus a `.json`
|
||||||
artifact in `out/`, with the report footer naming `decklist.txt`, the raw log, the
|
artifact in `out/`, with the report footer naming `decklist.mgt`, the raw log, the
|
||||||
JSON, and itself.
|
JSON, and itself.
|
||||||
|
|
||||||
## 6. Deck skeleton to draft against
|
## 6. Deck skeleton to draft against
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# correctly (the removal slot still does removal) even though the specific card
|
# correctly (the removal slot still does removal) even though the specific card
|
||||||
# differs.
|
# differs.
|
||||||
#
|
#
|
||||||
# decklist.txt remains the source of truth. This map applies ONLY when building
|
# decklist.mgt remains the source of truth. This map applies ONLY when building
|
||||||
# the .dck for simulation, and every substitution is recorded in the run's JSON
|
# the .dck for simulation, and every substitution is recorded in the run's JSON
|
||||||
# metadata and printed in reports.
|
# metadata and printed in reports.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ def run_arm(ctx, label, variant, games, seed, profile, overlay, clock, workdir):
|
||||||
tools_dir = os.path.dirname(os.path.abspath(__file__))
|
tools_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
subprocess.run([sys.executable, os.path.join(tools_dir, "to_dck.py"),
|
subprocess.run([sys.executable, os.path.join(tools_dir, "to_dck.py"),
|
||||||
"--deck", ctx.dir,
|
"--deck", ctx.dir,
|
||||||
os.path.join(ctx.dir, "decks", variant, "decklist.txt"),
|
os.path.join(ctx.dir, "decks", variant, "decklist.mgt"),
|
||||||
"-o", os.path.join(dst, dck_name)], capture_output=True)
|
"-o", os.path.join(dst, dck_name)], capture_output=True)
|
||||||
import shutil as sh
|
import shutil as sh
|
||||||
for p in POD:
|
for p in POD:
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ def variants(deck_root, only=None, resume=False):
|
||||||
out = []
|
out = []
|
||||||
for name in sorted(os.listdir(os.path.join(deck_root, "decks"))):
|
for name in sorted(os.listdir(os.path.join(deck_root, "decks"))):
|
||||||
d = os.path.join(deck_root, "decks", name)
|
d = os.path.join(deck_root, "decks", name)
|
||||||
if not os.path.isdir(d) or not os.path.exists(os.path.join(d, "decklist.txt")):
|
if not os.path.isdir(d) or not os.path.exists(os.path.join(d, "decklist.mgt")):
|
||||||
continue
|
continue
|
||||||
if only and name not in only:
|
if only and name not in only:
|
||||||
continue
|
continue
|
||||||
|
|
@ -122,7 +122,7 @@ def run_variant(ctx, name, games, seed, wid, pod_idx):
|
||||||
# build this variant's .dck into the worker's own profile
|
# build this variant's .dck into the worker's own profile
|
||||||
r = subprocess.run([sys.executable, os.path.join(TOOLS_ROOT, "tools", "to_dck.py"),
|
r = subprocess.run([sys.executable, os.path.join(TOOLS_ROOT, "tools", "to_dck.py"),
|
||||||
"--deck", deck_root,
|
"--deck", deck_root,
|
||||||
os.path.join(vdir, "decklist.txt"),
|
os.path.join(vdir, "decklist.mgt"),
|
||||||
"-o", os.path.join(dst, dck_name)],
|
"-o", os.path.join(dst, dck_name)],
|
||||||
capture_output=True, text=True)
|
capture_output=True, text=True)
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
|
|
@ -160,7 +160,7 @@ def run_variant(ctx, name, games, seed, wid, pod_idx):
|
||||||
|
|
||||||
subprocess.run([sys.executable, os.path.join(TOOLS_ROOT, "tools", "forge_report.py"),
|
subprocess.run([sys.executable, os.path.join(TOOLS_ROOT, "tools", "forge_report.py"),
|
||||||
out + ".log", out + ".json", out + ".txt", str(games), str(seed),
|
out + ".log", out + ".json", out + ".txt", str(games), str(seed),
|
||||||
os.path.join(vdir, "decklist.txt"), ctx.forge_metadata_name()],
|
os.path.join(vdir, "decklist.mgt"), ctx.forge_metadata_name()],
|
||||||
capture_output=True)
|
capture_output=True)
|
||||||
|
|
||||||
# Stamp the run config so tuned and stock arms are never silently compared.
|
# Stamp the run config so tuned and stock arms are never silently compared.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"""Check a decklist against Commander Bracket 3 + our house rules.
|
"""Check a decklist against Commander Bracket 3 + our house rules.
|
||||||
|
|
||||||
python3 tools/bracket_check.py --deck decks/reaper-king-no-more
|
python3 tools/bracket_check.py --deck decks/reaper-king-no-more
|
||||||
python3 tools/bracket_check.py --deck decks/reaper-king-no-more decks/reaper-king-no-more/decks/no-mikaeus/decklist.txt
|
python3 tools/bracket_check.py --deck decks/reaper-king-no-more decks/reaper-king-no-more/decks/no-mikaeus/decklist.mgt
|
||||||
|
|
||||||
Enforces:
|
Enforces:
|
||||||
* <= 3 Game Changers (official)
|
* <= 3 Game Changers (official)
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ def main():
|
||||||
# only cards actually in our decklist
|
# only cards actually in our decklist
|
||||||
db = DB(blacklist_path=ctx.blacklist_path)
|
db = DB(blacklist_path=ctx.blacklist_path)
|
||||||
variant_dir = os.path.dirname(os.path.dirname(log))
|
variant_dir = os.path.dirname(os.path.dirname(log))
|
||||||
deck_file = os.path.join(variant_dir, "decklist.txt")
|
deck_file = os.path.join(variant_dir, "decklist.mgt")
|
||||||
commander, deck = load_decklist(deck_file)
|
commander, deck = load_decklist(deck_file)
|
||||||
deck_set = set(deck) | {commander}
|
deck_set = set(deck) | {commander}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ against the local variants.json. No API calls.
|
||||||
|
|
||||||
python3 tools/combos.py --deck decks/reaper-king-no-more
|
python3 tools/combos.py --deck decks/reaper-king-no-more
|
||||||
python3 tools/combos.py --deck decks/reaper-king-no-more --almost 30
|
python3 tools/combos.py --deck decks/reaper-king-no-more --almost 30
|
||||||
python3 tools/combos.py --deck decks/reaper-king-no-more --deck-file some/decklist.txt
|
python3 tools/combos.py --deck decks/reaper-king-no-more --deck-file some/decklist.mgt
|
||||||
"""
|
"""
|
||||||
import sys, os, json
|
import sys, os, json
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ def variant_order(ctx):
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
rest = sorted(os.path.basename(os.path.dirname(p))
|
rest = sorted(os.path.basename(os.path.dirname(p))
|
||||||
for p in glob.glob(ctx.path("decks", "*", "decklist.txt"))
|
for p in glob.glob(ctx.path("decks", "*", "decklist.mgt"))
|
||||||
if os.path.basename(os.path.dirname(p)) not in hand)
|
if os.path.basename(os.path.dirname(p)) not in hand)
|
||||||
return hand + rest
|
return hand + rest
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ def main():
|
||||||
rows = []
|
rows = []
|
||||||
for name in order:
|
for name in order:
|
||||||
vdir = os.path.join(DECKS, name)
|
vdir = os.path.join(DECKS, name)
|
||||||
deck_f = os.path.join(vdir, "decklist.txt")
|
deck_f = os.path.join(vdir, "decklist.mgt")
|
||||||
if not os.path.exists(deck_f):
|
if not os.path.exists(deck_f):
|
||||||
continue
|
continue
|
||||||
cmd, deck = load_decklist(deck_f)
|
cmd, deck = load_decklist(deck_f)
|
||||||
|
|
@ -107,7 +107,7 @@ def main():
|
||||||
|
|
||||||
slug = ctx.slug
|
slug = ctx.slug
|
||||||
L += ["## layout", "", "```", "decks/<variant>/",
|
L += ["## layout", "", "```", "decks/<variant>/",
|
||||||
" decklist.txt the 100 cards, \"N Card Name\" per line",
|
" decklist.mgt the 100 cards, \"N Card Name\" per line",
|
||||||
" bracket.txt bracket-3 + house-rule check",
|
" bracket.txt bracket-3 + house-rule check",
|
||||||
" combos.txt combos already in the deck, and one card away",
|
" combos.txt combos already in the deck, and one card away",
|
||||||
" sim/ Forge runs, one triple per batch:",
|
" sim/ Forge runs, one triple per batch:",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Resolution order for the deck directory:
|
||||||
|
|
||||||
from deckctx import DeckContext
|
from deckctx import DeckContext
|
||||||
ctx = DeckContext.from_args() # pops --deck out of sys.argv
|
ctx = DeckContext.from_args() # pops --deck out of sys.argv
|
||||||
ctx.path("decklist.txt") # -> decks/<name>/decklist.txt
|
ctx.path("decklist.mgt") # -> decks/<name>/decklist.mgt
|
||||||
ctx.commander # -> "The Reaper, King No More"
|
ctx.commander # -> "The Reaper, King No More"
|
||||||
ctx.identity # -> frozenset("BRG"), from the DB
|
ctx.identity # -> frozenset("BRG"), from the DB
|
||||||
ctx.slug # -> "reaper-king-no-more"
|
ctx.slug # -> "reaper-king-no-more"
|
||||||
|
|
@ -30,7 +30,7 @@ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
def _looks_like_deck_dir(d):
|
def _looks_like_deck_dir(d):
|
||||||
return os.path.exists(os.path.join(d, "config.yaml")) or \
|
return os.path.exists(os.path.join(d, "config.yaml")) or \
|
||||||
os.path.exists(os.path.join(d, "decklist.txt"))
|
os.path.exists(os.path.join(d, "decklist.mgt"))
|
||||||
|
|
||||||
|
|
||||||
def resolve_deck_dir(argv=None, pop=True):
|
def resolve_deck_dir(argv=None, pop=True):
|
||||||
|
|
@ -103,7 +103,7 @@ class DeckContext:
|
||||||
@property
|
@property
|
||||||
def decklist_path(self):
|
def decklist_path(self):
|
||||||
return self.config.get("decklist") and self.path(self.config["decklist"]) \
|
return self.config.get("decklist") and self.path(self.config["decklist"]) \
|
||||||
or self.path("decklist.txt")
|
or self.path("decklist.mgt")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def blacklist_path(self):
|
def blacklist_path(self):
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ def parse(log_path):
|
||||||
return games
|
return games
|
||||||
|
|
||||||
|
|
||||||
def report(games, n_req, seed, log, jsonp, txtp, deck_path="decklist.txt",
|
def report(games, n_req, seed, log, jsonp, txtp, deck_path="decklist.mgt",
|
||||||
tally=None, tally_total=0, timeouts=0):
|
tally=None, tally_total=0, timeouts=0):
|
||||||
L, add = [], lambda s: L.append(s)
|
L, add = [], lambda s: L.append(s)
|
||||||
# prefer the authoritative Match Result tally over per-game lines
|
# prefer the authoritative Match Result tally over per-game lines
|
||||||
|
|
@ -209,7 +209,7 @@ def main():
|
||||||
log, jsonp, txtp = sys.argv[1], sys.argv[2], sys.argv[3]
|
log, jsonp, txtp = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
n_req = sys.argv[4] if len(sys.argv) > 4 else "?"
|
n_req = sys.argv[4] if len(sys.argv) > 4 else "?"
|
||||||
seed = sys.argv[5] if len(sys.argv) > 5 else "?"
|
seed = sys.argv[5] if len(sys.argv) > 5 else "?"
|
||||||
deck_path = sys.argv[6] if len(sys.argv) > 6 else "decklist.txt"
|
deck_path = sys.argv[6] if len(sys.argv) > 6 else "decklist.mgt"
|
||||||
ours_name = sys.argv[7] if len(sys.argv) > 7 else deck_name_for(deck_path)
|
ours_name = sys.argv[7] if len(sys.argv) > 7 else deck_name_for(deck_path)
|
||||||
if ours_name:
|
if ours_name:
|
||||||
OURS = ours_name
|
OURS = ours_name
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ def main():
|
||||||
only = args[args.index("--only") + 1] if "--only" in args else None
|
only = args[args.index("--only") + 1] if "--only" in args else None
|
||||||
stats = args[args.index("--from-stats") + 1] if "--from-stats" in args else None
|
stats = args[args.index("--from-stats") + 1] if "--from-stats" in args else None
|
||||||
base_path = args[args.index("--base") + 1] if "--base" in args else \
|
base_path = args[args.index("--base") + 1] if "--base" in args else \
|
||||||
ctx.path(ctx.config.get("generate_base", "decks/no-mikaeus/decklist.txt"))
|
ctx.path(ctx.config.get("generate_base", "decks/no-mikaeus/decklist.mgt"))
|
||||||
|
|
||||||
recipes = load_recipes(ctx)
|
recipes = load_recipes(ctx)
|
||||||
if "--list" in args:
|
if "--list" in args:
|
||||||
|
|
@ -203,7 +203,7 @@ def main():
|
||||||
body = [f"1 {n}" for n in sorted(set(res["deck"]) - {"Swamp", "Forest", "Mountain"})]
|
body = [f"1 {n}" for n in sorted(set(res["deck"]) - {"Swamp", "Forest", "Mountain"})]
|
||||||
basics = [f"{Counter(res['deck'])[b]} {b}" for b in ("Swamp", "Forest", "Mountain")
|
basics = [f"{Counter(res['deck'])[b]} {b}" for b in ("Swamp", "Forest", "Mountain")
|
||||||
if Counter(res["deck"])[b]]
|
if Counter(res["deck"])[b]]
|
||||||
open(os.path.join(vdir, "decklist.txt"), "w").write(
|
open(os.path.join(vdir, "decklist.mgt"), "w").write(
|
||||||
"\n".join(header + body + basics) + "\n")
|
"\n".join(header + body + basics) + "\n")
|
||||||
print(f"{name:18} ok -{len(res['cuts'])} +{len(res['adds'])}")
|
print(f"{name:18} ok -{len(res['cuts'])} +{len(res['adds'])}")
|
||||||
print(f"{'':18} cut: {', '.join(res['cuts'][:4])}{'...' if len(res['cuts'])>4 else ''}")
|
print(f"{'':18} cut: {', '.join(res['cuts'][:4])}{'...' if len(res['cuts'])>4 else ''}")
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ def main():
|
||||||
from cards import DB
|
from cards import DB
|
||||||
db = DB(blacklist_path=ctx.blacklist_path)
|
db = DB(blacklist_path=ctx.blacklist_path)
|
||||||
commander = ctx.commander
|
commander = ctx.commander
|
||||||
base_path = ctx.path("decks", "no-mikaeus", "decklist.txt")
|
base_path = ctx.path("decks", "no-mikaeus", "decklist.mgt")
|
||||||
if not os.path.exists(base_path):
|
if not os.path.exists(base_path):
|
||||||
base_path = ctx.decklist_path
|
base_path = ctx.decklist_path
|
||||||
base = load(base_path)
|
base = load(base_path)
|
||||||
|
|
@ -87,7 +87,7 @@ def main():
|
||||||
os.makedirs(vdir, exist_ok=True)
|
os.makedirs(vdir, exist_ok=True)
|
||||||
header = [f"# {name}", f"# {why}", "", "# Commander",
|
header = [f"# {name}", f"# {why}", "", "# Commander",
|
||||||
f"1 {commander}", "", "# Main"]
|
f"1 {commander}", "", "# Main"]
|
||||||
open(os.path.join(vdir, "decklist.txt"), "w").write(
|
open(os.path.join(vdir, "decklist.mgt"), "w").write(
|
||||||
"\n".join(header + lines) + "\n")
|
"\n".join(header + lines) + "\n")
|
||||||
|
|
||||||
flags = []
|
flags = []
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ allocation. This module defines the roles (as verified Scryfall Tagger slugs plu
|
||||||
oracle-text fallbacks) and can classify any card or score a whole decklist.
|
oracle-text fallbacks) and can classify any card or score a whole decklist.
|
||||||
|
|
||||||
python3 tools/roles.py --deck decks/reaper-king-no-more
|
python3 tools/roles.py --deck decks/reaper-king-no-more
|
||||||
python3 tools/roles.py --deck decks/reaper-king-no-more decks/reaper-king-no-more/decks/fast-ramp/decklist.txt
|
python3 tools/roles.py --deck decks/reaper-king-no-more decks/reaper-king-no-more/decks/fast-ramp/decklist.mgt
|
||||||
"""
|
"""
|
||||||
import sys, os
|
import sys, os
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
VDIR="$DECK_DIR/decks/$VARIANT"
|
VDIR="$DECK_DIR/decks/$VARIANT"
|
||||||
OUT="$VDIR/sim/$STAMP"
|
OUT="$VDIR/sim/$STAMP"
|
||||||
|
|
||||||
[ -f "$VDIR/decklist.txt" ] || { echo "no decklist: $VDIR/decklist.txt" >&2; exit 1; }
|
[ -f "$VDIR/decklist.mgt" ] || { echo "no decklist: $VDIR/decklist.mgt" >&2; exit 1; }
|
||||||
mkdir -p "$VDIR/sim"
|
mkdir -p "$VDIR/sim"
|
||||||
|
|
||||||
OPPONENTS=(
|
OPPONENTS=(
|
||||||
|
|
@ -33,7 +33,7 @@ OPPONENTS=(
|
||||||
|
|
||||||
# .dck filename and [metadata] Name= are derived from the deck (slug /
|
# .dck filename and [metadata] Name= are derived from the deck (slug /
|
||||||
# config.yaml `name:`), never hardcoded -- ask to_dck.py what it wrote.
|
# config.yaml `name:`), never hardcoded -- ask to_dck.py what it wrote.
|
||||||
DCK_NAME="$(python3 "$TOOLS/to_dck.py" --deck "$DECK_DIR" "$VDIR/decklist.txt" \
|
DCK_NAME="$(python3 "$TOOLS/to_dck.py" --deck "$DECK_DIR" "$VDIR/decklist.mgt" \
|
||||||
-o "$FORGE_DECKS/$(basename "$DECK_DIR").dck" | sed -n 's/^wrote \(.*\) (.*/\1/p')"
|
-o "$FORGE_DECKS/$(basename "$DECK_DIR").dck" | sed -n 's/^wrote \(.*\) (.*/\1/p')"
|
||||||
DCK_NAME="$(basename "$DCK_NAME")"
|
DCK_NAME="$(basename "$DCK_NAME")"
|
||||||
|
|
||||||
|
|
@ -48,4 +48,4 @@ echo "[$VARIANT] $GAMES games, 4-player Commander pod, seed $SEED"
|
||||||
| stdbuf -oL grep -E "^Game Result:" || true
|
| stdbuf -oL grep -E "^Game Result:" || true
|
||||||
|
|
||||||
python3 "$TOOLS/forge_report.py" "$OUT.log" "$OUT.json" "$OUT.txt" \
|
python3 "$TOOLS/forge_report.py" "$OUT.log" "$OUT.json" "$OUT.txt" \
|
||||||
"$GAMES" "$SEED" "$VDIR/decklist.txt"
|
"$GAMES" "$SEED" "$VDIR/decklist.mgt"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Convert decklist.txt -> Forge .dck format.
|
"""Convert decklist.mgt -> Forge .dck format.
|
||||||
|
|
||||||
python3 tools/to_dck.py --deck decks/reaper-king-no-more
|
python3 tools/to_dck.py --deck decks/reaper-king-no-more
|
||||||
python3 tools/to_dck.py --deck decks/reaper-king-no-more [decklist.txt] [-o ~/forge/decks/<slug>.dck]
|
python3 tools/to_dck.py --deck decks/reaper-king-no-more [decklist.mgt] [-o ~/forge/decks/<slug>.dck]
|
||||||
|
|
||||||
Forge sections are [Commander], [Main], [Sideboard] (verified in DeckSection.java).
|
Forge sections are [Commander], [Main], [Sideboard] (verified in DeckSection.java).
|
||||||
Card names must match Forge's own database, so this validates against
|
Card names must match Forge's own database, so this validates against
|
||||||
|
|
@ -70,7 +70,7 @@ def main():
|
||||||
args = [a for a in sys.argv[1:]]
|
args = [a for a in sys.argv[1:]]
|
||||||
out = args[args.index("-o") + 1] if "-o" in args else \
|
out = args[args.index("-o") + 1] if "-o" in args else \
|
||||||
os.path.join(FORGE, "decks", ctx.dck_filename())
|
os.path.join(FORGE, "decks", ctx.dck_filename())
|
||||||
src = next((a for a in args if a.endswith(".txt")), ctx.decklist_path)
|
src = next((a for a in args if a.endswith(".mgt")), ctx.decklist_path)
|
||||||
|
|
||||||
commander, deck = load_decklist(src)
|
commander, deck = load_decklist(src)
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ def main():
|
||||||
print(f"!! {len(missing)} card(s) Forge does not recognise:", file=sys.stderr)
|
print(f"!! {len(missing)} card(s) Forge does not recognise:", file=sys.stderr)
|
||||||
for m in missing:
|
for m in missing:
|
||||||
print(f" {m}", file=sys.stderr)
|
print(f" {m}", file=sys.stderr)
|
||||||
print(" (fix the name in decklist.txt, or Forge will drop it)\n", file=sys.stderr)
|
print(" (fix the name in decklist.mgt, or Forge will drop it)\n", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print("warning: could not read Forge's card list; skipping validation", file=sys.stderr)
|
print("warning: could not read Forge's card list; skipping validation", file=sys.stderr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
Each variant directory ends up self-contained:
|
Each variant directory ends up self-contained:
|
||||||
|
|
||||||
<deck>/decks/<name>/
|
<deck>/decks/<name>/
|
||||||
decklist.txt the 100 cards
|
decklist.mgt the 100 cards
|
||||||
bracket.txt bracket-3 + house-rule check
|
bracket.txt bracket-3 + house-rule check
|
||||||
combos.txt combos in the deck, and one card away
|
combos.txt combos in the deck, and one card away
|
||||||
sim/ Forge runs: <stamp>.{log,txt,json}
|
sim/ Forge runs: <stamp>.{log,txt,json}
|
||||||
|
|
@ -38,7 +38,7 @@ def main():
|
||||||
tools = os.path.join(TOOLS_ROOT, "tools")
|
tools = os.path.join(TOOLS_ROOT, "tools")
|
||||||
|
|
||||||
vdir = ctx.path("decks", name)
|
vdir = ctx.path("decks", name)
|
||||||
deck = os.path.join(vdir, "decklist.txt")
|
deck = os.path.join(vdir, "decklist.mgt")
|
||||||
if not os.path.exists(deck):
|
if not os.path.exists(deck):
|
||||||
sys.exit(f"no decklist at {deck}")
|
sys.exit(f"no decklist at {deck}")
|
||||||
os.makedirs(os.path.join(vdir, "sim"), exist_ok=True)
|
os.makedirs(os.path.join(vdir, "sim"), exist_ok=True)
|
||||||
|
|
@ -90,13 +90,13 @@ def main():
|
||||||
f"_generated {stamp} by `tools/variant.py`_",
|
f"_generated {stamp} by `tools/variant.py`_",
|
||||||
"",
|
"",
|
||||||
f"- **bracket 3: {verdict}** (two-card vacuum combos: {vac})",
|
f"- **bracket 3: {verdict}** (two-card vacuum combos: {vac})",
|
||||||
f"- {n_deck} lines in `decklist.txt`",
|
f"- {n_deck} lines in `decklist.mgt`",
|
||||||
"",
|
"",
|
||||||
"## files",
|
"## files",
|
||||||
"",
|
"",
|
||||||
"| file | what it is |",
|
"| file | what it is |",
|
||||||
"| --- | --- |",
|
"| --- | --- |",
|
||||||
"| `decklist.txt` | the deck, `N Card Name` per line |",
|
"| `decklist.mgt` | the deck, `N Card Name` per line |",
|
||||||
"| `bracket.txt` | bracket-3 + house-rule check |",
|
"| `bracket.txt` | bracket-3 + house-rule check |",
|
||||||
"| `combos.txt` | combos in the deck, and one card away |",
|
"| `combos.txt` | combos in the deck, and one card away |",
|
||||||
"| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |",
|
"| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue