feat: replace ENABLE_SMART_LANDS env var with per-build checkbox in New Deck modal (#64)

This commit is contained in:
mwisnowski 2026-03-25 22:08:25 -07:00 committed by GitHub
parent 94765622ba
commit b808f411ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1209 additions and 5046 deletions

View file

@ -69,7 +69,6 @@ ENABLE_CARD_DETAILS=1 # dockerhub: ENABLE_CARD_DETAILS="1"
SIMILARITY_CACHE_ENABLED=1 # dockerhub: SIMILARITY_CACHE_ENABLED="1"
SIMILARITY_CACHE_PATH="card_files/similarity_cache.parquet" # Path to Parquet cache file
ENABLE_BATCH_BUILD=1 # dockerhub: ENABLE_BATCH_BUILD="1" (enable Build X and Compare feature)
ENABLE_SMART_LANDS=1 # dockerhub: ENABLE_SMART_LANDS="1" (1=enable smart land base analysis; 0=use fixed defaults)
# LAND_PROFILE=mid # Optional: force a land profile (basics|mid|fixing); skips auto-detection
# LAND_COUNT=36 # Optional: force total land count; skips curve calculation

View file

@ -9,7 +9,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning
## [Unreleased]
### Added
_No unreleased changes yet_
- **Smart Land Bases checkbox**: The New Deck modal Preferences section now has a **Smart Land Bases** checkbox (checked by default) to enable or disable smart land analysis per-build, replacing the `ENABLE_SMART_LANDS` environment variable.
### Changed
_No unreleased changes yet_
@ -18,7 +18,7 @@ _No unreleased changes yet_
_No unreleased changes yet_
### Removed
_No unreleased changes yet_
- **`ENABLE_SMART_LANDS` environment variable**: Removed in favor of the per-build checkbox in the New Deck modal. Use `LAND_PROFILE` or `LAND_COUNT` for headless/CLI control.
## [4.3.1] - 2026-03-25
### Added

View file

@ -195,13 +195,13 @@ Enable cost-aware deck building with `ENABLE_BUDGET_MODE=1` (default). A per-car
## Smart Land Bases
Enable automatic land count and profile selection with `ENABLE_SMART_LANDS=1` (default). Each build analyses the commander's speed and the card pool's color-pip intensity to pick a land base profile.
Enable or disable smart land analysis per-build via the **Smart Land Bases** checkbox in the Preferences section of the New Deck modal (checked by default). Each build analyses the commander's speed and the card pool's color-pip intensity to pick a land base profile.
- **Basics-heavy**: 12 color decks or low-pip pools. ~60% basics, reduced ETB-tapped tolerance.
- **Balanced**: 23 color decks with moderate pip density. Standard ratios and ETB thresholds.
- **Fixing-heavy**: 3+ colors or high pip density (≥15 double-pip or ≥3 triple-or-more-pip cards). Minimal basics, raised ETB-tapped tolerance.
- Land targets: fast decks (commander CMC < 3) get 33 lands; slow decks (CMC > 4) get 3739.
- Override with `LAND_PROFILE=basics|mid|fixing` or `LAND_COUNT=<n>` to bypass auto-detection.
- Override with `LAND_PROFILE=basics|mid|fixing` or `LAND_COUNT=<n>` to bypass auto-detection (useful for headless/CLI builds).
- The **Land Summary** section of each deck result shows a "Smart Lands" notice explaining the chosen profile.
- See [`docs/user_guides/land_bases.md`](docs/user_guides/land_bases.md) for the full guide.
@ -290,7 +290,6 @@ See `.env.example` for the full catalog. Common knobs:
| `WEB_VIRTUALIZE` | `1` | Opt-in to virtualized lists/grids for large result sets. |
| `ALLOW_MUST_HAVES` | `1` | Enable include/exclude enforcement in Step 5. |
| `SHOW_MUST_HAVE_BUTTONS` | `0` | Surface the must include/exclude buttons and quick-add UI (requires `ALLOW_MUST_HAVES=1`). |
| `ENABLE_SMART_LANDS` | `1` | Enable automatic land count and profile selection based on commander speed and pip density. |
| `LAND_PROFILE` | _(auto)_ | Force a land profile: `basics`, `mid`, or `fixing`. Skips auto-detection. |
| `LAND_COUNT` | _(auto)_ | Force total land count (e.g. `36`). Skips curve calculation. |
| `ENABLE_BUDGET_MODE` | `1` | Enable budget mode controls (per-card ceiling, soft enforcement) and price display throughout the builder. |

View file

@ -2,7 +2,7 @@
## [Unreleased]
### Added
_No unreleased changes yet_
- **Smart Land Bases checkbox**: The New Deck modal now has a **Smart Land Bases** checkbox in the Preferences section (checked by default). Enables or disables smart land analysis per-build without needing environment variables.
### Changed
_No unreleased changes yet_
@ -11,7 +11,7 @@ _No unreleased changes yet_
_No unreleased changes yet_
### Removed
_No unreleased changes yet_
- **`ENABLE_SMART_LANDS` environment variable**: Replaced by the per-build checkbox. Use `LAND_PROFILE` or `LAND_COUNT` for headless overrides.
## [4.3.1] - 2026-03-25
### Added

View file

@ -10,8 +10,8 @@ from .. import builder_utils as bu
"""Phase 2 (pre-step): Smart land base analysis (Roadmap 14, M1).
LandAnalysisMixin.run_land_analysis() is called from run_deck_build_step2()
AFTER ideal_counts defaults are seeded, so ENABLE_SMART_LANDS, LAND_PROFILE,
and LAND_COUNT env overrides win over the calculated values.
AFTER ideal_counts defaults are seeded, so LAND_PROFILE and LAND_COUNT env
overrides win over the calculated values.
Responsibilities:
- compute_pip_density(): delegate to builder_utils
@ -38,9 +38,10 @@ class LandAnalysisMixin:
self._land_report_data dict persisted for M3 diagnostics export
Mutates:
self.ideal_counts['lands'] and self.ideal_counts['basic_lands']
(only when ENABLE_SMART_LANDS=1; env overrides honoured after)
(only when enable_smart_lands is True on the builder; LAND_PROFILE/LAND_COUNT env overrides honoured after)
"""
if not os.environ.get('ENABLE_SMART_LANDS'):
# Per-build toggle from UI checkbox (defaults True = opted in)
if not getattr(self, 'enable_smart_lands', True):
return
try:

View file

@ -126,6 +126,7 @@ async def build_new_modal(request: Request) -> HTMLResponse:
"use_owned_only": bool(sess.get("use_owned_only")),
"prefer_owned": bool(sess.get("prefer_owned")),
"swap_mdfc_basics": bool(sess.get("swap_mdfc_basics")),
"enable_smart_lands": bool(sess.get("enable_smart_lands", True)),
# Add ideal values from session (will be None on first load, triggering defaults)
"ramp": sess.get("ideals", {}).get("ramp"),
"lands": sess.get("ideals", {}).get("lands"),
@ -417,6 +418,7 @@ async def build_new_submit(
use_owned_only: bool = Form(False),
prefer_owned: bool = Form(False),
swap_mdfc_basics: bool = Form(False),
enable_smart_lands: bool = Form(False),
# Integrated Multi-Copy (optional)
multi_choice_id: str | None = Form(None),
multi_count: int | None = Form(None),
@ -472,6 +474,7 @@ async def build_new_submit(
"use_owned_only": bool(use_owned_only),
"prefer_owned": bool(prefer_owned),
"swap_mdfc_basics": bool(swap_mdfc_basics),
"enable_smart_lands": bool(enable_smart_lands),
"include_cards": include_cards or "",
"exclude_cards": exclude_cards or "",
"enforcement_mode": enforcement_mode or "warn",
@ -819,6 +822,10 @@ async def build_new_submit(
sess["swap_mdfc_basics"] = bool(swap_mdfc_basics)
except Exception:
sess["swap_mdfc_basics"] = False
try:
sess["enable_smart_lands"] = bool(enable_smart_lands)
except Exception:
sess["enable_smart_lands"] = True
# Combos config from modal
try:
if combo_count is not None:

View file

@ -185,6 +185,7 @@ def start_ctx_from_session(sess: dict, *, set_on_session: bool = True) -> Dict[s
include_cards=sess.get("include_cards"),
exclude_cards=sess.get("exclude_cards"),
swap_mdfc_basics=bool(sess.get("swap_mdfc_basics")),
enable_smart_lands=bool(sess.get("enable_smart_lands", True)),
partner_feature_enabled=partner_enabled,
secondary_commander=secondary_commander,
background_commander=background_choice,

View file

@ -2516,6 +2516,7 @@ def start_build_ctx(
include_cards: List[str] | None = None,
exclude_cards: List[str] | None = None,
swap_mdfc_basics: bool | None = None,
enable_smart_lands: bool | None = None,
partner_feature_enabled: bool | None = None,
secondary_commander: str | None = None,
background_commander: str | None = None,
@ -2682,6 +2683,8 @@ def start_build_ctx(
pass
# Smart land analysis — mirrors run_deck_build_step2() so web builds get profiles too
try:
if enable_smart_lands is not None:
b.enable_smart_lands = bool(enable_smart_lands)
b.run_land_analysis()
except Exception:
pass

View file

@ -104,6 +104,10 @@
<input type="checkbox" name="swap_mdfc_basics" id="swap-mdfc-chk" value="1" {% if form and form.swap_mdfc_basics %}checked{% endif %} />
<span>Swap basics for MDFC lands</span>
</label>
<label for="smart-lands-chk" class="form-checkbox-label" title="When enabled, the builder automatically adjusts the land count and mana-base profile based on your commander's speed and color complexity.">
<input type="checkbox" name="enable_smart_lands" id="smart-lands-chk" value="1" {% if form and form.enable_smart_lands %}checked{% endif %} />
<span>Smart Land Bases</span>
</label>
</div>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -43,7 +43,6 @@ services:
SIMILARITY_CACHE_ENABLED: "1" # 1=use pre-computed similarity cache; 0=real-time calculation
SIMILARITY_CACHE_PATH: "card_files/similarity_cache.parquet" # Path to Parquet cache file
ENABLE_BATCH_BUILD: "1" # 1=enable Build X and Compare feature; 0=hide build count slider
ENABLE_SMART_LANDS: "1" # 1=enable smart land base analysis (auto land count + profile from CMC/pips); 0=use fixed defaults
# LAND_PROFILE: "mid" # Optional: force a land profile (basics|mid|fixing); skips auto-detection
# LAND_COUNT: "36" # Optional: force total land count; skips curve calculation

View file

@ -45,7 +45,6 @@ services:
SIMILARITY_CACHE_ENABLED: "1" # 1=use pre-computed similarity cache; 0=real-time calculation
SIMILARITY_CACHE_PATH: "card_files/similarity_cache.parquet" # Path to Parquet cache file
ENABLE_BATCH_BUILD: "1" # 1=enable Build X and Compare feature; 0=hide build count slider
ENABLE_SMART_LANDS: "1" # 1=enable smart land base analysis (auto land count + profile from CMC/pips); 0=use fixed defaults
# LAND_PROFILE: "mid" # Optional: force a land profile (basics|mid|fixing); skips auto-detection
# LAND_COUNT: "36" # Optional: force total land count; skips curve calculation

View file

@ -14,7 +14,7 @@ By default every deck gets exactly 35 lands regardless of CMC curve or color den
From those three signals it picks a **land count** (3339), a **basics count**, and an **ETB tapped tolerance**, then passes those targets to every existing land-selection step — no other logic changes.
Enable with `ENABLE_SMART_LANDS=1` (default: on in Docker).
Enable Smart Land Bases per-build via the **Smart Land Bases** checkbox in the Preferences section of the New Deck modal (checked by default). Disable it for a single build by unchecking the box.
---
@ -81,15 +81,14 @@ The **Why:** section explains in plain English what drove the decision — singl
---
## Environment Variable Overrides
## Overrides
| Variable | Values | Effect |
|----------|--------|--------|
| `ENABLE_SMART_LANDS` | `1` (on), `0` / unset (off) | Master switch. When off, fixed defaults (35 lands, 15 basics) are used. |
| `LAND_PROFILE` | `basics`, `mid`, `fixing` | Force a specific profile, skip auto-detection. |
| `LAND_COUNT` | integer (e.g. `36`) | Force total land count, skip curve calculation. |
Env overrides are applied **after** the analysis, so they always win over the calculated values.
Env overrides are applied **after** the analysis, so they always win over the calculated values. For headless/CLI builds these are the primary way to control land behaviour.
---