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.
Files changed (206) hide show
  1. {belote_cli-4.9.6 → belote_cli-4.10.0}/.gitignore +4 -0
  2. {belote_cli-4.9.6 → belote_cli-4.10.0}/CHANGELOG.md +184 -0
  3. {belote_cli-4.9.6 → belote_cli-4.10.0}/DEVELOPMENT.md +131 -40
  4. {belote_cli-4.9.6 → belote_cli-4.10.0}/PKG-INFO +34 -17
  5. {belote_cli-4.9.6 → belote_cli-4.10.0}/README.md +32 -15
  6. {belote_cli-4.9.6 → belote_cli-4.10.0}/pyproject.toml +1 -1
  7. {belote_cli-4.9.6 → belote_cli-4.10.0}/scripts/benchmark.py +8 -8
  8. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/__init__.py +1 -1
  9. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/a11y.py +71 -7
  10. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/achievements.py +11 -3
  11. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ai.py +34 -38
  12. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ansi.py +5 -12
  13. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/round_ledger.py +5 -5
  14. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/run_state.py +12 -0
  15. belote_cli-4.10.0/src/belote/belatro/core/scoring.py +636 -0
  16. belote_cli-4.10.0/src/belote/belatro/core/state.py +46 -0
  17. belote_cli-4.10.0/src/belote/belatro/daily.py +224 -0
  18. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/engine/event_bus.py +20 -0
  19. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/engine/modifier_patch.py +11 -10
  20. belote_cli-4.10.0/src/belote/belatro/engine/round_driver.py +644 -0
  21. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ghost_run.py +13 -9
  22. belote_cli-4.10.0/src/belote/belatro/integration.py +75 -0
  23. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/base.py +12 -0
  24. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/contract.py +5 -3
  25. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/economy.py +8 -1
  26. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/trick_timing.py +15 -3
  27. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/shaper.py +2 -2
  28. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/tarots.py +4 -1
  29. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/main.py +458 -325
  30. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/personality.py +2 -1
  31. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/trust.py +1 -9
  32. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/progression/save.py +85 -29
  33. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/progression/unlocks.py +3 -2
  34. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/ante_themes.py +19 -3
  35. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/boss.py +8 -5
  36. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/shop.py +52 -4
  37. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run_summary.py +4 -6
  38. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/announce.py +17 -57
  39. belote_cli-4.10.0/src/belote/belatro/ui/collection.py +155 -0
  40. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/history.py +4 -2
  41. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/hud.py +12 -19
  42. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/rules.py +29 -33
  43. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/shop.py +71 -5
  44. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/trust_bar.py +31 -7
  45. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/config.py +7 -21
  46. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/deck.py +17 -12
  47. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/game.py +99 -101
  48. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/gameflow.py +259 -258
  49. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/input.py +104 -68
  50. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/main.py +59 -31
  51. belote_cli-4.10.0/src/belote/paths.py +45 -0
  52. belote_cli-4.10.0/src/belote/round_engine.py +118 -0
  53. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/scoring.py +35 -30
  54. belote_cli-4.10.0/src/belote/settings.py +158 -0
  55. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/stats.py +106 -20
  56. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/themes.py +42 -6
  57. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/anim.py +6 -0
  58. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/announce.py +78 -18
  59. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/menu.py +46 -93
  60. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/prompts.py +37 -32
  61. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/render.py +92 -101
  62. belote_cli-4.10.0/src/belote/ui/settings_menu.py +167 -0
  63. belote_cli-4.10.0/src/belote/ui/text.py +21 -0
  64. belote_cli-4.10.0/src/belote/variants.py +49 -0
  65. belote_cli-4.10.0/tests/belatro/_events.py +79 -0
  66. belote_cli-4.10.0/tests/belatro/test_audit_round_fixes.py +586 -0
  67. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_boss_contracts.py +91 -15
  68. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_boss_modifiers_integration.py +33 -31
  69. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_competition_malediction.py +9 -17
  70. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_contract_unlocks.py +6 -5
  71. belote_cli-4.10.0/tests/belatro/test_daily.py +108 -0
  72. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_dead_flag_fixes.py +113 -43
  73. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_deck_variants.py +3 -2
  74. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_decks_4_5.py +10 -9
  75. belote_cli-4.10.0/tests/belatro/test_design_calls.py +116 -0
  76. belote_cli-4.10.0/tests/belatro/test_economy.py +109 -0
  77. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_event_bus.py +64 -0
  78. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_handler_registry.py +2 -1
  79. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_heist.py +10 -8
  80. belote_cli-4.10.0/tests/belatro/test_jokers.py +520 -0
  81. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_jokers_4_5.py +4 -3
  82. belote_cli-4.10.0/tests/belatro/test_partner_jokers.py +690 -0
  83. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_partner_trust.py +124 -8
  84. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase0_coverage.py +17 -15
  85. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase1_plumbing.py +2 -2
  86. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase2_content.py +5 -4
  87. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_phase3_meta.py +13 -2
  88. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_progression.py +113 -0
  89. belote_cli-4.10.0/tests/belatro/test_registry_integrity.py +131 -0
  90. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_round_driver.py +41 -28
  91. belote_cli-4.10.0/tests/belatro/test_run_state.py +185 -0
  92. belote_cli-4.10.0/tests/belatro/test_score_accumulator.py +117 -0
  93. belote_cli-4.10.0/tests/belatro/test_screens.py +187 -0
  94. belote_cli-4.10.0/tests/belatro/test_shop.py +257 -0
  95. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_slot_machine_tally.py +10 -8
  96. belote_cli-4.10.0/tests/conftest.py +24 -0
  97. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_a11y.py +41 -5
  98. belote_cli-4.10.0/tests/test_achievements.py +151 -0
  99. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai.py +13 -12
  100. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai_deluge_voids.py +3 -7
  101. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_alt_screen_scroll.py +5 -5
  102. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_announce_stats.py +3 -0
  103. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_belote.py +8 -6
  104. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_deluge_legal_cards.py +3 -2
  105. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_g2_ai_signals.py +1 -1
  106. belote_cli-4.10.0/tests/test_gameflow_headless.py +110 -0
  107. belote_cli-4.10.0/tests/test_hud_fit.py +72 -0
  108. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_input_eof.py +47 -2
  109. belote_cli-4.10.0/tests/test_input_escape_seqs.py +136 -0
  110. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_invariants.py +8 -8
  111. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_layout.py +3 -2
  112. belote_cli-4.10.0/tests/test_legal_bids.py +31 -0
  113. belote_cli-4.10.0/tests/test_menu_target_and_exit.py +130 -0
  114. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_official_rules.py +3 -2
  115. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_properties.py +6 -4
  116. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_diff.py +4 -10
  117. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_golden.py +4 -2
  118. belote_cli-4.10.0/tests/test_repo_layout.py +46 -0
  119. belote_cli-4.10.0/tests/test_round_engine.py +109 -0
  120. belote_cli-4.10.0/tests/test_settings.py +117 -0
  121. belote_cli-4.10.0/tests/test_stats_persistence.py +265 -0
  122. {belote_cli-4.9.6 → belote_cli-4.10.0}/uv.lock +1 -1
  123. belote_cli-4.9.6/.claude/settings.local.json +0 -19
  124. belote_cli-4.9.6/src/belote/belatro/core/scoring.py +0 -676
  125. belote_cli-4.9.6/src/belote/belatro/engine/round_driver.py +0 -558
  126. belote_cli-4.9.6/src/belote/belatro/ui/collection.py +0 -153
  127. belote_cli-4.9.6/tests/belatro/__init__.py +0 -0
  128. belote_cli-4.9.6/tests/belatro/test_belatro.py +0 -2023
  129. belote_cli-4.9.6/tests/belatro/test_partner_jokers.py +0 -357
  130. belote_cli-4.9.6/tests/belatro/test_registry_integrity.py +0 -61
  131. belote_cli-4.9.6/tests/test_achievements.py +0 -58
  132. {belote_cli-4.9.6 → belote_cli-4.10.0}/.python-version +0 -0
  133. {belote_cli-4.9.6 → belote_cli-4.10.0}/LICENSE +0 -0
  134. {belote_cli-4.9.6/src → belote_cli-4.10.0/src/belote/belatro}/__init__.py +0 -0
  135. {belote_cli-4.9.6/src/belote/belatro → belote_cli-4.10.0/src/belote/belatro/core}/__init__.py +0 -0
  136. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/core/economy.py +0 -0
  137. {belote_cli-4.9.6/src/belote/belatro/core → belote_cli-4.10.0/src/belote/belatro/engine}/__init__.py +0 -0
  138. {belote_cli-4.9.6/src/belote/belatro/engine → belote_cli-4.10.0/src/belote/belatro/items}/__init__.py +0 -0
  139. {belote_cli-4.9.6/src/belote/belatro/items → belote_cli-4.10.0/src/belote/belatro/items/jokers}/__init__.py +0 -0
  140. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/annonces.py +0 -0
  141. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/coinche.py +0 -0
  142. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/corrupted.py +0 -0
  143. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/jokers/hand_comp.py +0 -0
  144. {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
  145. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/passive.py +0 -0
  146. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/partner_jokers/risky.py +0 -0
  147. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/planets.py +0 -0
  148. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/registry.py +0 -0
  149. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/items/vouchers.py +0 -0
  150. {belote_cli-4.9.6/src/belote/belatro/items/partner_jokers → belote_cli-4.10.0/src/belote/belatro/partner}/__init__.py +0 -0
  151. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/partner/partner_state.py +0 -0
  152. {belote_cli-4.9.6/src/belote/belatro/partner → belote_cli-4.10.0/src/belote/belatro/progression}/__init__.py +0 -0
  153. {belote_cli-4.9.6/src/belote/belatro/progression → belote_cli-4.10.0/src/belote/belatro/run}/__init__.py +0 -0
  154. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/ante.py +0 -0
  155. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/run/decks.py +0 -0
  156. {belote_cli-4.9.6/src/belote/belatro/run → belote_cli-4.10.0/src/belote/belatro/ui}/__init__.py +0 -0
  157. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/consumables.py +0 -0
  158. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/inventory.py +0 -0
  159. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/belatro/ui/menu.py +0 -0
  160. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/context.py +0 -0
  161. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/replay.py +0 -0
  162. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/rules.py +0 -0
  163. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/__init__.py +0 -0
  164. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/fit_guard.py +0 -0
  165. {belote_cli-4.9.6 → belote_cli-4.10.0}/src/belote/ui/layout.py +0 -0
  166. {belote_cli-4.9.6/src/belote/belatro/ui → belote_cli-4.10.0/tests}/__init__.py +0 -0
  167. {belote_cli-4.9.6/tests → belote_cli-4.10.0/tests/belatro}/__init__.py +0 -0
  168. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_collection_logic.py +0 -0
  169. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_consumables_ui.py +0 -0
  170. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_endless.py +0 -0
  171. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_ghost_run.py +0 -0
  172. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_history_overlay.py +0 -0
  173. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_hud_synergy.py +0 -0
  174. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_hud_toggle.py +0 -0
  175. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_inventory_overlay.py +0 -0
  176. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_joker_callouts.py +0 -0
  177. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_joker_contracts.py +0 -0
  178. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_run_summary.py +0 -0
  179. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_shop_animations.py +0 -0
  180. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_shop_empty_pools.py +0 -0
  181. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/belatro/test_voucher_idempotency.py +0 -0
  182. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/perf_baselines.json +0 -0
  183. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ai_sans_atout.py +0 -0
  184. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_anim_helpers.py +0 -0
  185. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_ansi_helpers.py +0 -0
  186. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_belote_stinger.py +0 -0
  187. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_benchmark_smoke.py +0 -0
  188. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_bidding_all_pass.py +0 -0
  189. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_declaration_tiebreak.py +0 -0
  190. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_extended.py +0 -0
  191. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_g1_coinche_classic.py +0 -0
  192. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_game_logic.py +0 -0
  193. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_gameflow.py +0 -0
  194. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_hand_auto_sort.py +0 -0
  195. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_history_overlay_cache.py +0 -0
  196. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_input_wasd.py +0 -0
  197. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_new_coverage.py +0 -0
  198. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_no_color.py +0 -0
  199. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_perf_regression.py +0 -0
  200. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_felt_polish.py +0 -0
  201. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_render_hand_lift.py +0 -0
  202. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_replay.py +0 -0
  203. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_scoring_totals.py +0 -0
  204. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_undo.py +0 -0
  205. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_wcwidth.py +0 -0
  206. {belote_cli-4.9.6 → belote_cli-4.10.0}/tests/test_winner_glow.py +0 -0
@@ -147,3 +147,7 @@ cython_debug/
147
147
 
148
148
  # PyCharm
149
149
  .idea/
150
+
151
+ # Editor swap files
152
+ *.kate-swp
153
+ *.swp
@@ -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
- python -m venv .venv
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` for testing. Install it if you haven't already:
46
- ```bash
47
- pip install pytest
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/test_game.py
63
- PYTHONPATH=src pytest tests/belatro/test_scoring.py
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/test_game.py::test_play_card_legal
67
- PYTHONPATH=src pytest -k "test_scoring"
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 (1111 tests expected)
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 to stderr — readable by terminal screen readers (Orca, NVDA over
120
- WSL, VoiceOver via iTerm2).
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
- The following environment variables enable opt-in features. Each is read
131
- once at startup; toggling mid-run has no effect.
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 is written
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
- ### Code-only update (push to GitHub without releasing a new PyPI version)
258
+ The short version is in the [Cheat Sheet](#cheat-sheet) at the top. Details:
168
259
 
169
- If you're just iterating on code, fixing typos, updating docs, etc., and don't want to cut a new PyPI release yet:
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
- ## Releasing a New Version (Manual)
268
+ ### Full release (GitHub + PyPI)
178
269
 
179
- 1. **Bump the version** in BOTH `pyproject.toml` AND `src/belote/__init__.py`
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
- 2. **Add a CHANGELOG entry** at the top of `CHANGELOG.md`.
183
- 3. **Clean stale build artifacts:**
274
+ 3. **Add a CHANGELOG entry** at the top of `CHANGELOG.md`.
275
+ 4. **Commit, tag and push:**
184
276
  ```bash
185
- rm -rf dist/ build/ *.egg-info/
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
- 4. **Build, validate, and upload:**
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
- *Note: `twine upload` will prompt for your PyPI credentials or use your `~/.pypirc` file.*
195
-
196
- 5. **Commit and tag in git:**
197
- ```bash
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.4
1
+ Metadata-Version: 2.5
2
2
  Name: belote-cli
3
- Version: 4.9.6
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
- | | | > Start Game < |
117
- | | | AI: < Hard > |
118
- | | | Target: < 1000 > |
119
- | | | Speed: < Normal > |
120
- | | | Theme: < Classic Green > |
116
+ | | | Daily Challenge |
117
+ | | | > Start Game < |
118
+ | | | Settings |
121
119
  | | | Rules & History |
122
- \ \ | Statistics |
123
- `\ `\ | Quit |
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 (once uploaded)
161
- pip install belote-cli
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
- # Custom settings
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 with configurable AI difficulty, Target Score, and Speed.
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 (1111 tests)
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 **1111 tests** passing with 100% coverage on game-logic modules (4.9.6).
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**: 1111/1111 passed
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