belote-cli 4.9.6__tar.gz → 4.10.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.
- {belote_cli-4.9.6 → belote_cli-4.10.0}/.gitignore +4 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/CHANGELOG.md +184 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/DEVELOPMENT.md +131 -40
- {belote_cli-4.9.6 → belote_cli-4.10.0}/PKG-INFO +34 -17
- {belote_cli-4.9.6 → belote_cli-4.10.0}/README.md +32 -15
- {belote_cli-4.9.6 → belote_cli-4.10.0}/pyproject.toml +1 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/scripts/benchmark.py +8 -8
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/__init__.py +1 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/a11y.py +71 -7
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/achievements.py +11 -3
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ai.py +34 -38
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ansi.py +5 -12
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/round_ledger.py +5 -5
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/run_state.py +12 -0
- belote_cli-4.10.0/src/belote/belatro/core/scoring.py +636 -0
- belote_cli-4.10.0/src/belote/belatro/core/state.py +46 -0
- belote_cli-4.10.0/src/belote/belatro/daily.py +224 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/engine/event_bus.py +20 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/engine/modifier_patch.py +11 -10
- belote_cli-4.10.0/src/belote/belatro/engine/round_driver.py +644 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ghost_run.py +13 -9
- belote_cli-4.10.0/src/belote/belatro/integration.py +75 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/base.py +12 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/contract.py +5 -3
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/economy.py +8 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/trick_timing.py +15 -3
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/shaper.py +2 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/tarots.py +4 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/main.py +458 -325
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/personality.py +2 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/trust.py +1 -9
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/progression/save.py +85 -29
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/progression/unlocks.py +3 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/ante_themes.py +19 -3
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/boss.py +8 -5
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/shop.py +52 -4
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run_summary.py +4 -6
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/announce.py +17 -57
- belote_cli-4.10.0/src/belote/belatro/ui/collection.py +155 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/history.py +4 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/hud.py +12 -19
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/rules.py +29 -33
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/shop.py +71 -5
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/trust_bar.py +31 -7
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/config.py +7 -21
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/deck.py +17 -12
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/game.py +99 -101
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/gameflow.py +259 -258
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/input.py +104 -68
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/main.py +59 -31
- belote_cli-4.10.0/src/belote/paths.py +45 -0
- belote_cli-4.10.0/src/belote/round_engine.py +118 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/scoring.py +35 -30
- belote_cli-4.10.0/src/belote/settings.py +158 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/stats.py +106 -20
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/themes.py +42 -6
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/anim.py +6 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/announce.py +78 -18
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/menu.py +46 -93
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/prompts.py +37 -32
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/render.py +92 -101
- belote_cli-4.10.0/src/belote/ui/settings_menu.py +167 -0
- belote_cli-4.10.0/src/belote/ui/text.py +21 -0
- belote_cli-4.10.0/src/belote/variants.py +49 -0
- belote_cli-4.10.0/tests/belatro/_events.py +79 -0
- belote_cli-4.10.0/tests/belatro/test_audit_round_fixes.py +586 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_boss_contracts.py +91 -15
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_boss_modifiers_integration.py +33 -31
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_competition_malediction.py +9 -17
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_contract_unlocks.py +6 -5
- belote_cli-4.10.0/tests/belatro/test_daily.py +108 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_dead_flag_fixes.py +113 -43
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_deck_variants.py +3 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_decks_4_5.py +10 -9
- belote_cli-4.10.0/tests/belatro/test_design_calls.py +116 -0
- belote_cli-4.10.0/tests/belatro/test_economy.py +109 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_event_bus.py +64 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_handler_registry.py +2 -1
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_heist.py +10 -8
- belote_cli-4.10.0/tests/belatro/test_jokers.py +520 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_jokers_4_5.py +4 -3
- belote_cli-4.10.0/tests/belatro/test_partner_jokers.py +690 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_partner_trust.py +124 -8
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase0_coverage.py +17 -15
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase1_plumbing.py +2 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase2_content.py +5 -4
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase3_meta.py +13 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_progression.py +113 -0
- belote_cli-4.10.0/tests/belatro/test_registry_integrity.py +131 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_round_driver.py +41 -28
- belote_cli-4.10.0/tests/belatro/test_run_state.py +185 -0
- belote_cli-4.10.0/tests/belatro/test_score_accumulator.py +117 -0
- belote_cli-4.10.0/tests/belatro/test_screens.py +187 -0
- belote_cli-4.10.0/tests/belatro/test_shop.py +257 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_slot_machine_tally.py +10 -8
- belote_cli-4.10.0/tests/conftest.py +24 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_a11y.py +41 -5
- belote_cli-4.10.0/tests/test_achievements.py +151 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai.py +13 -12
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai_deluge_voids.py +3 -7
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_alt_screen_scroll.py +5 -5
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_announce_stats.py +3 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_belote.py +8 -6
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_deluge_legal_cards.py +3 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_g2_ai_signals.py +1 -1
- belote_cli-4.10.0/tests/test_gameflow_headless.py +110 -0
- belote_cli-4.10.0/tests/test_hud_fit.py +72 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_input_eof.py +47 -2
- belote_cli-4.10.0/tests/test_input_escape_seqs.py +136 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_invariants.py +8 -8
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_layout.py +3 -2
- belote_cli-4.10.0/tests/test_legal_bids.py +31 -0
- belote_cli-4.10.0/tests/test_menu_target_and_exit.py +130 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_official_rules.py +3 -2
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_properties.py +6 -4
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_diff.py +4 -10
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_golden.py +4 -2
- belote_cli-4.10.0/tests/test_repo_layout.py +46 -0
- belote_cli-4.10.0/tests/test_round_engine.py +109 -0
- belote_cli-4.10.0/tests/test_settings.py +117 -0
- belote_cli-4.10.0/tests/test_stats_persistence.py +265 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/uv.lock +1 -1
- belote_cli-4.9.6/.claude/settings.local.json +0 -19
- belote_cli-4.9.6/src/belote/belatro/core/scoring.py +0 -676
- belote_cli-4.9.6/src/belote/belatro/engine/round_driver.py +0 -558
- belote_cli-4.9.6/src/belote/belatro/ui/collection.py +0 -153
- belote_cli-4.9.6/tests/belatro/__init__.py +0 -0
- belote_cli-4.9.6/tests/belatro/test_belatro.py +0 -2023
- belote_cli-4.9.6/tests/belatro/test_partner_jokers.py +0 -357
- belote_cli-4.9.6/tests/belatro/test_registry_integrity.py +0 -61
- belote_cli-4.9.6/tests/test_achievements.py +0 -58
- {belote_cli-4.9.6 → belote_cli-4.10.0}/.python-version +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/LICENSE +0 -0
- {belote_cli-4.9.6/src → belote_cli-4.10.0/src/belote/belatro}/__init__.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro → belote_cli-4.10.0/src/belote/belatro/core}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/economy.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/core → belote_cli-4.10.0/src/belote/belatro/engine}/__init__.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/engine → belote_cli-4.10.0/src/belote/belatro/items}/__init__.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/items → belote_cli-4.10.0/src/belote/belatro/items/jokers}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/annonces.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/coinche.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/corrupted.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/hand_comp.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/items/jokers → belote_cli-4.10.0/src/belote/belatro/items/partner_jokers}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/passive.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/risky.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/planets.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/registry.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/vouchers.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/items/partner_jokers → belote_cli-4.10.0/src/belote/belatro/partner}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/partner_state.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/partner → belote_cli-4.10.0/src/belote/belatro/progression}/__init__.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/progression → belote_cli-4.10.0/src/belote/belatro/run}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/ante.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/decks.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/run → belote_cli-4.10.0/src/belote/belatro/ui}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/consumables.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/inventory.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/menu.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/context.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/replay.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/rules.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/fit_guard.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/layout.py +0 -0
- {belote_cli-4.9.6/src/belote/belatro/ui → belote_cli-4.10.0/tests}/__init__.py +0 -0
- {belote_cli-4.9.6/tests → belote_cli-4.10.0/tests/belatro}/__init__.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_collection_logic.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_consumables_ui.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_endless.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_ghost_run.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_history_overlay.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_hud_synergy.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_hud_toggle.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_inventory_overlay.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_joker_callouts.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_joker_contracts.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_run_summary.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_shop_animations.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_shop_empty_pools.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_voucher_idempotency.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/perf_baselines.json +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai_sans_atout.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_anim_helpers.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ansi_helpers.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_belote_stinger.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_benchmark_smoke.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_bidding_all_pass.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_declaration_tiebreak.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_extended.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_g1_coinche_classic.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_game_logic.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_gameflow.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_hand_auto_sort.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_history_overlay_cache.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_input_wasd.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_new_coverage.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_no_color.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_perf_regression.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_felt_polish.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_hand_lift.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_replay.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_scoring_totals.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_undo.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_wcwidth.py +0 -0
- {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_winner_glow.py +0 -0
|
@@ -5,6 +5,190 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.10.0] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
Verification pass over two external audit reports (every claim checked
|
|
11
|
+
against the code), then the full follow-up: BelAtro becomes a true expansion
|
|
12
|
+
of the core game, one shared round engine, the god functions split, and three
|
|
13
|
+
features (Settings, achievement toasts, Daily Challenge). Refactors were
|
|
14
|
+
verified with seeded whole-round fingerprints (BelAtro: 800 rounds across all
|
|
15
|
+
bosses/jokers/flags; classic: 450 rounds incl. heavy undo) — byte-identical
|
|
16
|
+
except the intended undo fix. Tests 1228 → 1286, coverage 79% → 83%.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Settings screen** (main menu → Settings), persisted to `settings.json` in
|
|
20
|
+
the config dir: AI difficulty (per seat), target, speed, theme, animations,
|
|
21
|
+
fast redraw, screen-reader hints, replay analysis, and expansion toggles
|
|
22
|
+
(BelAtro ghost recording). `BELOTE_*` env vars still override while set; the
|
|
23
|
+
CLI flags now override the saved values for one session.
|
|
24
|
+
- **Achievement toasts:** unlocking an achievement pops a boxed toast (and a
|
|
25
|
+
screen-reader line) at the end of the round / game.
|
|
26
|
+
- **Daily Challenge:** one date-seeded BelAtro run per day, identical for
|
|
27
|
+
everyone (fixed deck, starter shop pool, no endless), with a share code and
|
|
28
|
+
a local best-result record (`daily.json`).
|
|
29
|
+
- **Screen-reader hints now announce declarations** (`a11y.announce_declaration`
|
|
30
|
+
existed but was never called).
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **Classic: undo during play re-ran the coinche flow** — the player was
|
|
34
|
+
re-prompted to coinche and the AI defenders were silently re-polled
|
|
35
|
+
(shifting their RNG) every time an undo resumed the play phase.
|
|
36
|
+
- **BelAtro collection gallery** pinned the cursor to the top row (the list
|
|
37
|
+
was sliced from the selected item), hiding every item above it.
|
|
38
|
+
- **BelAtro rules screen** called `hide_cursor()` without writing it (no-op).
|
|
39
|
+
- **Le Divorce / La Trahison** could leave partner trust frozen at 0 if the
|
|
40
|
+
round aborted; the restore now runs in a `finally`.
|
|
41
|
+
- **BelAtro: Sans Atout bid from the normal prompt became a pass.**
|
|
42
|
+
`UICallbacks.prompt_bid` filtered UI sentinels with `isinstance(res, str)`,
|
|
43
|
+
which also matched `SANS_ATOUT_BID` (the plain string `"sans_atout"`).
|
|
44
|
+
- **L'Aristocrate ace payout ignored La Rupture / L'Anarchie.** Aces are now
|
|
45
|
+
credited via `compute_trick_winners` (new `_aces_won_by_team` helper) instead
|
|
46
|
+
of raw `trick_winner_seat` with the final trump.
|
|
47
|
+
- **mypy was blind to every absolute `belote.*` import.** A stray empty
|
|
48
|
+
`src/__init__.py` made mypy name modules `src.belote.*`; with
|
|
49
|
+
`ignore_missing_imports` all `from belote.… import` resolved to `Any`.
|
|
50
|
+
Removed it (guarded by `tests/test_repo_layout.py`) and fixed the 9 errors
|
|
51
|
+
it was hiding (incl. a possible `None.lower()` in the BelAtro rules screen).
|
|
52
|
+
- **Theme file ignored `XDG_CONFIG_HOME` / `%APPDATA%`** (hardcoded
|
|
53
|
+
`~/.config`, which also let the test suite write the real user config).
|
|
54
|
+
- **Main menu flicker:** the 300ms steam animation no longer clears the whole
|
|
55
|
+
screen each tick; it repaints in place.
|
|
56
|
+
- **Achievements are now visible** on the Statistics screen (they were
|
|
57
|
+
evaluated and saved but never shown).
|
|
58
|
+
|
|
59
|
+
### Changed — architecture
|
|
60
|
+
- **The core never imports BelAtro** (enforced by `tests/test_repo_layout.py`).
|
|
61
|
+
Boss/deck/voucher rule switches moved from BelAtro's `BossModifiers` +
|
|
62
|
+
`_joker_state` keys to a core `RuleVariants` (`belote/variants.py`,
|
|
63
|
+
`GameState.variants`). BelAtro's chips/mult/money/joker state moved off the
|
|
64
|
+
core `GameState` onto `belatro/core/state.py::BelatroState`, a subclass that
|
|
65
|
+
core transitions (now generic over `GameState` subclasses) carry along.
|
|
66
|
+
The stats screen, bid-hint line, I/V keys and main-menu modes are now
|
|
67
|
+
registration hooks wired from the entry point via `belatro/integration.py`.
|
|
68
|
+
- **One round engine** (`belote/round_engine.py`): classic `gameflow` and
|
|
69
|
+
BelAtro `round_driver` are two `RoundPolicy` implementations of
|
|
70
|
+
`play_round`. Coinche level now lives on `state.coinche_level` in both modes.
|
|
71
|
+
- **God functions split:** `process_event` (369 lines) → per-event handler
|
|
72
|
+
methods; `drive_round` (450) → a round context + phase functions;
|
|
73
|
+
`_play_blind` (344) → setup / boss / settlement helpers.
|
|
74
|
+
- **Tests:** the 2037-line `test_belatro.py` split by topic (economy, score
|
|
75
|
+
accumulator, run state, jokers, shop, …); 2 duplicate tests removed; new
|
|
76
|
+
coverage for the round engine, headless classic rounds, the collection /
|
|
77
|
+
rules / shop screens, settings and the daily challenge.
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
- AI / target / speed / theme moved from the main menu into Settings.
|
|
81
|
+
- Shared key-alias table for the Unix and Windows readers; one
|
|
82
|
+
`ThemeManager.cycle()`; table-driven card art (byte-identical output);
|
|
83
|
+
`wrap_words` text helper; `BELOTE_NO_DIFF` read once instead of per frame.
|
|
84
|
+
- One `legal_bids(state)` in `game.py` replaces four hand-rolled copies
|
|
85
|
+
(bid prompt, bid renderer, BelAtro joker hooks, partner legality gate).
|
|
86
|
+
- One `deck.is_trump_card()` replaces the inline "trump suit or Déluge 7/8"
|
|
87
|
+
predicate in `deck.py`, `game.py`, `ai.py` and `round_driver.py`
|
|
88
|
+
(`_card_beats` keeps its Tout-Atout-exclusive variant, now commented).
|
|
89
|
+
- New `belote/paths.py` owns data/config locations (was 5 copies; the BelAtro
|
|
90
|
+
ones fell back to the CWD on Windows without `%APPDATA%`).
|
|
91
|
+
- Dix de Der uses `GLOBAL_CONFIG.LAST_TRICK_BONUS` everywhere (3 hardcoded 10s).
|
|
92
|
+
- BelAtro history entries are built from typed `GameState`/`ScoringBreakdown`
|
|
93
|
+
instead of `object` + `getattr`.
|
|
94
|
+
|
|
95
|
+
### Removed
|
|
96
|
+
- `BelAtroAnnounce.score_popup` (slated for removal in 4.8) and its dead
|
|
97
|
+
overlay flag helpers.
|
|
98
|
+
- `ScoreAccumulator.partner_jokers_double` / `TrustTrack.partner_jokers_double`
|
|
99
|
+
(deprecated since 3.5.0; `partner_tier` replaces them).
|
|
100
|
+
- Dead code: `Seat.prev_seat`, `GameState.current_bidder`, `set_announced`,
|
|
101
|
+
`Config.ensure_stats_dir`, legacy `Config.CARD_*` / `MIN_TERM_*` /
|
|
102
|
+
`THEME_NAME`, `render.CARD_W/CARD_H/CARD_GAP/SIDE_COL_W`,
|
|
103
|
+
`ansi.save_cursor/restore_cursor/scroll_region`, unused HUD tables.
|
|
104
|
+
|
|
105
|
+
## [4.9.7] - 2026-11-01
|
|
106
|
+
|
|
107
|
+
Repo-wide audit: four parallel reviewers swept the classic engine, the
|
|
108
|
+
classic UI/terminal layer, the BelAtro engine/run/progression code and the
|
|
109
|
+
BelAtro items/UI; every finding was verified (most with a repro script) before
|
|
110
|
+
fixing. Test count 1111 → 1228.
|
|
111
|
+
|
|
112
|
+
### Fixed — test suite
|
|
113
|
+
|
|
114
|
+
- **Tests no longer depend on (or write to) your real profile.** A new
|
|
115
|
+
`tests/conftest.py` points `HOME` / `XDG_CONFIG_HOME` / `XDG_DATA_HOME` at a
|
|
116
|
+
temp dir. The suite used to render with whatever theme the developer last
|
|
117
|
+
picked (the golden-frame hashes only passed with `moonlit_tavern` saved) and
|
|
118
|
+
`theme_manager.set_current()` rewrote `~/.config/belote/theme.json`.
|
|
119
|
+
Golden hashes re-pinned to the default `classic_green` theme.
|
|
120
|
+
- Hardcoded `/home/mrrobot/belote/` paths in `tests/test_alt_screen_scroll.py`
|
|
121
|
+
replaced with a path relative to the test file.
|
|
122
|
+
- `scripts/benchmark.py` is now `mypy --strict` clean.
|
|
123
|
+
|
|
124
|
+
### Fixed — classic game
|
|
125
|
+
|
|
126
|
+
- **L'Anarchie scored every trick with the end-of-round trump.** Tricks now
|
|
127
|
+
record the trump they were played under (`GameState.trick_trumps`) and final
|
|
128
|
+
scoring uses it (was wrong in ~99% of L'Anarchie rounds).
|
|
129
|
+
- A **worthless carré of 7s/8s outranked the other team's sequences** and
|
|
130
|
+
wiped out their points. 0-point carrés no longer take part in declarations.
|
|
131
|
+
- **Hard AI threw high off-suit cards to the opponents**, believing an
|
|
132
|
+
off-suit Ace beat a led King (normal and Tout Atout contracts).
|
|
133
|
+
- **Tout Atout under L'Anarchie** silently became a one-suit game while still
|
|
134
|
+
being scored as Tout Atout; trump no longer rotates under TA.
|
|
135
|
+
- Capots by the opponents counted as **your** capots in stats/achievements;
|
|
136
|
+
`first_capot` used `== 1`; "Triple Coup" read lifetime instead of session capots.
|
|
137
|
+
- 6+ card sequences were labelled "0" in the round summary.
|
|
138
|
+
|
|
139
|
+
### Fixed — terminal, UI and files
|
|
140
|
+
|
|
141
|
+
- `--target 750` (non-preset) crashed the main menu.
|
|
142
|
+
- Stats file: atomic writes; corrupt / wrong-shaped files no longer crash the
|
|
143
|
+
game (moved aside to `stats.json.bak`).
|
|
144
|
+
- Unknown escape sequences leaked as keystrokes (F2 quit the game, Ctrl+→
|
|
145
|
+
played card 5, Delete typed `~`); Ctrl-C now quits.
|
|
146
|
+
- In-game `FitAbortedError` (terminal too small → Q) exits cleanly.
|
|
147
|
+
- Spacious HUD overflowed at 120–143 columns and scrolled the screen.
|
|
148
|
+
- Game-over screen "staircased" (bare `\n` in raw mode).
|
|
149
|
+
- Pressing T crashed on a read-only config dir; theme saves are atomic.
|
|
150
|
+
- The game can now start on Windows (`termios` imported only on Unix); classic
|
|
151
|
+
stats honour `%APPDATA%`.
|
|
152
|
+
- `BELOTE_A11Y=1` no longer scribbles over the game screen — see
|
|
153
|
+
DEVELOPMENT.md → Accessibility (`BELOTE_A11Y_OUT`).
|
|
154
|
+
|
|
155
|
+
### Fixed — BelAtro
|
|
156
|
+
|
|
157
|
+
- **All-pass deals ended the run**; they now redeal (and round-end jokers no
|
|
158
|
+
longer fire on the abandoned deal).
|
|
159
|
+
- Partner bidding was backwards in round 1 (could take any suit except the
|
|
160
|
+
legal one; L'Économe could never bid).
|
|
161
|
+
- L'Architecte "buy contract" crashed in round 1 after taking $10.
|
|
162
|
+
- Le Traître / L'Agent Double replaced the boss's larger sabotage set.
|
|
163
|
+
- L'Avocat's doubled target was ignored for payout, Le Puriste, trust and history.
|
|
164
|
+
- Ghost runs: real seeds, seeded rounds, unique file names.
|
|
165
|
+
- Corrupt / newer-schema `profile.json` no longer crashes launch.
|
|
166
|
+
- Capots by EW counted as yours (unlocks, Pluto); Mercury/Pluto paid on EW contracts.
|
|
167
|
+
- The shop offered jokers you already own (duplicates corrupted each other's
|
|
168
|
+
state); Le Jugement too.
|
|
169
|
+
- Le Patriote, L'Illusionniste and La Sentinelle (P) mishandled Tout Atout;
|
|
170
|
+
Le Passeur paid $2 when North bid Sans Atout.
|
|
171
|
+
- Shop cards: frame misaligned, long names overflowed, editions invisible.
|
|
172
|
+
- Compact HUD: trust bar overwrote the synergy tooltip.
|
|
173
|
+
- History overlay overflowed 90–99 column terminals.
|
|
174
|
+
- An ante-8 victory continued into endless mode was recorded as a loss.
|
|
175
|
+
|
|
176
|
+
### Changed — design calls
|
|
177
|
+
|
|
178
|
+
- **Le Diable** now works: for one round North over-cuts with its lowest
|
|
179
|
+
winning trump whenever it can — even over your winning card.
|
|
180
|
+
- **Sell jokers** in the shop with `S` for half their cost (same value as
|
|
181
|
+
La Tempérance). Corrupted jokers can't be sold; selling a Negative joker
|
|
182
|
+
returns its slot.
|
|
183
|
+
- **Le Sergent** grows with the streak: +0.5, +1.0, +1.5, then +2.0 per trick
|
|
184
|
+
(was a flat +0.5 that ignored "consecutive").
|
|
185
|
+
- **Le Tournoi** really offers you a coinche; theme flags no longer outlive
|
|
186
|
+
their ante. **Le Café**'s +25 chips is a petit-blind bonus, not permanent.
|
|
187
|
+
- **Auto-Capot** (trust 9+) is no longer advertised — it was never implemented.
|
|
188
|
+
- **Trust 3+ shows North's void suits** (`✦ Void: ♠♦`) instead of the whole
|
|
189
|
+
hand; seeing the full hand needs Le Carnet.
|
|
190
|
+
- **Pluto** pays only when you took the contract and made it as a capot.
|
|
191
|
+
|
|
8
192
|
## [4.9.6] - 2026-06-01
|
|
9
193
|
|
|
10
194
|
Follow-up bug-hunt, logic, and performance audit. Three parallel Explore agents
|
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
Welcome to the Belote development guide. This project is structured as a standard Python package.
|
|
4
4
|
|
|
5
|
+
## Cheat Sheet
|
|
6
|
+
|
|
7
|
+
Everything below assumes you're in the repo with the venv active:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd ~/Projects/belote
|
|
11
|
+
source .venv/bin/activate # gives you `python`, `pip`, pytest, mypy, ruff
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**1. Check everything is green (do this before every commit):**
|
|
15
|
+
```bash
|
|
16
|
+
PYTHONPATH=src python -m pytest -q && \
|
|
17
|
+
PYTHONPATH=src python -m mypy --strict src/ && \
|
|
18
|
+
python -m ruff check src/ tests/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**2. Push code to GitHub (no PyPI release):**
|
|
22
|
+
```bash
|
|
23
|
+
git status # see what changed — don't commit editor swap files
|
|
24
|
+
git add <files> # or: git add -A (after checking git status)
|
|
25
|
+
git commit -m "what changed"
|
|
26
|
+
git push origin master
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**3. Release a new version to PyPI** (example: 4.9.7):
|
|
30
|
+
```bash
|
|
31
|
+
# a) bump the version in BOTH files (they must match):
|
|
32
|
+
# pyproject.toml -> version = "4.9.7"
|
|
33
|
+
# src/belote/__init__.py -> __version__ = "4.9.7"
|
|
34
|
+
# b) add a "## 4.9.7" entry at the top of CHANGELOG.md
|
|
35
|
+
# c) run the gates (step 1), then commit, tag and push:
|
|
36
|
+
git add -A
|
|
37
|
+
git commit -m "Release v4.9.7"
|
|
38
|
+
git tag -a v4.9.7 -m "v4.9.7"
|
|
39
|
+
git push origin master --tags
|
|
40
|
+
# d) build, check and upload:
|
|
41
|
+
rm -rf dist/ build/ *.egg-info/
|
|
42
|
+
pipx run build --sdist --wheel
|
|
43
|
+
pipx run twine check dist/*
|
|
44
|
+
pipx run twine upload dist/*
|
|
45
|
+
# username: __token__
|
|
46
|
+
# password: your PyPI API token (starts with pypi-)
|
|
47
|
+
# make one at https://pypi.org/manage/account/token/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**4. Update the game installed with pipx** (from PyPI — https://pypi.org/project/belote-cli/):
|
|
51
|
+
```bash
|
|
52
|
+
pipx install belote-cli # first time only
|
|
53
|
+
pipx upgrade belote-cli # after each release
|
|
54
|
+
belote --version # confirm the new version
|
|
55
|
+
```
|
|
56
|
+
PyPI can take a minute to show a new upload. If `pipx upgrade` says you're
|
|
57
|
+
already on the latest, wait a moment and retry, or force it with
|
|
58
|
+
`pipx install --force belote-cli`.
|
|
59
|
+
|
|
5
60
|
## Setup
|
|
6
61
|
|
|
7
62
|
1. **Clone the repository:**
|
|
@@ -10,15 +65,17 @@ Welcome to the Belote development guide. This project is structured as a standar
|
|
|
10
65
|
cd belote
|
|
11
66
|
```
|
|
12
67
|
|
|
13
|
-
2. **Create a virtual environment
|
|
68
|
+
2. **Create a virtual environment** (use `python3` — many Linux distros don't
|
|
69
|
+
ship a bare `python` or `pip` outside a venv):
|
|
14
70
|
```bash
|
|
15
|
-
|
|
71
|
+
python3 -m venv .venv
|
|
16
72
|
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
17
73
|
```
|
|
18
74
|
|
|
19
|
-
3. **Install in editable mode
|
|
75
|
+
3. **Install in editable mode with the test + dev tools** (pytest, pytest-cov,
|
|
76
|
+
mypy, ruff):
|
|
20
77
|
```bash
|
|
21
|
-
pip install -e .
|
|
78
|
+
pip install -e ".[test,dev]"
|
|
22
79
|
```
|
|
23
80
|
|
|
24
81
|
## Running the Game
|
|
@@ -42,29 +99,31 @@ PYTHONPATH=src python3 -m belote.main
|
|
|
42
99
|
|
|
43
100
|
## Testing
|
|
44
101
|
|
|
45
|
-
We use `pytest`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
102
|
+
We use `pytest` (installed by `pip install -e ".[test,dev]"` above).
|
|
103
|
+
|
|
104
|
+
`tests/conftest.py` points `HOME`, `XDG_CONFIG_HOME` and `XDG_DATA_HOME` at a
|
|
105
|
+
throwaway temp dir for the whole run, so the suite never reads or writes your
|
|
106
|
+
real `~/.config/belote/theme.json`, stats or BelAtro saves, and always renders
|
|
107
|
+
with the default theme (the golden-frame snapshot depends on that).
|
|
49
108
|
|
|
50
109
|
Run tests:
|
|
51
110
|
```bash
|
|
52
111
|
# Run all tests (Classic + BelAtro)
|
|
53
112
|
PYTHONPATH=src pytest
|
|
54
113
|
|
|
55
|
-
# Run only Classic Belote tests
|
|
56
|
-
PYTHONPATH=src pytest tests/
|
|
114
|
+
# Run only Classic Belote tests (tests/belatro/ lives inside tests/)
|
|
115
|
+
PYTHONPATH=src pytest tests/ --ignore=tests/belatro
|
|
57
116
|
|
|
58
117
|
# Run only BelAtro tests
|
|
59
118
|
PYTHONPATH=src pytest tests/belatro/
|
|
60
119
|
|
|
61
120
|
# Run a single test file
|
|
62
|
-
PYTHONPATH=src pytest tests/
|
|
63
|
-
PYTHONPATH=src pytest tests/belatro/
|
|
121
|
+
PYTHONPATH=src pytest tests/test_game_logic.py
|
|
122
|
+
PYTHONPATH=src pytest tests/belatro/test_round_driver.py
|
|
64
123
|
|
|
65
124
|
# Run a single test by name
|
|
66
|
-
PYTHONPATH=src pytest tests/
|
|
67
|
-
PYTHONPATH=src pytest -k "
|
|
125
|
+
PYTHONPATH=src pytest tests/test_render_golden.py::test_golden_frame_sha256
|
|
126
|
+
PYTHONPATH=src pytest -k "capot"
|
|
68
127
|
|
|
69
128
|
# Run with verbose output
|
|
70
129
|
PYTHONPATH=src pytest -v
|
|
@@ -84,7 +143,7 @@ PYTHONPATH=src mypy --strict src/
|
|
|
84
143
|
# Linting (0 violations expected)
|
|
85
144
|
ruff check src/ tests/
|
|
86
145
|
|
|
87
|
-
# Full test suite (
|
|
146
|
+
# Full test suite (1228 tests expected)
|
|
88
147
|
PYTHONPATH=src pytest
|
|
89
148
|
```
|
|
90
149
|
|
|
@@ -92,7 +151,7 @@ Run all gates before committing:
|
|
|
92
151
|
|
|
93
152
|
```bash
|
|
94
153
|
PYTHONPATH=src python -m pytest --tb=short -q && \
|
|
95
|
-
python -m mypy --strict src/ && \
|
|
154
|
+
PYTHONPATH=src python -m mypy --strict src/ && \
|
|
96
155
|
python -m ruff check src/ tests/
|
|
97
156
|
```
|
|
98
157
|
|
|
@@ -112,12 +171,42 @@ PYTHONPATH=src python scripts/benchmark.py
|
|
|
112
171
|
|
|
113
172
|
Use these as a regression-detection floor for future changes.
|
|
114
173
|
|
|
174
|
+
## Architecture: core game vs. BelAtro expansion
|
|
175
|
+
|
|
176
|
+
- **The core never imports BelAtro.** `tests/test_repo_layout.py` enforces
|
|
177
|
+
it; only the composition root `belote/main.py` wires the expansion in via
|
|
178
|
+
`belote/belatro/integration.py` (menu modes, stats section, settings
|
|
179
|
+
toggle) and BelAtro installs its run-scoped hooks itself (history
|
|
180
|
+
override, bid hint, extension keys I/V).
|
|
181
|
+
- **One round engine.** `belote/round_engine.py::play_round` owns the phase
|
|
182
|
+
order (bidding with all-pass handling → contract → tricks). Classic
|
|
183
|
+
(`gameflow.ClassicRound`) and BelAtro (`belatro/engine/round_driver.py::
|
|
184
|
+
_RoundContext`) are `RoundPolicy` implementations — change the round's
|
|
185
|
+
flow there, once.
|
|
186
|
+
- **Rule variants are core.** Boss / deck / voucher effects that change how
|
|
187
|
+
Belote is played live on `GameState.variants` (`belote/variants.py`);
|
|
188
|
+
bosses only switch variants on.
|
|
189
|
+
- **Expansion state rides on a subclass.** BelAtro's chips / mult / money /
|
|
190
|
+
joker state live on `belatro/core/state.py::BelatroState`. Core
|
|
191
|
+
transitions are generic over `GameState` subclasses and build states with
|
|
192
|
+
`replace()`, so the subclass survives every move.
|
|
193
|
+
|
|
115
194
|
## Accessibility
|
|
116
195
|
|
|
117
196
|
Set `BELOTE_A11Y=1` to emit one-line plain-text descriptions of card plays,
|
|
118
197
|
trick winners, locked contracts (4.6.5), declarations (4.6.5), and round
|
|
119
|
-
results
|
|
120
|
-
|
|
198
|
+
results — readable by terminal screen readers (Orca, NVDA over WSL,
|
|
199
|
+
VoiceOver via iTerm2).
|
|
200
|
+
|
|
201
|
+
Where the lines go (4.9.7 — writing them onto the full-screen game used to
|
|
202
|
+
scroll and scramble the frame):
|
|
203
|
+
|
|
204
|
+
- `BELOTE_A11Y_OUT=/path` — appended to that file or terminal device, e.g.
|
|
205
|
+
run `tty` in a second terminal and use its `/dev/pts/N`.
|
|
206
|
+
- otherwise stderr, when it's redirected (`belote 2>a11y.txt`) or is a
|
|
207
|
+
different terminal from the game.
|
|
208
|
+
- otherwise `~/.local/share/belote/a11y.log` (`%APPDATA%\belote\a11y.log` on
|
|
209
|
+
Windows) — follow it with `tail -f` in another terminal.
|
|
121
210
|
|
|
122
211
|
The env var is read once at import; tests that mutate it must call
|
|
123
212
|
`belote.a11y._refresh_enabled_from_env()` after the patch. Hook points live
|
|
@@ -127,8 +216,10 @@ run won/lost).
|
|
|
127
216
|
|
|
128
217
|
## Optional Runtime Flags
|
|
129
218
|
|
|
130
|
-
|
|
131
|
-
|
|
219
|
+
Players toggle these from the in-game **Settings** screen (`belote/settings.py`
|
|
220
|
+
→ `settings.json`; `settings.apply()` pushes the values into the modules).
|
|
221
|
+
The environment variables below still work and *override* the saved setting
|
|
222
|
+
while set — handy for debugging and CI. Each is read once at startup.
|
|
132
223
|
|
|
133
224
|
- `BELOTE_REPLAY=1` — after every Classic round, print a one-line summary
|
|
134
225
|
of how often South's plays matched the Hard-AI's preferred line
|
|
@@ -136,8 +227,8 @@ once at startup; toggling mid-run has no effect.
|
|
|
136
227
|
affects scoring. Backed by `src/belote/replay.py`.
|
|
137
228
|
- `BELOTE_GHOST=1` — silently record every BelAtro run (seed, deck,
|
|
138
229
|
bids, plays, round outcomes) to
|
|
139
|
-
`~/.local/share/belote/ghosts/<label>-<seed>.json`. The file
|
|
140
|
-
once when the run ends. Useful for sharing or replaying interesting
|
|
230
|
+
`~/.local/share/belote/ghosts/<label>-<seed>-<timestamp>.json`. The file
|
|
231
|
+
is written once when the run ends and never overwrites an earlier one. Useful for sharing or replaying interesting
|
|
141
232
|
runs. Backed by `src/belote/belatro/ghost_run.py`.
|
|
142
233
|
- `NO_COLOR=<any-non-empty>` — suppress truecolor SGR escapes from
|
|
143
234
|
`fg()` / `bg()` per the [no-color.org](https://no-color.org/) spec.
|
|
@@ -164,9 +255,9 @@ once at startup; toggling mid-run has no effect.
|
|
|
164
255
|
|
|
165
256
|
## Releasing a New Version
|
|
166
257
|
|
|
167
|
-
|
|
258
|
+
The short version is in the [Cheat Sheet](#cheat-sheet) at the top. Details:
|
|
168
259
|
|
|
169
|
-
|
|
260
|
+
### Code-only update (push to GitHub without releasing a new PyPI version)
|
|
170
261
|
|
|
171
262
|
```bash
|
|
172
263
|
git add <files>
|
|
@@ -174,32 +265,32 @@ git commit -m "<what changed>"
|
|
|
174
265
|
git push origin master
|
|
175
266
|
```
|
|
176
267
|
|
|
177
|
-
|
|
268
|
+
### Full release (GitHub + PyPI)
|
|
178
269
|
|
|
179
|
-
1. **
|
|
270
|
+
1. **Run all gates** (see Code Quality) — don't release a red build.
|
|
271
|
+
2. **Bump the version** in BOTH `pyproject.toml` AND `src/belote/__init__.py`
|
|
180
272
|
(they must stay in sync — `belote --version` / `belatro --version` read
|
|
181
273
|
`__version__` while PyPI / pipx read `pyproject.toml`).
|
|
182
|
-
|
|
183
|
-
|
|
274
|
+
3. **Add a CHANGELOG entry** at the top of `CHANGELOG.md`.
|
|
275
|
+
4. **Commit, tag and push:**
|
|
184
276
|
```bash
|
|
185
|
-
|
|
277
|
+
git add -A # check `git status` first
|
|
278
|
+
git commit -m "Release vX.Y.Z"
|
|
279
|
+
git tag -a vX.Y.Z -m "vX.Y.Z"
|
|
280
|
+
git push origin master --tags
|
|
186
281
|
```
|
|
187
|
-
|
|
282
|
+
5. **Clean stale build artifacts, then build, validate and upload:**
|
|
188
283
|
```bash
|
|
284
|
+
rm -rf dist/ build/ *.egg-info/
|
|
189
285
|
pipx run build --sdist --wheel
|
|
190
286
|
pipx run twine check dist/*
|
|
191
287
|
pipx run twine upload dist/*
|
|
192
288
|
```
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
git add pyproject.toml CHANGELOG.md
|
|
199
|
-
git commit -m "Release vX.Y.Z"
|
|
200
|
-
git tag -a vX.Y.Z -m "vX.Y.Z"
|
|
201
|
-
git push origin master --tags
|
|
202
|
-
```
|
|
289
|
+
`twine upload` uses `~/.pypirc` if present; otherwise it prompts — enter
|
|
290
|
+
`__token__` as the username and a PyPI API token as the password.
|
|
291
|
+
PyPI never accepts the same version twice, so a botched upload means
|
|
292
|
+
bumping to the next patch version.
|
|
293
|
+
6. **Update your own install:** `pipx upgrade belote-cli`.
|
|
203
294
|
|
|
204
295
|
## Performance measurement
|
|
205
296
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: belote-cli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.10.0
|
|
4
4
|
Summary: A 4-player terminal card game
|
|
5
5
|
Project-URL: Homepage, https://github.com/ElysiumDisc/belote
|
|
6
6
|
Project-URL: Repository, https://github.com/ElysiumDisc/belote
|
|
@@ -113,14 +113,14 @@ belatro
|
|
|
113
113
|
.-'``'|-._ ) _.-|
|
|
114
114
|
/ .--.| `''---...........---''` |
|
|
115
115
|
/ / | BelAtro |
|
|
116
|
-
| | |
|
|
117
|
-
| | |
|
|
118
|
-
| | |
|
|
119
|
-
| | | Speed: < Normal > |
|
|
120
|
-
| | | Theme: < Classic Green > |
|
|
116
|
+
| | | Daily Challenge |
|
|
117
|
+
| | | > Start Game < |
|
|
118
|
+
| | | Settings |
|
|
121
119
|
| | | Rules & History |
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
| | | Statistics |
|
|
121
|
+
| | | Quit |
|
|
122
|
+
\ \ | |
|
|
123
|
+
`\ `\ | |
|
|
124
124
|
`\ `| |
|
|
125
125
|
_/ /\ /
|
|
126
126
|
(__/ \ /
|
|
@@ -157,8 +157,8 @@ belatro
|
|
|
157
157
|
# Install in editable mode (recommended for development)
|
|
158
158
|
pip install -e .
|
|
159
159
|
|
|
160
|
-
# Or install from PyPI (
|
|
161
|
-
|
|
160
|
+
# Or install from PyPI (recommended for playing)
|
|
161
|
+
pipx install belote-cli # update later with: pipx upgrade belote-cli
|
|
162
162
|
|
|
163
163
|
# Play using the belote command
|
|
164
164
|
belote
|
|
@@ -166,10 +166,26 @@ belote
|
|
|
166
166
|
# Play the new Roguelite expansion
|
|
167
167
|
belatro
|
|
168
168
|
|
|
169
|
-
#
|
|
169
|
+
# One-off overrides of the saved settings (not persisted)
|
|
170
170
|
belote --difficulty hard --target 500 --seed 123 --speed fast
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
+
**Settings** live in the main menu's *Settings* screen and are saved to
|
|
174
|
+
`settings.json` in your config directory (`$XDG_CONFIG_HOME/belote`, else
|
|
175
|
+
`~/.config/belote`; `%APPDATA%\belote` on Windows): AI difficulty (per seat
|
|
176
|
+
via Enter), target score, speed, theme, animations, fast redraw,
|
|
177
|
+
screen-reader hints, post-round replay analysis and BelAtro ghost-run
|
|
178
|
+
recording. The old `BELOTE_*` environment variables still work and override
|
|
179
|
+
the saved value while set.
|
|
180
|
+
|
|
181
|
+
**Daily Challenge** (main menu): one seeded BelAtro run per day, the same for
|
|
182
|
+
everyone (fixed deck, date-derived seed, starter shop pool, no endless).
|
|
183
|
+
The intro shows the day's number and a share code; your best result per day
|
|
184
|
+
is kept locally.
|
|
185
|
+
|
|
186
|
+
**Achievements** pop a toast the moment you unlock one and are listed on the
|
|
187
|
+
Statistics screen.
|
|
188
|
+
|
|
173
189
|
|
|
174
190
|
## Controls
|
|
175
191
|
|
|
@@ -189,6 +205,7 @@ belote --difficulty hard --target 500 --seed 123 --speed fast
|
|
|
189
205
|
**BelAtro (Roguelite):**
|
|
190
206
|
- `1`-`5`: Inspect specific Jokers in the Shop
|
|
191
207
|
- `C`: Open the Consumables tray in the Shop (use Tarots / Planets you've purchased)
|
|
208
|
+
- `S` *(4.9.7)*: Sell a joker in the Shop for half its cost (corrupted jokers can't be sold)
|
|
192
209
|
|
|
193
210
|
**Gameplay:**
|
|
194
211
|
- `←` `→` or `↑` `↓` or **`W` `A` `S` `D`** *(4.5.0)*: Move selection
|
|
@@ -211,14 +228,14 @@ belote --difficulty hard --target 500 --seed 123 --speed fast
|
|
|
211
228
|
- **Collection (Almanac):** Persistent tracker to browse every Joker, Planet, and Voucher you've discovered across your runs.
|
|
212
229
|
- **Full Boss Blind Suite:** All 21 unique bosses implemented, including complex mechanics like *L'Anarchie* (dynamic trump) and *La Rupture* (no consecutive wins).
|
|
213
230
|
- **Multiplier Scoring:** Use items to stack Multipliers and reach scores in the millions.
|
|
214
|
-
- **Partner Trust:** Build a relationship with your AI partner to unlock synergies.
|
|
231
|
+
- **Partner Trust:** Build a relationship with your AI partner to unlock synergies. At trust 3+ the trust bar shows which suits North is void in (`✦ Void: ♠♦`); seeing North's full hand requires the Le Carnet voucher.
|
|
215
232
|
- **Rich Terminal UI:** Full-screen themed felt table with detailed card graphics and "You" vs "Partner" terminology.
|
|
216
233
|
- **Enhanced Hard AI**: Advanced void inference and 2-ply lookahead for critical tricks (Dix de Der).
|
|
217
234
|
- **Customizable Themes:** Switch between thirteen color palettes (Classic Green, Dark Mode, Blue Velvet, Red Casino, Sepia Vintage, High Contrast, Colorblind, Forest Night, Moonlit Tavern, Royal Purple, Emerald Isle, Sunset Magma, Provence Lavande) using the `T` key during gameplay.
|
|
218
235
|
- **Polished Felt Mat (3.9.4):** The trick mat now has a subtle vignette at its edges, a faint deterministic braille pip-dot texture (fabric-weave feel without intrusive glyphs), and — at standard/spacious terminal sizes — a decorative `╔═══◆═══...═══◆═══╗` outer frame with corner ornaments.
|
|
219
236
|
- **Selection HUD (3.9.4):** Selecting a card in hand now paints a highlighted bar under it AND a centered `► A♠ — Trump ◄` readout below, color-coded by suit / trump / legality.
|
|
220
237
|
- **Hand Sorting:** Strategic "play value" organization (honors grouped together) for better tactical awareness.
|
|
221
|
-
- **Main Menu:** Simple single-player entry point
|
|
238
|
+
- **Main Menu:** Simple single-player entry point; AI difficulty, Target Score, Speed and Theme live in the Settings screen.
|
|
222
239
|
- **Undo/Redo:** Press `Z` to undo your last move during bidding or play.
|
|
223
240
|
- **Statistics:** unified global tracking of games played, win rates, best rounds, and BelAtro expansion milestones.
|
|
224
241
|
- **Responsive Layout (3 tiers):** Three preset layouts — **compact** (80×32, fits 1366×768), **standard** (96×38), **spacious** (120×48+). The game picks the largest preset that fits your terminal on every render, so resizing mid-game adapts automatically; cards, side columns, and HUD verbosity all scale with the preset. Vertical centering pads tall terminals so the game never clings to the top.
|
|
@@ -264,7 +281,7 @@ belote/
|
|
|
264
281
|
│ ├── input.py # Platform-dispatched key reader and interruptible sleep
|
|
265
282
|
│ ├── stats.py # Global and session statistics tracking
|
|
266
283
|
│ └── rules.py # Game rules content
|
|
267
|
-
├── tests/ # Comprehensive test suite (
|
|
284
|
+
├── tests/ # Comprehensive test suite (1228 tests)
|
|
268
285
|
├── scripts/ # Performance benchmarks
|
|
269
286
|
├── pyproject.toml # Build system and dev dependencies (ruff/mypy)
|
|
270
287
|
├── LICENSE # MIT License
|
|
@@ -279,14 +296,14 @@ belote/
|
|
|
279
296
|
PYTHONPATH=src pytest
|
|
280
297
|
```
|
|
281
298
|
|
|
282
|
-
Currently **
|
|
299
|
+
Currently **1286 tests** passing with 100% coverage on game-logic modules (4.10.0).
|
|
283
300
|
|
|
284
301
|
## Technical Integrity
|
|
285
302
|
|
|
286
303
|
The codebase is strictly validated with the following tools:
|
|
287
304
|
- **mypy**: 0 errors (strict type safety)
|
|
288
305
|
- **ruff**: 0 violations (linting & formatting)
|
|
289
|
-
- **pytest**:
|
|
306
|
+
- **pytest**: 1228/1228 passed
|
|
290
307
|
- **Functional Architecture**: Purely immutable state transitions using `dataclasses.replace`
|
|
291
308
|
- **Performance**: High-efficiency rendering and sub-millisecond AI decision times (see `scripts/benchmark.py`)
|
|
292
309
|
|