Herman Entertainment System – Adding Games


Once you have programmed your Herman Entertainment System you will want to start making and adding your own games. If you use our HES Space on Perplexity it should make the prompting easy if you want to use AI to create more games, it already has the system specifications and coding information setup. For an example of this, see our discussion for an Oregon Trail clone, it worked well and is an interesting take on the game given the system limitations. Instructions after the video.

TLDR: To add games to the HES use the following steps.
1. To connect to the HES:
ssh [email protected]
2. To create the game file, then paste and save code:
nano /home/herman/games/oregon_trail.py

3. To make the game executable:
chmod +x /home/herman/games/oregon_trail.py

4. To restart the system and load new games:
sudo reboot


* To remove broken games:
rm /home/herman/games/oregon_trail.py

Once you have the python code you would like to use you will need to SSH into the HES to add the game. This essentially makes it so you are running the command line on the device, the HES in this case, and not the computer you are operating. This allows you to run code, install apps, and run all sorts of function on the HES (raspi). We will use this Oregon trail game as an example. You start by connecting to the HES, you will need to look at your wifi backend to determine the ip address, for example ours is at 192.168.1.95 and was listed on the router as “raspberrypi”. Once you have this you can open a terminal or command line and type (but use your address):

ssh [email protected]

You will be prompted for the password you created when you first setup the HES. Next we need to create the game file.

nano /home/herman/games/oregon_trail.py

This will create the file and open a text editor where we will paste our game code. Please note, there is a conflict with something in the naming conventions and the way the code runs, it is highly recommended to include an underscore in the game name to prevent these conflicts, so “oregon_trail.py” instead of “oregontrail.py” or “tetris_game.py” instead of “tetris.py”.

Now we copy and paste the following into the game file.

#!/usr/bin/env python3
import time
import random
import RPi.GPIO as GPIO
from PIL import Image, ImageDraw, ImageFont

