Sephiria inventory optimizer v0.1.0
- Slab catalog and effect handlers ported from WhiteDog1004/sephiria - Hill-climbing solver maximizes effect sum on slab-occupied cells - PIL renderer outputs PNG with effects overlay; downloads + caches slab images from img.sephiria.wiki on demand - Tkinter GUI for picking slabs by tier; CLI also available - Screenshot recognizer (template matching, beta) - build.bat / build.sh for portable single-file builds via PyInstaller
This commit is contained in:
113
sephiria_inv/__main__.py
Normal file
113
sephiria_inv/__main__.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Entry point: GUI by default, CLI when --cli is passed."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
from .renderer import save_solution
|
||||
from .slabs import SLABS, SLABS_BY_VALUE
|
||||
from .solver import solve
|
||||
|
||||
|
||||
def _cli(argv: List[str]) -> int:
|
||||
p = argparse.ArgumentParser(
|
||||
prog="sephiria_inv",
|
||||
description="Optimize Sephiria slab placement and render the result.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--cli", action="store_true",
|
||||
help="Run in CLI mode (no GUI).",
|
||||
)
|
||||
p.add_argument(
|
||||
"-s", "--slab", action="append", default=[],
|
||||
help="Slab value to include. Use value (e.g. 'harvesting') with optional ':N' "
|
||||
"multiplier (e.g. 'harvesting:3'). Repeat as needed.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--slots", type=int, default=34, help="Inventory slot count (18..60).",
|
||||
)
|
||||
p.add_argument("--seed", type=int, default=None)
|
||||
p.add_argument(
|
||||
"--time-limit", type=float, default=4.0,
|
||||
help="Solver time budget in seconds.",
|
||||
)
|
||||
p.add_argument(
|
||||
"-o", "--output", default="sephiria_layout.png",
|
||||
help="Output PNG path.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--list", action="store_true",
|
||||
help="List known slab values and exit.",
|
||||
)
|
||||
p.add_argument(
|
||||
"--no-download", action="store_true",
|
||||
help="Skip CDN download (text-only render).",
|
||||
)
|
||||
p.add_argument(
|
||||
"--screenshot", default=None,
|
||||
help="Read slab list from a game screenshot (PNG/JPG).",
|
||||
)
|
||||
p.add_argument(
|
||||
"--bbox", default=None,
|
||||
help="Required with --screenshot. Pixel bbox of the inventory grid as "
|
||||
"'left,top,right,bottom'.",
|
||||
)
|
||||
args = p.parse_args(argv)
|
||||
|
||||
if args.list:
|
||||
for s in SLABS:
|
||||
print(f" {s.value:<16s} {s.ko_label:<6s} ({s.tier})")
|
||||
return 0
|
||||
|
||||
basket: List[str] = []
|
||||
if args.screenshot:
|
||||
if not args.bbox:
|
||||
print("--screenshot 사용 시 --bbox 'l,t,r,b' 가 필요합니다.", file=sys.stderr)
|
||||
return 2
|
||||
try:
|
||||
l, t, r, b = (int(v) for v in args.bbox.split(","))
|
||||
except ValueError:
|
||||
print("--bbox 형식: left,top,right,bottom (정수)", file=sys.stderr)
|
||||
return 2
|
||||
from .screenshot import recognize, recognized_values
|
||||
recs = recognize(args.screenshot, (l, t, r, b), slot_num=args.slots)
|
||||
basket.extend(recognized_values(recs))
|
||||
print(f"인식된 석판: {len(basket)}개 from screenshot")
|
||||
for raw in args.slab:
|
||||
if ":" in raw:
|
||||
v, n = raw.split(":", 1)
|
||||
try:
|
||||
count = int(n)
|
||||
except ValueError:
|
||||
print(f"잘못된 개수: {raw}", file=sys.stderr)
|
||||
return 2
|
||||
else:
|
||||
v, count = raw, 1
|
||||
if v not in SLABS_BY_VALUE:
|
||||
print(f"알 수 없는 석판 value: {v} (use --list)", file=sys.stderr)
|
||||
return 2
|
||||
basket.extend([v] * count)
|
||||
|
||||
if not basket:
|
||||
print("최소 하나 이상의 --slab 을 지정하세요.", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
sol = solve(basket, slot_num=args.slots, time_limit=args.time_limit, seed=args.seed)
|
||||
save_solution(sol, args.output, download=not args.no_download)
|
||||
print(f"score={sol.score} placed={len(sol.placements)} → {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: List[str] | None = None) -> int:
|
||||
argv = list(argv if argv is not None else sys.argv[1:])
|
||||
if "--cli" in argv or "--list" in argv or any(a.startswith("-s") or a.startswith("--slab") for a in argv):
|
||||
return _cli(argv)
|
||||
# Fall back to GUI
|
||||
from .gui import main as gui_main
|
||||
return gui_main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user