Spaces:
Sleeping
Sleeping
| import random | |
| import time | |
| import gradio as gr | |
| class SavingPrivateRyanDefinitive: | |
| def __init__(self): | |
| # Game State | |
| self.mode = "Stable" | |
| self.current_mission = 1 | |
| self.total_missions = 5 | |
| self.distance = 0 | |
| self.game_over = False | |
| self.ryan_found = False | |
| self.miller_wounded = False | |
| # Plot-Specific Locations | |
| self.missions = { | |
| 1: {"name": "Omaha Beach", "goal": 300, "desc": "Dog One Sector. The Atlantic Wall."}, | |
| 2: {"name": "Neuville Village", "goal": 400, "desc": "Searching for Ryan. Sniper territory."}, | |
| 3: {"name": "Radar Station", "goal": 500, "desc": "The hills of Normandy. German MG nest ahead."}, | |
| 4: {"name": "Battle of Ramelle", "goal": 600, "desc": "Hold the bridge at all costs."}, | |
| 5: {"name": "Normandy Cemetery", "goal": 100, "desc": "Present Day. Colleville-sur-Mer."} | |
| } | |
| # The Actual 2nd Ranger Squad | |
| # Format: status, death_mission, death_distance, final_words | |
| self.squad = { | |
| "Capt. Miller": {"status": "Alive", "death": (4, 590), "words": "James... earn this. Earn it."}, | |
| "Sgt. Horvath": {"status": "Alive", "death": (4, 550), "words": "I moved the rock..."}, | |
| "Pvt. Reiben": {"status": "Alive", "death": (9, 999), "words": "I'm going home."}, | |
| "Pvt. Jackson": {"status": "Alive", "death": (4, 450), "words": "Be not far from me, for trouble is near."}, | |
| "Pvt. Mellish": {"status": "Alive", "death": (4, 500), "words": "Give 'em hell!"}, | |
| "Pvt. Caparzo": {"status": "Alive", "death": (2, 200), "words": "It's for the kid's father."}, | |
| "Medic Wade": {"status": "Alive", "death": (3, 250), "words": "I want to go home... Mama..."}, | |
| "Cpl. Upham": {"status": "Alive", "death": (9, 999), "words": "FUBAR."} | |
| } | |
| def get_beta_visuals(self): | |
| """Generates the Graphical UI elements for Beta Mode""" | |
| if self.mode == "Stable": return "" | |
| progress = int((self.distance / self.missions[self.current_mission]["goal"]) * 15) | |
| bar = "β" * progress + "β" * (15 - progress) | |
| status_icons = "".join([f"[{n[5:8]}:{'π' if s['status']=='Alive' else 'π'}]" for n, s in self.squad.items()]) | |
| visual = f"\n{'='*50}\n" | |
| visual += f"π SECTOR: {self.missions[self.current_mission]['name'].upper()}\n" | |
| visual += f"πΊοΈ MAP PROGRESS: |{bar}| {self.distance}m\n" | |
| visual += f"π₯ SQUAD VITALS: {status_icons}\n" | |
| visual += f"{'='*50}\n" | |
| return visual | |
| def get_chatter(self): | |
| living = [n for n, s in self.squad.items() if s["status"] == "Alive" and n != "Capt. Miller"] | |
| if not living: return "Miller: 'Keep moving!'" | |
| speaker = random.choice(living) | |
| quotes = [ | |
| "What's the pool up to? I've got 300 on the Captain.", | |
| "FUBAR. Everything is FUBAR.", | |
| "You think Ryan's worth all this?", | |
| "Keep five yards! Don't bunch up!", | |
| "I haven't had a decent meal since Portsmouth." | |
| ] | |
| return f"{speaker}: '{random.choice(quotes)}'" | |
| def handle_plot(self): | |
| plot_log = "" | |
| # Check for scripted deaths | |
| for name, info in self.squad.items(): | |
| m_death, d_death = info["death"] | |
| if self.current_mission == m_death and self.distance >= d_death and info["status"] == "Alive": | |
| info["status"] = "Dead" | |
| plot_log += f"\nπ¨ CINEMATIC EVENT: {name} has been hit! {info['words']}\n" | |
| if name == "Capt. Miller": | |
| self.miller_wounded = True | |
| # Plot Beats | |
| if self.current_mission == 3 and self.distance > 300: | |
| plot_log += "\nπ STORY: Miller stops the squad's infighting. 'I'm a schoolteacher from Pennsylvania.' The squad goes silent.\n" | |
| if self.current_mission == 4 and self.distance > 100 and not self.ryan_found: | |
| self.ryan_found = True | |
| plot_log += "\n⨠STORY: Private James Francis Ryan found. He won't leave his post. Miller: 'Rally on the bridge!'\n" | |
| return plot_log | |
| def play(self, action, mode): | |
| self.mode = mode | |
| if self.game_over: return "The mission is over. James Ryan is home.", "End of File" | |
| # Movement and Narrative | |
| advance = random.randint(45, 85) | |
| self.distance += advance | |
| # Build Output | |
| output = self.get_beta_visuals() | |
| output += f"\nπ’ {self.get_chatter()}\n" | |
| output += self.handle_plot() | |
| # Check Mission Completion | |
| if self.distance >= self.missions[self.current_mission]["goal"]: | |
| if self.current_mission < self.total_missions: | |
| self.current_mission += 1 | |
| self.distance = 0 | |
| output += f"\nβ MISSION COMPLETE. Transitioning to {self.missions[self.current_mission]['name']}...\n" | |
| else: | |
| self.game_over = True | |
| output += "\nποΈ FINAL SCENE: 'Tell me I'm a good man.' Ryan salutes the grave. Fade to black.\n" | |
| # Special Death Logic for Miller | |
| if self.miller_wounded: | |
| output += "\n㪠[BETA]: P-51 Mustangs scream overhead. The Tiger tank is destroyed. Miller looks at Ryan... 'Earn this.'\n" | |
| self.current_mission = 5 # Skip to Cemetery | |
| self.distance = 0 | |
| self.miller_wounded = False | |
| status_str = f"Mission {self.current_mission}: {self.missions[self.current_mission]['name']}" | |
| return output, status_str | |
| # UI Assembly | |
| def run_app(): | |
| game_engine = SavingPrivateRyanDefinitive() | |
| with gr.Blocks(theme=gr.themes.Monochrome(), title="Saving Private Ryan") as demo: | |
| gr.Markdown("# ποΈ SAVING PRIVATE RYAN: THE DEFINITIVE CAMPAIGN") | |
| gr.Markdown("Experience the full journey from Omaha Beach to the final sacrifice.") | |
| with gr.Row(): | |
| mode_select = gr.Radio(["Stable", "Beta"], label="Interface", value="Beta") | |
| action_btn = gr.Button("ADVANCE SQUAD", variant="primary") | |
| with gr.Row(): | |
| text_output = gr.TextArea(label="Battlefield Journal", lines=22) | |
| meta_status = gr.Textbox(label="Current Objective") | |
| action_btn.click( | |
| game_engine.play, | |
| inputs=[gr.State("move"), mode_select], | |
| outputs=[text_output, meta_status] | |
| ) | |
| demo.launch() | |
| if __name__ == "__main__": | |
| run_app() |