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>/
|
||||
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
|
||||
combos.txt combos already in the deck, and one card away
|
||||
sim/ Forge runs, one triple per batch:
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-08 02:07Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:52Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:51Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: FAIL** (two-card vacuum combos: 7)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 15:56Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:37Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
_generated 2026-09-07 23:38Z by `tools/variant.py`_
|
||||
|
||||
- **bracket 3: PASS** (two-card vacuum combos: 0)
|
||||
- 86 lines in `decklist.txt`
|
||||
- 86 lines in `decklist.mgt`
|
||||
|
||||
## files
|
||||
|
||||
| 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 |
|
||||
| `combos.txt` | combos in the deck, and one card away |
|
||||
| `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
|
||||
│
|
||||
decklist.txt ─────────────────────────► │ blacklist.txt ──┐
|
||||
decklist.mgt ─────────────────────────► │ blacklist.txt ──┐
|
||||
│ │ │
|
||||
├─► bracket_check.py ── game changers, MLD, vacuum combos, blacklist ──┐
|
||||
├─► 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`,
|
||||
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
|
||||
|
||||
```
|
||||
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"
|
||||
plan.md
|
||||
data/ # ~1.2GB, all gitignored
|
||||
|
|
@ -327,7 +327,7 @@ reaper-king-no-more/
|
|||
variants.json AtomicCards.json embeddings.npz (generated)
|
||||
decks/ # RESULTS, one dir per archetype variant
|
||||
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}
|
||||
tools/
|
||||
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.
|
||||
3. ~~`bracket_check.py`~~ — **done and passing.**
|
||||
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.**
|
||||
4-player pod vs precons, reproducible by seed.
|
||||
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.)
|
||||
|
||||
**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.
|
||||
|
||||
## 6. Deck skeleton to draft against
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# correctly (the removal slot still does removal) even though the specific card
|
||||
# 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
|
||||
# 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__))
|
||||
subprocess.run([sys.executable, os.path.join(tools_dir, "to_dck.py"),
|
||||
"--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)
|
||||
import shutil as sh
|
||||
for p in POD:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ def variants(deck_root, only=None, resume=False):
|
|||
out = []
|
||||
for name in sorted(os.listdir(os.path.join(deck_root, "decks"))):
|
||||
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
|
||||
if only and name not in only:
|
||||
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
|
||||
r = subprocess.run([sys.executable, os.path.join(TOOLS_ROOT, "tools", "to_dck.py"),
|
||||
"--deck", deck_root,
|
||||
os.path.join(vdir, "decklist.txt"),
|
||||
os.path.join(vdir, "decklist.mgt"),
|
||||
"-o", os.path.join(dst, dck_name)],
|
||||
capture_output=True, text=True)
|
||||
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"),
|
||||
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)
|
||||
|
||||
# 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.
|
||||
|
||||
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:
|
||||
* <= 3 Game Changers (official)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ def main():
|
|||
# only cards actually in our decklist
|
||||
db = DB(blacklist_path=ctx.blacklist_path)
|
||||
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)
|
||||
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 --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
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def variant_order(ctx):
|
|||
except ImportError:
|
||||
pass
|
||||
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)
|
||||
return hand + rest
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ def main():
|
|||
rows = []
|
||||
for name in order:
|
||||
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):
|
||||
continue
|
||||
cmd, deck = load_decklist(deck_f)
|
||||
|
|
@ -107,7 +107,7 @@ def main():
|
|||
|
||||
slug = ctx.slug
|
||||
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",
|
||||
" combos.txt combos already in the deck, and one card away",
|
||||
" sim/ Forge runs, one triple per batch:",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Resolution order for the deck directory:
|
|||
|
||||
from deckctx import DeckContext
|
||||
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.identity # -> frozenset("BRG"), from the DB
|
||||
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):
|
||||
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):
|
||||
|
|
@ -103,7 +103,7 @@ class DeckContext:
|
|||
@property
|
||||
def decklist_path(self):
|
||||
return self.config.get("decklist") and self.path(self.config["decklist"]) \
|
||||
or self.path("decklist.txt")
|
||||
or self.path("decklist.mgt")
|
||||
|
||||
@property
|
||||
def blacklist_path(self):
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ def parse(log_path):
|
|||
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):
|
||||
L, add = [], lambda s: L.append(s)
|
||||
# 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]
|
||||
n_req = sys.argv[4] if len(sys.argv) > 4 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)
|
||||
if ours_name:
|
||||
OURS = ours_name
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ def main():
|
|||
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
|
||||
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)
|
||||
if "--list" in args:
|
||||
|
|
@ -203,7 +203,7 @@ def main():
|
|||
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")
|
||||
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")
|
||||
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 ''}")
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ def main():
|
|||
from cards import DB
|
||||
db = DB(blacklist_path=ctx.blacklist_path)
|
||||
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):
|
||||
base_path = ctx.decklist_path
|
||||
base = load(base_path)
|
||||
|
|
@ -87,7 +87,7 @@ def main():
|
|||
os.makedirs(vdir, exist_ok=True)
|
||||
header = [f"# {name}", f"# {why}", "", "# Commander",
|
||||
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")
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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"
|
||||
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"
|
||||
|
||||
OPPONENTS=(
|
||||
|
|
@ -33,7 +33,7 @@ OPPONENTS=(
|
|||
|
||||
# .dck filename and [metadata] Name= are derived from the deck (slug /
|
||||
# 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')"
|
||||
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
|
||||
|
||||
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
|
||||
"""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 [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).
|
||||
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:]]
|
||||
out = args[args.index("-o") + 1] if "-o" in args else \
|
||||
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)
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ def main():
|
|||
print(f"!! {len(missing)} card(s) Forge does not recognise:", file=sys.stderr)
|
||||
for m in missing:
|
||||
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:
|
||||
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:
|
||||
|
||||
<deck>/decks/<name>/
|
||||
decklist.txt the 100 cards
|
||||
decklist.mgt the 100 cards
|
||||
bracket.txt bracket-3 + house-rule check
|
||||
combos.txt combos in the deck, and one card away
|
||||
sim/ Forge runs: <stamp>.{log,txt,json}
|
||||
|
|
@ -38,7 +38,7 @@ def main():
|
|||
tools = os.path.join(TOOLS_ROOT, "tools")
|
||||
|
||||
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):
|
||||
sys.exit(f"no decklist at {deck}")
|
||||
os.makedirs(os.path.join(vdir, "sim"), exist_ok=True)
|
||||
|
|
@ -90,13 +90,13 @@ def main():
|
|||
f"_generated {stamp} by `tools/variant.py`_",
|
||||
"",
|
||||
f"- **bracket 3: {verdict}** (two-card vacuum combos: {vac})",
|
||||
f"- {n_deck} lines in `decklist.txt`",
|
||||
f"- {n_deck} lines in `decklist.mgt`",
|
||||
"",
|
||||
"## files",
|
||||
"",
|
||||
"| 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 |",
|
||||
"| `combos.txt` | combos in the deck, and one card away |",
|
||||
"| `sim/` | Forge runs — `.log` raw, `.txt` report, `.json` data |",
|
||||
|
|
|
|||
Loading…
Reference in a new issue