Spaces:
Sleeping
Sleeping
| import random | |
| import json | |
| import os | |
| import gradio as gr | |
| class SavingPrivateRyanGame: | |
| def __init__(self): | |
| self.current_mission = 1 | |
| self.total_missions = 4 | |
| self.mission_names = [ | |
| "Omaha Beach Landing", | |
| "Neuville Village: The Sniper", | |
| "The Radar Station: The Field", | |
| "Ramelle: The Bridge" | |
| ] | |
| self.player_name = "Captain Miller" | |
| self.inventory = ["Thompson SMG", "Colt 1911", "Sticky Bombs"] | |
| # Real Movie Squad | |
| self.squad = { | |
| "Sergeant Horvath": {"status": "Alive", "death_trigger": 550, "mission": 4}, | |
| "Private Reiben": {"status": "Alive", "death_trigger": 999, "mission": 4}, # Survivor | |
| "Private Jackson": {"status": "Alive", "death_trigger": 450, "mission": 4}, | |
| "Private Mellish": {"status": "Alive", "death_trigger": 500, "mission": 4}, | |
| "Private Caparzo": {"status": "Alive", "death_trigger": 200, "mission": 2}, | |
| "Medic Wade": {"status": "Alive", "death_trigger": 250, "mission": 3}, | |
| "Corporal Upham": {"status": "Alive", "death_trigger": 999, "mission": 4} # Survivor | |
| } | |
| self.miller_alive = True | |
| self.ryan_found = False | |
| self.game_over = False | |
| self.output_text = "" | |
| self.reset_mission_stats() | |
| def get_chatter(self): | |
| living = [n for n, i in self.squad.items() if i["status"] == "Alive"] | |
| if not living: return "Miller: 'Move up!'" | |
| speaker = random.choice(living) | |
| quotes = [ | |
| "'FUBAR, Captain. Absolute FUBAR.'", | |
| "'Why are we risking eight guys for one?'", | |
| "'I've got a bad feeling about that corner.'", | |
| "'Just tell us where we're going, Cap.'", | |
| "'Keep your head down, Upham!'" | |
| ] | |
| return f"{speaker}: {random.choice(quotes)}" | |
| def reset_mission_stats(self): | |
| self.distance = 0 | |
| self.bunkers_destroyed = 0 | |
| self.tiger_tank_approaching = False | |
| configs = { | |
| 1: {"map": "BEACH", "obj": 300}, | |
| 2: {"map": "VILLAGE", "obj": 400}, | |
| 3: {"map": "FOREST", "obj": 500}, | |
| 4: {"map": "CITY (RAMELLE)", "obj": 600}, | |
| } | |
| cfg = configs.get(self.current_mission) | |
| self.map_type = cfg["map"] | |
| self.mission_objective = cfg["obj"] | |
| def process_plot_points(self): | |
| """Triggers scripted deaths and events exactly as they happen in the movie.""" | |
| event_log = "" | |
| # Mission 2: Caparzo | |
| if self.current_mission == 2 and self.distance >= 200 and self.squad["Private Caparzo"]["status"] == "Alive": | |
| self.squad["Private Caparzo"]["status"] = "Dead" | |
| event_log = "β οΈ PLOT EVENT: Rain starts falling. Caparzo tries to help a childβA SNIPER SHOT! Caparzo is down. Jackson counter-snipes the German in the tower." | |
| # Mission 3: Wade | |
| elif self.current_mission == 3 and self.distance >= 250 and self.squad["Medic Wade"]["status"] == "Alive": | |
| self.squad["Medic Wade"]["status"] = "Dead" | |
| event_log = "β οΈ PLOT EVENT: Assaulting the MG42 nest. 'MEDIC!' Wade is hit in the liver. The squad tries to save him, but he's gone. Miller releases the German prisoner (Steamship Willie)." | |
| # Mission 4: The Final Stand | |
| elif self.current_mission == 4: | |
| if not self.ryan_found: | |
| self.ryan_found = True | |
| event_log = "β¨ PLOT EVENT: You found Private James Ryan. He refuses to leave his post. The squad stays to defend the bridge." | |
| # Scripted deaths during the bridge defense | |
| if self.distance >= 450 and self.squad["Private Jackson"]["status"] == "Alive": | |
| self.squad["Private Jackson"]["status"] = "Dead" | |
| event_log = "β οΈ Jackson is in the bell tower... A Tiger tank levels the tower. Jackson has fallen." | |
| if self.distance >= 500 and self.squad["Private Mellish"]["status"] == "Alive": | |
| self.squad["Private Mellish"]["status"] = "Dead" | |
| event_log = "β οΈ Hand-to-hand combat in the ruins. Mellish is overwhelmed. Upham is frozen in the stairwell..." | |
| if self.distance >= 550 and self.squad["Sergeant Horvath"]["status"] == "Alive": | |
| self.squad["Sergeant Horvath"]["status"] = "Dead" | |
| event_log = "β οΈ Horvath is hit multiple times. He fires one last round at the tank. 'I moved the rock...'" | |
| return event_log | |
| def miller_sacrifice(self): | |
| """The final scene on the bridge.""" | |
| self.miller_alive = False | |
| self.game_over = True | |
| sacrifice_text = """ | |
| ββββββββββββββββββββββββββββββ | |
| π₯ THE FINAL SACRIFICE π₯ | |
| ββββββββββββββββββββββββββββββ | |
| Miller is wounded on the bridge, leaning against a pillar. | |
| A Tiger tank rolls toward him. He pulls his Colt 1911 and fires uselessly at the steel. | |
| SuddenlyβBOOM! A P-51 Mustang 'Tank Buster' screams overhead and vaporizes the Tiger. | |
| Reiben finds you. You pull James Ryan close. | |
| Miller: 'James... earn this. Earn it.' | |
| Captain John H. Miller has died. | |
| ββββββββββββββββββββββββββββββ | |
| """ | |
| return sacrifice_text | |
| def make_move(self, action): | |
| self.output_text = "" | |
| if self.game_over: return self.output_text, "Campaign Finished", gr.update(visible=False) | |
| # Movement | |
| adv = random.randint(40, 80) if action == "rush" else random.randint(20, 40) | |
| self.distance += adv | |
| # Check Plot | |
| plot_event = self.process_plot_points() | |
| if plot_event: self.print_to_output(f"\n{plot_event}\n") | |
| self.print_to_output(f"π’ {self.get_chatter()}") | |
| # The End Trigger | |
| if self.current_mission == 4 and self.distance >= 590: | |
| self.print_to_output(self.miller_sacrifice()) | |
| return self.output_text, "Earn This.", gr.update(visible=False) | |
| # Standard Check | |
| show_action = False | |
| if self.distance >= (self.bunkers_destroyed + 1) * (self.mission_objective / 3): | |
| show_action = True | |
| self.print_to_output("\nβ οΈ CONTACT: Enemy forces blocking progress!") | |
| if action == "assault": | |
| self.bunkers_destroyed += 1 | |
| self.print_to_output("π₯ Clear! Advancing...") | |
| self.check_transition() | |
| self.print_status() | |
| return self.output_text, "Awaiting Command", gr.update(visible=show_action) | |
| def print_status(self): | |
| living = [n for n, i in self.squad.items() if i["status"] == "Alive"] | |
| self.print_to_output(f"\nπ LOCATION: {self.map_type} | DISTANCE: {self.distance}/{self.mission_objective}m") | |
| self.print_to_output(f"π₯ SQUAD: Miller, {', '.join(living)}") | |
| def check_transition(self): | |
| if self.distance >= self.mission_objective and self.current_mission < 4: | |
| self.current_mission += 1 | |
| self.reset_mission_stats() | |
| self.print_to_output(f"\nβ MISSION COMPLETE. Transitioning to {self.mission_names[self.current_mission-1]}...") | |
| def print_to_output(self, text): | |
| self.output_text += text + "\n" | |
| def start_game(self, _): | |
| self.output_text = "June 6, 1944. Dog One Sector. Omaha Beach.\n'CLEAR THE RAMP!'" | |
| self.current_mission = 1 | |
| self.reset_mission_stats() | |
| self.print_status() | |
| return self.output_text, "Rangers Lead The Way", gr.update(visible=False) | |
| def create_ui(): | |
| game = SavingPrivateRyanGame() | |
| with gr.Blocks(theme=gr.themes.Monochrome()) as demo: | |
| gr.Markdown("# ποΈ SAVING PRIVATE RYAN: THE COMPLETE JOURNEY") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| start_btn = gr.Button("π Start Campaign", variant="primary") | |
| rush_btn = gr.Button("β‘ Advance") | |
| with gr.Column(visible=False) as action_row: | |
| gr.Markdown("### β οΈ ENGAGEMENT") | |
| assault_btn = gr.Button("π₯ Tactical Assault", variant="stop") | |
| with gr.Column(scale=2): | |
| output = gr.TextArea(label="Radio Log", lines=25) | |
| status = gr.Textbox(label="Mission Status") | |
| controls = [output, status, action_row] | |
| start_btn.click(game.start_game, None, controls) | |
| rush_btn.click(lambda: game.make_move("rush"), None, controls) | |
| assault_btn.click(lambda: game.make_move("assault"), None, controls) | |
| return demo | |
| if __name__ == "__main__": | |
| create_ui().launch() |