I asked ChatGPT agent to turn program 1 from that mag into an executable python program. Here’s the code. It works:
"""
horse_racing_ti.py
===================
This module implements a command‑line horse racing game inspired by
"Program 1: TI Horse Racing" from the October 1984 issue of *Compute!*
magazine. The original program was written in TI Extended BASIC and used
simple sprite animation to race horses across the screen. Players could
wager on the outcome using win, place, show, exacta (called "exactor" in
the magazine) or quinella bets and watch the race unfold.
The implementation here reimagines those mechanics for a modern Python
environment. It keeps the core ideas and payouts from the original
source while simplifying some of the visual aspects. Each race features
a random number of horses (between 4 and 12) with randomly chosen names
and odds. The player begins with $1000 and participates in up to nine
races, placing bets on their chosen horse(s) each time. After all
horses have finished, the program computes the payout based on the bet
type and odds, updates the player's bankroll and optionally records a
new high score. At the end of the session, the player can choose to
start over or exit.
Key features
------------
* **Random race composition:** Each race has between 4 and 12 horses.
Horse names are drawn from a list (no duplicates) and odds are
randomly generated to fall between 1 and 10 as in the TI code.
* **Flexible betting:** The player may place one of five types of bets:
win (horse must finish first), place (finish in the top two), show
(finish in the top three), exacta (pick the top two finishers in
order) or quinella (pick the top two in any order). Wagers are
validated against the player’s current money.
* **Race simulation:** Instead of animating sprites, the simulation
assigns each horse a random performance time derived from the sum of
several random steps. Faster times finish earlier; ties are broken
randomly. A simple progress indicator prints the name of each
participating horse along with its "distance" after each tick to add
suspense.
* **Authentic payouts:** Payouts mirror the logic in the original
Extended BASIC code. Win bets pay the full odds, place bets pay half
the odds, show bets pay a third of the odds, exacta bets pay the
combined odds of the two chosen horses, and quinella bets pay half of
that combined value.
This script is self‑contained and does not require external modules
outside of the Python standard library. To play the game, run the
module directly with Python 3.
Example::
$ python3 horse_racing_ti.py
Copyright © 2025
Distributed under the MIT license.
"""
import random
import os
import json
class HorseRacingGame:
"""Encapsulates the horse racing game logic."""
# List of horse names (mirrors those from the original DATA lines)
HORSE_NAMES = [
"SUNNY EASTER", "WAY TO GO", "HORSE", "MISS CLEM", "GIVE ME ACTION",
"I'M IN FRONT", "MYSTIC LIGHTNING", "SUPERCHARGED", "INDIAN PRINCESS",
"HOLD ON TIGHT", "SPEED DEMON", "LITTLE DEVIL", "GENESIS", "ACER",
"MR ARNESON", "ON TOP", "SLOWPOKE", "CUREDENT", "JUGGERNAUT",
"GUSTAFU"
]
def __init__(self, bankroll: int = 1000, num_races: int = 9):
self.bankroll = bankroll
self.initial_bankroll = bankroll
self.num_races = num_races
self.current_race = 1
# Record file for high scores (in the same directory as script)
self.record_file = os.path.join(os.path.dirname(__file__), "horse_racing_record.json")
self.high_score, self.high_scorer = self._load_record()
def _load_record(self):
"""Load the high score record from disk.
Returns a tuple (score, name). If no record exists, returns
(0, "")."""
try:
with open(self.record_file, "r", encoding="utf-8") as f:
data = json.load(f)
return data.get("score", 0), data.get("name", "")
except FileNotFoundError:
return 0, ""
except Exception:
# If the file is corrupted, ignore it.
return 0, ""
def _save_record(self, score: int, name: str) -> None:
"""Save a new high score to disk."""
data = {"score": score, "name": name[:20]}
with open(self.record_file, "w", encoding="utf-8") as f:
json.dump(data, f)
def _choose_horses(self) -> list:
"""Randomly select a subset of horse names for the current race.
The number of horses is uniformly chosen between 4 and 12
inclusive. Names are chosen without replacement.
Returns a list of selected horse names.
"""
num_horses = random.randint(4, 12)
return random.sample(self.HORSE_NAMES, num_horses)
@staticmethod
def _generate_odds(num_horses: int) -> list:
"""Generate random odds for each horse.
Follows the logic of the TI BASIC program: Each horse starts
with zero, then is incremented by a random number between 0
and 15 inclusive one hundred times. The final odds are
computed by subtracting the tenth of the accumulated total
from 10, with a floor of 1.
"""
# Start with zeros
odd_accum = [0] * num_horses
# Increment one random horse by a random value 0–15 one hundred times
for _ in range(100):
horse_idx = random.randrange(num_horses)
odd_accum[horse_idx] += random.randint(0, 15)
# Convert to odds between 1 and 10
odds = []
for total in odd_accum:
# Equivalent to ODD(A) = 10 - INT(ODD(A)/10); minimum 1
# In the BASIC code ODD accumulators are integers of
# increments; dividing by 10, truncated, then subtract from 10.
odd_value = 10 - (total // 10)
if odd_value < 1:
odd_value = 1
odds.append(odd_value)
return odds
def _simulate_race(self, horses: list) -> list:
"""Simulate a race and return the finishing order.
Each horse is assigned a performance time by summing a number
of random steps. Lower times finish earlier. To add a
dramatic element, progress is printed on screen so players can
watch the race unfold in real time.
Returns a list of indices representing the order of finish.
"""
num_horses = len(horses)
# Each horse is represented by a dict with cumulative distance
positions = [0 for _ in range(num_horses)]
finished = [False] * num_horses
finish_order = []
# Distance required to finish; roughly corresponds to ~224
finish_distance = 100
tick = 0
while len(finish_order) < num_horses:
tick += 1
# Move each horse a random step (0–4) if not finished
for idx in range(num_horses):
if not finished[idx]:
positions[idx] += random.randint(0, 4)
if positions[idx] >= finish_distance:
finished[idx] = True
finish_order.append(idx)
# Print progress every few ticks to avoid flooding
if tick % 5 == 0:
print("\nRace progress:")
for idx, name in enumerate(horses):
bar = "." * (positions[idx] // 4)
marker = "🐎"
print(f" {name:<20} {bar}{marker}")
return finish_order
def _get_bet(self, horses: list, odds: list) -> tuple:
"""Prompt the user for bet type, amount and horses.
Returns a tuple (bet_type, bet_amount, horse1_index, horse2_index)
where horse2_index may be None for win/place/show bets.
"""
print("\nWhat would you like to bet?")
print("(W)in, (P)lace, (S)how, (E)xacta or (Q)uinella.")
bet_type = ""
while bet_type not in {"W", "P", "S", "E", "Q"}:
bet_type = input("Enter choice (W/P/S/E/Q): ").strip().upper()
# Get bet amount
bet = 0
while bet <= 0 or bet > self.bankroll:
try:
bet = int(input(f"You have ${self.bankroll}. Enter bet amount: "))
except ValueError:
bet = 0
if bet <= 0 or bet > self.bankroll:
print("Please enter a valid amount within your bankroll.")
self.bankroll -= bet
# Determine horse(s) to bet on
if bet_type in {"W", "P", "S"}:
horse_idx = -1
while horse_idx < 0 or horse_idx >= len(horses):
try:
horse_idx = int(input(f"Which horse number (1-{len(horses)}): ")) - 1
except ValueError:
horse_idx = -1
if horse_idx < 0 or horse_idx >= len(horses):
print("Not a valid horse.")
return bet_type, bet, horse_idx, None
else:
# Exacta or Quinella: ask for two horse numbers
first_idx = second_idx = -1
while True:
try:
raw = input(f"Enter two horse numbers separated by comma (1-{len(horses)}): ")
parts = raw.replace(" ", "").split(",")
if len(parts) != 2:
raise ValueError
first_idx = int(parts[0]) - 1
second_idx = int(parts[1]) - 1
except ValueError:
print("Please enter two valid numbers separated by a comma.")
continue
# validate
if (first_idx < 0 or first_idx >= len(horses) or
second_idx < 0 or second_idx >= len(horses)):
print("One of those horses is not running.")
continue
if first_idx == second_idx:
print("Please choose two different horses.")
continue
break
return bet_type, bet, first_idx, second_idx
def _settle_bet(
self,
bet_type: str,
bet_amount: int,
horse_idx: int,
horse2_idx: int,
finish_order: list,
odds: list
) -> None:
"""Settle the bet and update bankroll.
Implements the same payout logic as the Extended BASIC code.
Prints a message indicating whether the player won or lost and
their winnings if applicable.
"""
if bet_type in {"W", "P", "S"}:
# Determine place condition
allowed_positions = {
"W": {0}, # must finish first
"P": {0, 1}, # top two
"S": {0, 1, 2}, # top three
}[bet_type]
if finish_order.index(horse_idx) in allowed_positions:
# Payout factor: 1 for Win, 2 for Place, 3 for Show
denominator = {"W": 1, "P": 2, "S": 3}[bet_type]
winnings = bet_amount * odds[horse_idx] // denominator
print("You win!")
print(f"You get ${winnings}.")
self.bankroll += winnings
else:
print("You lost. Too bad.")
elif bet_type == "E":
# Exacta: must pick first and second in exact order
if finish_order[0] == horse_idx and finish_order[1] == horse2_idx:
winnings = bet_amount * (odds[horse_idx] + odds[horse2_idx])
print("You won the exacta!")
print(f"Payoff: ${winnings}")
self.bankroll += winnings
else:
print("You lost the exacta. Too bad.")
if self.bankroll == 0:
print("You have no money left.")
else: # Quinella
# Either horse can place first or second
first_two = {finish_order[0], finish_order[1]}
if {horse_idx, horse2_idx} == first_two:
winnings = (bet_amount * (odds[horse_idx] + odds[horse2_idx])) // 2
print("You win the quinella!")
print(f"Winnings: ${winnings}")
self.bankroll += winnings
else:
print("You lost the quinella. Too bad.")
if self.bankroll == 0:
print("You have no money left.")
def play(self) -> None:
"""Run the horse racing game session."""
print("At the Races!")
if self.high_score > 0:
print(f"Record: ${self.high_score} by {self.high_scorer}")
input("Press Enter to begin...")
while self.current_race <= self.num_races:
print("\n" + "=" * 40)
print(f"Race #{self.current_race}")
# Select horses and generate odds
horses = self._choose_horses()
odds = self._generate_odds(len(horses))
# Display horses and odds
for idx, (name, odd) in enumerate(zip(horses, odds), start=1):
print(f"{idx}. {name} \t {odd}:1")
# Get bet information
bet_type, bet_amount, horse_idx, horse2_idx = self._get_bet(horses, odds)
# Simulate the race
print("\nThere they go!")
finish_order = self._simulate_race(horses)
# Display official results
print("\nOfficial results")
print(f"{'Place':<6}{'Horse #':<8}{'Name'}")
for pos, idx in enumerate(finish_order, start=1):
print(f"{pos:<6}{idx+1:<8}{horses[idx]}")
# Settle bet
self._settle_bet(bet_type, bet_amount, horse_idx, horse2_idx, finish_order, odds)
# Next race
self.current_race += 1
# Early termination if bankroll is gone
if self.bankroll <= 0:
print("\nYou've run out of money!")
break
# End of session
print("\nThat's all.")
print(f"You ended with ${self.bankroll}.")
# Check record
if self.bankroll > self.high_score:
print("You set a new record!")
name = input("Please enter your name: ")
self._save_record(self.bankroll, name)
self.high_score, self.high_scorer = self.bankroll, name
elif self.bankroll == self.high_score and self.high_score > 0:
print("You tied the record!")
# Ask to play again
again = ""
while again not in {"Y", "N"}:
again = input("Would you like to try again? (Y/N): ").strip().upper()
if again == "Y":
# Reset state and replay
self.bankroll = self.initial_bankroll
self.current_race = 1
self.play()
if __name__ == "__main__":
# Seed the RNG to vary outcomes
random.seed()
game = HorseRacingGame()
game.play()