middle.py
python · 201 lines
1#!/usr/bin/env python32"""Middle Path — a small reflective ritual for the terminal."""34from __future__ import annotations56import os7import random8import sys9import time10from typing import Final1112# -- Constants ---------------------------------------------------------------1314INPUT_MIN: Final[int] = -515INPUT_MAX: Final[int] = 516MIN_PAIRS: Final[int] = 517MAX_PAIRS: Final[int] = 818PAUSE_SHORT: Final[float] = 0.419PAUSE_LONG: Final[float] = 0.82021DITCH_THRESHOLD: Final[int] = 422LEANING_THRESHOLD: Final[int] = 22324ALL_PAIRS: Final[list[tuple[str, str]]] = [25 ("Indulgence", "Denial"),26 ("Holding", "Releasing"),27 ("Knowing", "Not knowing"),28 ("Self", "No-self"),29 ("Effort", "Collapse"),30 ("Control", "Letting go"),31 ("Clinging", "Avoidance"),32 ("Certainty", "Confusion"),33 ("Doing too much", "Doing nothing"),34]3536GREETING: Final[str] = """37You are walking.3839Stay between the ditches.4041Press Enter to begin.42"""4344FAREWELL: Final[str] = """45You are still walking.4647Not perfect.48Not lost.4950Just here.51"""525354# -- Position classification -------------------------------------------------5556class Position:57 """Named constants for classified positions on the path."""5859 DITCH: Final[str] = "ditch"60 LEANING: Final[str] = "leaning"61 STEADY: Final[str] = "steady"626364# -- Functions ---------------------------------------------------------------656667def clear_screen() -> None:68 """Clear the terminal screen."""69 os.system("cls" if os.name == "nt" else "clear") # noqa: S605707172def print_greeting() -> None:73 """Display the opening message and wait for the user to begin."""74 print(GREETING)75 input()767778def get_pairs() -> list[tuple[str, str]]:79 """Select and shuffle a random subset of tension pairs.8081 Returns:82 A list of 5-8 tension pairs in random order.83 """84 count = random.randint(MIN_PAIRS, MAX_PAIRS)85 pairs = random.sample(ALL_PAIRS, count)86 return pairs878889def present_pair(pair: tuple[str, str]) -> None:90 """Display a tension pair with its visual scale.9192 Args:93 pair: A tuple of (left_label, right_label).94 """95 left, right = pair96 print()97 print(f"{left} <------------------> {right}")98 print()99 print(f"Where are you right now? ({INPUT_MIN} to +{INPUT_MAX})")100 print()101102103def get_input() -> int:104 """Read and validate an integer from the user.105106 Reprompts gently until a valid integer in range is provided.107108 Returns:109 An integer between INPUT_MIN and INPUT_MAX inclusive.110 """111 while True:112 try:113 raw = input("> ").strip()114 value = int(raw)115 except ValueError:116 print()117 print("A number, please.")118 print()119 continue120 except (EOFError, KeyboardInterrupt):121 print()122 sys.exit(0)123124 if INPUT_MIN <= value <= INPUT_MAX:125 return value126127 print()128 print(f"Between {INPUT_MIN} and +{INPUT_MAX}.")129 print()130131132def classify_position(value: int) -> str:133 """Classify a position value into a named category.134135 Args:136 value: An integer in the range INPUT_MIN to INPUT_MAX.137138 Returns:139 One of Position.DITCH, Position.LEANING, or Position.STEADY.140 """141 magnitude = abs(value)142143 if magnitude >= DITCH_THRESHOLD:144 return Position.DITCH145 if magnitude >= LEANING_THRESHOLD:146 return Position.LEANING147 return Position.STEADY148149150def respond(position: str) -> str:151 """Return a gentle response for the given position classification.152153 Args:154 position: One of the Position constants.155156 Returns:157 A short reflective message.158 """159 responses: dict[str, str] = {160 Position.DITCH: (161 "You slipped into the ditch.\n"162 "Return to the path."163 ),164 Position.LEANING: "Careful. You're leaning.",165 Position.STEADY: "Steady. Walking.",166 }167 return responses[position]168169170def main() -> None:171 """Run a single session of Middle Path."""172 clear_screen()173 print_greeting()174175 pairs = get_pairs()176177 for pair in pairs:178 present_pair(pair)179180 while True:181 value = get_input()182 position = classify_position(value)183 message = respond(position)184185 print()186 time.sleep(PAUSE_SHORT)187 print(message)188 time.sleep(PAUSE_LONG)189190 if position != Position.DITCH:191 break192193 # Ditch: repeat the same pair.194 print()195196 print(FAREWELL)197198199if __name__ == "__main__":200 main()201