class OregonTrail:
    def __init__(self, display_obj, gpio_obj, backlight_obj):
        if None in (display_obj, gpio_obj, backlight_obj):
            raise ValueError("Missing hardware components")
            
        self.display = display_obj
        self.GPIO = gpio_obj
        self.backlight = backlight_obj
        self.last_input_time = 0
        self.debounce_delay = 0.15
        
        # Optimized fonts for 240x240 display
        try:
            self.font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
            self.font_medium = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16)
            self.font_large = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 18)
        except:
            self.font_small = ImageFont.load_default()
            self.font_medium = ImageFont.load_default()
            self.font_large = ImageFont.load_default()

        self.reset_game()

    def reset_game(self):
        # Simplified game state
        self.money = 400
        self.food = 0
        self.bullets = 0
        self.oxen = 0
        self.clothes = 0
        
        # Simplified party (3 people instead of 5)
        self.party = [
            {"name": "You", "sick": False},
            {"name": "Wife", "sick": False},
            {"name": "Child", "sick": False}
        ]
        
        # Journey
        self.miles = 0
        self.month = 3  # March
        self.day = 1
        
        # Simplified landmarks
        self.landmarks = [
            {"name": "Independence", "miles": 0},
            {"name": "Kansas River", "miles": 185},
            {"name": "Big Blue River", "miles": 304},
            {"name": "Fort Kearney", "miles": 554},
            {"name": "Chimney Rock", "miles": 640},
            {"name": "Fort Laramie", "miles": 932},
            {"name": "Independence Rock", "miles": 1288},
            {"name": "Snake River", "miles": 1828},
            {"name": "Oregon City", "miles": 2040}
        ]
        self.current_landmark = 0

    def read_controls_debounced(self):
        current_time = time.time()
        if current_time - self.last_input_time < self.debounce_delay:
            return {}
            
        controls = {
            'up': not self.GPIO.input(17),
            'down': not self.GPIO.input(22),
            'left': not self.GPIO.input(27),
            'right': not self.GPIO.input(23),
            'button_a': not self.GPIO.input(5),
            'button_b': not self.GPIO.input(6)
        }
        
        if any(controls.values()):
            self.last_input_time = current_time
        return controls

    def draw_large_wagon(self, draw, x, y, color=(0, 255, 0)):
        """Draw a detailed wagon sprite"""
        # Wagon body
        draw.rectangle([x+10, y+5, x+50, y+25], fill=color)
        draw.rectangle([x+12, y+7, x+48, y+23], outline=(0, 0, 0), width=2)
        
        # Wagon cover
        draw.ellipse([x+8, y-5, x+52, y+15], fill=(255, 255, 255))
        draw.ellipse([x+10, y-3, x+50, y+13], outline=(0, 0, 0), width=2)
        
        # Wheels
        draw.ellipse([x+5, y+20, x+20, y+35], fill=color)
        draw.ellipse([x+7, y+22, x+18, y+33], outline=(0, 0, 0), width=2)
        draw.ellipse([x+40, y+20, x+55, y+35], fill=color)
        draw.ellipse([x+42, y+22, x+53, y+33], outline=(0, 0, 0), width=2)
        
        # Spokes
        draw.line([x+12, y+27, x+13, y+28], fill=(0, 0, 0), width=2)
        draw.line([x+47, y+27, x+48, y+28], fill=(0, 0, 0), width=2)

    def draw_broken_wagon(self, draw, x, y):
        """Draw a broken wagon with missing wheel"""
        # Tilted wagon body
        draw.polygon([x+10, y+15, x+50, y+10, x+48, y+30, x+12, y+35], fill=(139, 69, 19))
        
        # Torn wagon cover
        draw.polygon([x+8, y+5, x+30, y-5, x+52, y+8, x+35, y+20], fill=(200, 200, 200))
        
        # One good wheel
        draw.ellipse([x+5, y+30, x+20, y+45], fill=(139, 69, 19))
        draw.ellipse([x+7, y+32, x+18, y+43], outline=(0, 0, 0), width=2)
        
        # Broken wheel pieces
        draw.arc([x+40, y+25, x+55, y+40], 0, 180, fill=(139, 69, 19), width=3)
        draw.line([x+45, y+35, x+50, y+45], fill=(139, 69, 19), width=2)

    def draw_oxen(self, draw, x, y, color=(139, 69, 19)):
        """Draw oxen pulling the wagon"""
        # Ox body
        draw.ellipse([x, y+10, x+25, y+25], fill=color)
        # Ox head
        draw.ellipse([x-8, y+12, x+5, y+23], fill=color)
        # Horns
        draw.line([x-6, y+12, x-4, y+8], fill=(255, 255, 255), width=2)
        draw.line([x+1, y+12, x+3, y+8], fill=(255, 255, 255), width=2)
        # Legs
        draw.line([x+5, y+25, x+5, y+32], fill=color, width=3)
        draw.line([x+15, y+25, x+15, y+32], fill=color, width=3)
        draw.line([x+20, y+25, x+20, y+32], fill=color, width=3)

    def draw_sick_oxen(self, draw, x, y):
        """Draw sick/injured oxen"""
        # Lying down ox
        draw.ellipse([x, y+20, x+25, y+30], fill=(100, 50, 0))
        draw.ellipse([x-8, y+22, x+5, y+28], fill=(100, 50, 0))
        # X eyes (dead/sick)
        draw.line([x-5, y+23, x-2, y+26], fill=(255, 0, 0), width=2)
        draw.line([x-2, y+23, x-5, y+26], fill=(255, 0, 0), width=2)

    def draw_river_scene(self, draw, x, y):
        """Draw river crossing scene"""
        # River waves
        for i in range(4):
            draw.arc([x+i*20, y+15, x+i*20+25, y+30], 0, 180, fill=(0, 150, 255), width=3)
            draw.arc([x+i*20+12, y+20, x+i*20+37, y+35], 180, 360, fill=(0, 150, 255), width=3)
        
        # Small wagon crossing
        self.draw_large_wagon(draw, x+30, y-5, (139, 69, 19))

    def draw_fort(self, draw, x, y):
        """Draw fort with flag"""
        # Fort walls
        draw.rectangle([x, y, x+60, y+40], fill=(139, 69, 19))
        draw.rectangle([x+5, y+5, x+55, y+35], outline=(0, 0, 0), width=2)
        
        # Towers
        draw.rectangle([x-5, y-10, x+10, y+20], fill=(139, 69, 19))
        draw.rectangle([x+50, y-10, x+65, y+20], fill=(139, 69, 19))
        
        # Flag
        draw.line([x+30, y-15, x+30, y+5], fill=(139, 69, 19), width=3)
        draw.polygon([x+30, y-15, x+45, y-10, x+30, y-5], fill=(255, 0, 0))

    def draw_chimney_rock(self, draw, x, y):
        """Draw Chimney Rock landmark"""
        # Base rock
        draw.ellipse([x, y+20, x+50, y+50], fill=(128, 128, 128))
        # Chimney spire
        draw.rectangle([x+20, y-10, x+30, y+30], fill=(160, 160, 160))
        draw.rectangle([x+18, y-12, x+32, y-8], fill=(128, 128, 128))

    def draw_mountains(self, draw, x, y):
        """Draw mountain range"""
        # Multiple mountain peaks
        draw.polygon([x, y+40, x+20, y, x+40, y+25, x+60, y-5, x+80, y+40], fill=(100, 100, 100))
        draw.polygon([x+10, y+40, x+30, y+10, x+50, y+40], fill=(120, 120, 120))
        
        # Snow caps
        draw.polygon([x+15, y+5, x+25, y, x+35, y+5, x+25, y+15], fill=(255, 255, 255))
        draw.polygon([x+50, y, x+60, y-5, x+70, y, x+60, y+10], fill=(255, 255, 255))

    def draw_grave(self, draw, x, y):
        """Draw gravestone"""
        # Gravestone
        draw.rectangle([x+20, y+10, x+40, y+40], fill=(128, 128, 128))
        draw.ellipse([x+20, y+10, x+40, y+25], fill=(128, 128, 128))
        
        # Cross
        draw.line([x+30, y+15, x+30, y+30], fill=(255, 255, 255), width=3)
        draw.line([x+25, y+20, x+35, y+20], fill=(255, 255, 255), width=3)
        
        # RIP text
        draw.text((x+22, y+25), "RIP", font=self.font_small, fill=(0, 0, 0))

    def draw_storm_clouds(self, draw, x, y):
        """Draw storm clouds with lightning"""
        # Dark clouds
        draw.ellipse([x, y, x+30, y+20], fill=(64, 64, 64))
        draw.ellipse([x+20, y-5, x+50, y+15], fill=(64, 64, 64))
        draw.ellipse([x+40, y, x+70, y+20], fill=(64, 64, 64))
        
        # Lightning bolt
        draw.polygon([x+35, y+20, x+30, y+35, x+35, y+35, x+25, y+50, x+30, y+50, x+40, y+30], 
                    fill=(255, 255, 0))

    def draw_thief(self, draw, x, y):
        """Draw thief stealing supplies"""
        # Thief figure (stick figure with hat)
        draw.ellipse([x+15, y, x+25, y+10], fill=(255, 200, 150))  # Head
        draw.rectangle([x+12, y-5, x+28, y+2], fill=(0, 0, 0))     # Hat
        draw.line([x+20, y+10, x+20, y+30], fill=(0, 0, 0), width=3)  # Body
        draw.line([x+20, y+15, x+15, y+25], fill=(0, 0, 0), width=2)  # Arm
        draw.line([x+20, y+15, x+25, y+25], fill=(0, 0, 0), width=2)  # Arm
        draw.line([x+20, y+30, x+15, y+40], fill=(0, 0, 0), width=2)  # Leg
        draw.line([x+20, y+30, x+25, y+40], fill=(0, 0, 0), width=2)  # Leg
        
        # Stolen supplies bag
        draw.ellipse([x+30, y+20, x+45, y+35], fill=(139, 69, 19))
        draw.text((x+32, y+25), "$", font=self.font_small, fill=(255, 255, 0))

    def draw_snake(self, draw, x, y):
        """Draw snake for snake bite event"""
        # Snake body (S-curve)
        points = []
        for i in range(0, 60, 2):
            snake_y = y + 20 + int(10 * (i % 20 - 10) / 10)
            points.append((x + i, snake_y))
        
        for i in range(len(points)-1):
            draw.line([points[i], points[i+1]], fill=(0, 128, 0), width=4)
        
        # Snake head
        draw.ellipse([x+55, y+15, x+65, y+25], fill=(0, 100, 0))
        # Eyes
        draw.ellipse([x+57, y+17, x+59, y+19], fill=(255, 0, 0))
        draw.ellipse([x+61, y+17, x+63, y+19], fill=(255, 0, 0))

    def draw_deer(self, draw, x, y):
        """Draw deer for hunting"""
        # Deer body
        draw.ellipse([x+10, y+15, x+35, y+25], fill=(139, 69, 19))
        # Deer head
        draw.ellipse([x+30, y+10, x+45, y+20], fill=(139, 69, 19))
        # Antlers
        draw.line([x+35, y+5, x+32, y], fill=(160, 160, 160), width=2)
        draw.line([x+35, y+5, x+38, y], fill=(160, 160, 160), width=2)
        draw.line([x+40, y+5, x+37, y], fill=(160, 160, 160), width=2)
        draw.line([x+40, y+5, x+43, y], fill=(160, 160, 160), width=2)
        # Legs
        draw.line([x+15, y+25, x+15, y+35], fill=(139, 69, 19), width=2)
        draw.line([x+20, y+25, x+20, y+35], fill=(139, 69, 19), width=2)
        draw.line([x+25, y+25, x+25, y+35], fill=(139, 69, 19), width=2)
        draw.line([x+30, y+25, x+30, y+35], fill=(139, 69, 19), width=2)

    def draw_event_graphic(self, draw, event_type, x, y):
        """Draw graphics for different event types"""
        if event_type == "wagon_break":
            self.draw_broken_wagon(draw, x, y)
        elif event_type == "ox_injury":
            self.draw_sick_oxen(draw, x, y)
        elif event_type == "storm":
            self.draw_storm_clouds(draw, x, y)
        elif event_type == "theft":
            self.draw_thief(draw, x, y)
        elif event_type == "snake_bite":
            self.draw_snake(draw, x, y)
        elif event_type == "sickness":
            self.draw_grave(draw, x, y)
        elif event_type == "river_crossing":
            self.draw_river_scene(draw, x, y)
        elif event_type == "hunting":
            self.draw_deer(draw, x, y)
        elif event_type == "good_weather":
            # Sun
            draw.ellipse([x+20, y+10, x+40, y+30], fill=(255, 255, 0))
            # Sun rays
            for angle in range(0, 360, 45):
                import math
                rad = math.radians(angle)
                x1 = x + 30 + int(25 * math.cos(rad))
                y1 = y + 20 + int(25 * math.sin(rad))
                x2 = x + 30 + int(35 * math.cos(rad))
                y2 = y + 20 + int(35 * math.sin(rad))
                draw.line([x1, y1, x2, y2], fill=(255, 255, 0), width=2)

    def draw_landmark_graphic(self, draw, landmark_name, x, y):
        """Draw landmark-specific graphics"""
        if "River" in landmark_name:
            self.draw_river_scene(draw, x, y)
        elif "Fort" in landmark_name:
            self.draw_fort(draw, x, y)
        elif "Chimney Rock" in landmark_name:
            self.draw_chimney_rock(draw, x, y)
        elif "Rock" in landmark_name:
            # Independence Rock
            draw.ellipse([x, y+15, x+60, y+45], fill=(128, 128, 128))
            draw.ellipse([x+20, y, x+80, y+35], fill=(128, 128, 128))
            draw.text((x+25, y+20), "1847", font=self.font_small, fill=(0, 0, 0))
        elif "Pass" in landmark_name or "Mountain" in landmark_name:
            self.draw_mountains(draw, x, y)
        else:
            # Default town graphic
            draw.rectangle([x+10, y+20, x+30, y+40], fill=(139, 69, 19))
            draw.polygon([x+5, y+20, x+20, y+10, x+35, y+20], fill=(255, 0, 0))
            draw.rectangle([x+40, y+25, x+55, y+40], fill=(139, 69, 19))
            draw.polygon([x+37, y+25, x+47, y+15, x+58, y+25], fill=(255, 0, 0))

    def draw_simple_menu(self, title, options, selected):
        """Simplified menu with larger fonts"""
        image = Image.new('RGB', (240, 240), (0, 0, 0))
        draw = ImageDraw.Draw(image)
        
        # Title
        draw.text((10, 10), title, font=self.font_large, fill=(0, 255, 0))
        
        # Options with larger spacing
        y = 50
        for i, option in enumerate(options):
            color = (255, 255, 0) if i == selected else (255, 255, 255)
            prefix = ">" if i == selected else " "
            draw.text((15, y), f"{prefix} {option}", font=self.font_medium, fill=color)
            y += 25
            
        # Instructions
        draw.text((10, 210), "A=OK  B=Back", font=self.font_small, fill=(255, 255, 0))
        self.display.image(image)

    def draw_story_screen(self, title, text_lines, graphic_type=None, event_type=None):
        """Story screen with graphics and larger text"""
        image = Image.new('RGB', (240, 240), (0, 0, 0))
        draw = ImageDraw.Draw(image)
        
        # Title
        draw.text((10, 5), title, font=self.font_large, fill=(0, 255, 0))
        
        # Graphics
        if graphic_type == "wagon_scene":
            self.draw_oxen(draw, 20, 30)
            self.draw_large_wagon(draw, 50, 25)
        elif event_type:
            self.draw_event_graphic(draw, event_type, 120, 30)
        elif graphic_type:
            if graphic_type == "grave":
                self.draw_grave(draw, 100, 30)
            else:
                self.draw_landmark_graphic(draw, graphic_type, 90, 30)
            
        # Text with proper spacing
        y = 90 if (graphic_type or event_type) else 40
        for line in text_lines:
            if y > 190:
                break
            draw.text((10, y), line, font=self.font_small, fill=(255, 255, 255))
            y += 18
            
        draw.text((10, 215), "Press A to continue", font=self.font_small, fill=(255, 255, 0))
        self.display.image(image)
        
        # Wait for input
        while True:
            controls = self.read_controls_debounced()
            if controls.get('button_a') or controls.get('button_b'):
                break
            time.sleep(0.1)

    def shopping_screen(self):
        """Simplified shopping with visual feedback"""
        items = [
            {"name": "Oxen", "price": 40, "have": self.oxen},
            {"name": "Food", "price": 20, "have": self.food},
            {"name": "Bullets", "price": 10, "have": self.bullets},
            {"name": "Clothes", "price": 15, "have": self.clothes}
        ]
        selected = 0
        
        while True:
            image = Image.new('RGB', (240, 240), (0, 0, 0))
            draw = ImageDraw.Draw(image)
            
            draw.text((10, 5), "General Store", font=self.font_large, fill=(0, 255, 0))
            draw.text((10, 30), f"Money: ${self.money}", font=self.font_medium, fill=(255, 255, 0))
            
            y = 60
            for i, item in enumerate(items):
                color = (255, 255, 0) if i == selected else (255, 255, 255)
                prefix = ">" if i == selected else " "
                draw.text((15, y), f"{prefix} {item['name']}: ${item['price']}", 
                         font=self.font_medium, fill=color)
                draw.text((15, y+15), f"   Have: {item['have']}", 
                         font=self.font_small, fill=(200, 200, 200))
                y += 35
                
            draw.text((10, 195), "A=Buy  B=Done", font=self.font_small, fill=(255, 255, 0))
            draw.text((10, 210), "Need: 2 Oxen, 100 Food", font=self.font_small, fill=(255, 100, 100))
            
            self.display.image(image)
            
            controls = self.read_controls_debounced()
            if controls.get('up'):
                selected = max(0, selected - 1)
            elif controls.get('down'):
                selected = min(len(items) - 1, selected + 1)
            elif controls.get('button_a'):
                item = items[selected]
                if self.money >= item["price"]:
                    self.money -= item["price"]
                    if selected == 0:
                        self.oxen += 1
                        items[0]["have"] = self.oxen
                    elif selected == 1:
                        self.food += 50
                        items[1]["have"] = self.food
                    elif selected == 2:
                        self.bullets += 20
                        items[2]["have"] = self.bullets
                    elif selected == 3:
                        self.clothes += 1
                        items[3]["have"] = self.clothes
            elif controls.get('button_b'):
                if self.oxen >= 2 and self.food >= 100:
                    break
                else:
                    self.draw_story_screen("Not Ready!", [
                        "You need at least:",
                        "2 Oxen and 100 Food",
                        "to start your journey"
                    ])
                    
            time.sleep(0.1)

    def travel_screen(self):
        """Main travel interface with large, clear display"""
        image = Image.new('RGB', (240, 240), (0, 0, 0))
        draw = ImageDraw.Draw(image)
        
        # Date
        months = ["", "", "", "March", "April", "May", "June", "July", "August", "Sept"]
        draw.text((10, 5), f"{months[self.month]} {self.day}", font=self.font_medium, fill=(255, 255, 255))
        
        # Progress bar
        progress = min(100, int((self.miles / 2040) * 100))
        draw.rectangle([10, 25, 230, 35], outline=(255, 255, 255))
        draw.rectangle([10, 25, 10 + int(220 * progress / 100), 35], fill=(0, 255, 0))
        draw.text((10, 40), f"{self.miles}/2040 miles ({progress}%)", font=self.font_small, fill=(255, 255, 255))
        
        # Current location
        current_location = self.landmarks[self.current_landmark]["name"]
        draw.text((10, 60), f"At: {current_location}", font=self.font_medium, fill=(0, 255, 0))
        
        # Supplies - larger, clearer display
        draw.text((10, 85), f"Food: {self.food} lbs", font=self.font_medium, fill=(255, 255, 255))
        draw.text((10, 105), f"Bullets: {self.bullets}", font=self.font_medium, fill=(255, 255, 255))
        draw.text((10, 125), f"Oxen: {self.oxen}", font=self.font_medium, fill=(255, 255, 255))
        
        # Party health
        draw.text((10, 150), "Party:", font=self.font_medium, fill=(255, 255, 0))
        y = 170
        for member in self.party:
            color = (255, 0, 0) if member["sick"] else (0, 255, 0)
            status = "Sick" if member["sick"] else "Good"
            draw.text((15, y), f"{member['name']}: {status}", font=self.font_small, fill=color)
            y += 15
            
        draw.text((10, 215), "A=Continue  B=Menu", font=self.font_small, fill=(255, 255, 0))
        self.display.image(image)

    def trail_menu(self):
        """Simplified trail menu"""
        options = ["Continue", "Rest", "Hunt"]
        selected = 0
        
        while True:
            self.draw_simple_menu("What now?", options, selected)
            
            controls = self.read_controls_debounced()
            if controls.get('up'):
                selected = max(0, selected - 1)
            elif controls.get('down'):
                selected = min(len(options) - 1, selected + 1)
            elif controls.get('button_a'):
                return selected
            elif controls.get('button_b'):
                return -1
                
            time.sleep(0.1)

    def hunt_game(self):
        """Simplified hunting game with deer graphic"""
        if self.bullets < 5:
            return "Not enough bullets!", "hunting"
            
        self.draw_story_screen("Hunting", [
            "You see a deer!",
            "",
            "Press A quickly when",
            "you see 'SHOOT!'"
        ], event_type="hunting")
        
        # Wait random time
        time.sleep(random.uniform(1, 3))
        
        # Show shoot prompt
        image = Image.new('RGB', (240, 240), (0, 0, 0))
        draw = ImageDraw.Draw(image)
        draw.text((70, 100), "SHOOT!", font=self.font_large, fill=(255, 0, 0))
        self.display.image(image)
        
        # Check reaction time
        start_time = time.time()
        hit = False
        
        while time.time() - start_time < 1.5:
            controls = self.read_controls_debounced()
            if controls.get('button_a'):
                reaction_time = time.time() - start_time
                hit = reaction_time < 1.0
                break
            time.sleep(0.05)
            
        self.bullets -= 5
        
        if hit:
            meat = random.randint(20, 60)
            self.food += meat
            return f"Good shot! Got {meat} lbs meat", "good_weather"
        else:
            return "Missed! No food gained", "hunting"

    def travel_day(self):
        """Simplified daily travel with event graphics"""
        # Travel 15-25 miles per day
        daily_miles = random.randint(15, 25)
        if self.oxen < 2:
            daily_miles = int(daily_miles * 0.7)  # Slower with fewer oxen
            
        self.miles += daily_miles
        
        # Consume food
        self.food = max(0, self.food - 3)  # 3 people eat 1 lb each
        
        # Advance date
        self.day += 1
        if self.day > 30:
            self.day = 1
            self.month += 1
            
        # Random events with graphics
        if random.random() < 0.25:
            events = [
                {"text": "Wagon wheel broke!", "type": "wagon_break", "effect": "time"},
                {"text": "Ox injured and limping", "type": "ox_injury", "effect": "ox"},
                {"text": "Severe thunderstorm", "type": "storm", "effect": "time"},
                {"text": "Thief stole supplies", "type": "theft", "effect": "food"},
                {"text": "Snake bite - someone sick", "type": "snake_bite", "effect": "sick"},
                {"text": "Beautiful weather", "type": "good_weather", "effect": "bonus"},
                {"text": "River crossing ahead", "type": "river_crossing", "effect": "choice"}
            ]
            
            event = random.choice(events)
            
            # Apply event effects
            if event["effect"] == "food":
                self.food = max(0, self.food - random.randint(10, 20))
            elif event["effect"] == "ox" and self.oxen > 1:
                self.oxen -= 1
            elif event["effect"] == "sick":
                healthy = [p for p in self.party if not p["sick"]]
                if healthy:
                    random.choice(healthy)["sick"] = True
            elif event["effect"] == "bonus":
                self.miles += 10
            elif event["effect"] == "time":
                self.miles = max(0, self.miles - 10)
                
            return event["text"], event["type"]
            
        return None, None

    def check_landmarks(self):
        """Check for landmark arrivals with graphics"""
        for i, landmark in enumerate(self.landmarks):
            if (self.miles >= landmark["miles"] and 
                i > self.current_landmark and 
                landmark["miles"] > 0):
                self.current_landmark = i
                
                self.draw_story_screen(
                    "Landmark!",
                    [f"You have reached", landmark["name"], "", 
                     f"Miles traveled: {self.miles}"],
                    graphic_type=landmark["name"]
                )
                return True
        return False

    def run(self):
        game_state = "intro"
        
        while True:
            controls = self.read_controls_debounced()
            
            if controls.get('button_b') and game_state == "intro":
                return
                
            if game_state == "intro":
                self.draw_story_screen("The Oregon Trail", [
                    "It is 1847. You are a pioneer",
                    "heading west to Oregon City.",
                    "",
                    "You must travel 2040 miles",
                    "across dangerous frontier.",
                    "",
                    "Buy supplies, manage resources,",
                    "and survive the journey!",
                    "",
                    "Good luck, pioneer!"
                ], graphic_type="wagon_scene")
                
                game_state = "shopping"
                
            elif game_state == "shopping":
                self.shopping_screen()
                game_state = "traveling"
                
            elif game_state == "traveling":
                # Check win condition
                if self.miles >= 2040:
                    game_state = "win"
                    continue
                    
                # Check lose conditions
                alive_count = sum(1 for p in self.party if not p["sick"])
                if alive_count == 0:
                    game_state = "lose_sickness"
                    continue
                elif self.food <= 0:
                    game_state = "lose_starvation"
                    continue
                    
                # Show travel screen
                self.travel_screen()
                
                # Wait for input
                while True:
                    controls = self.read_controls_debounced()
                    if controls.get('button_a'):
                        break
                    elif controls.get('button_b'):
                        choice = self.trail_menu()
                        if choice == 0:  # Continue
                            break
                        elif choice == 1:  # Rest
                            self.draw_story_screen("Resting", ["Your party rests for 2 days"], 
                                                 event_type="good_weather")
                            self.day += 2
                            # Chance to recover
                            for member in self.party:
                                if member["sick"] and random.random() < 0.5:
                                    member["sick"] = False
                        elif choice == 2:  # Hunt
                            result, graphic = self.hunt_game()
                            self.draw_story_screen("Hunt Result", [result], event_type=graphic)
                        elif choice == -1:  # Back
                            break
                    time.sleep(0.1)
                
                # Travel for the day
                event, event_type = self.travel_day()
                if event:
                    self.draw_story_screen("Trail Event", [event], event_type=event_type)
                    
                # Check landmarks
                self.check_landmarks()
                
            elif game_state == "win":
                alive_count = sum(1 for p in self.party if not p["sick"])
                score = alive_count * 200 + self.food + self.bullets
                
                self.draw_story_screen("Success!", [
                    "You made it to Oregon City!",
                    "",
                    f"Final score: {score}",
                    f"Food left: {self.food} lbs",
                    f"Bullets left: {self.bullets}",
                    "",
                    "A=Play Again  B=Menu"
                ], graphic_type="wagon_scene")
                
                while True:
                    controls = self.read_controls_debounced()
                    if controls.get('button_a'):
                        self.reset_game()
                        game_state = "shopping"
                        break
                    elif controls.get('button_b'):
                        return
                    time.sleep(0.1)
                    
            elif game_state.startswith("lose"):
                if game_state == "lose_sickness":
                    message = [
                        "Your entire party has died",
                        "of dysentery and disease.",
                        "",
                        f"You traveled {self.miles} miles",
                        "out of 2040 total.",
                        "",
                        "A=Try Again  B=Menu"
                    ]
                else:  # lose_starvation
                    message = [
                        "Your party has starved",
                        "on the Oregon Trail.",
                        "",
                        f"You traveled {self.miles} miles",
                        "out of 2040 total.",
                        "",
                        "A=Try Again  B=Menu"
                    ]
                
                self.draw_story_screen("You Have Died", message, graphic_type="grave")
                
                while True:
                    controls = self.read_controls_debounced()
                    if controls.get('button_a'):
                        self.reset_game()
                        game_state = "shopping"
                        break
                    elif controls.get('button_b'):
                        return
                    time.sleep(0.1)
                    
            time.sleep(0.1)

Now to save this do “Control x”, then “y”, then “enter” to save. There should be instructions for this at the bottom of the window. Now you should be back at the command prompt and we need to modify our file to tell the system it is executable (that it is a program to run) so enter:

chmod +x /home/herman/games/oregon_trail.py

Then to restart your system and load the new game:

sudo reboot

Now you should be all set, it is pretty straightforward, on reboot the device will disconnect from the network so you will lose your connection. Once rebooted the new game should be in your list and ready to play, just try not to die of dysentery.

**A final note on broken games.

If your game is broken, does not load, or gives an error just provide this information to your AI (if that is what you’re using to code) and it will give you new code to try. We have found it easiest to simply remove the original file and then start over. To do that you would use the following code to remove the file, and then just start from the file creation step above (nano…) to recreate it with the new code.

rm /home/herman/games/oregon_trail.py