Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,148 +1,204 @@
|
|
|
|
|
|
|
|
| 1 |
import random
|
| 2 |
-
import
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
|
| 5 |
-
class
|
| 6 |
def __init__(self):
|
| 7 |
-
# Game State
|
| 8 |
-
self.mode = "Stable"
|
| 9 |
self.current_mission = 1
|
| 10 |
-
self.total_missions =
|
| 11 |
-
self.
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
self.
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
3: {"name": "Radar Station", "goal": 500, "desc": "The hills of Normandy. German MG nest ahead."},
|
| 21 |
-
4: {"name": "Battle of Ramelle", "goal": 600, "desc": "Hold the bridge at all costs."},
|
| 22 |
-
5: {"name": "Normandy Cemetery", "goal": 100, "desc": "Present Day. Colleville-sur-Mer."}
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
# The Actual 2nd Ranger Squad
|
| 26 |
-
# Format: status, death_mission, death_distance, final_words
|
| 27 |
self.squad = {
|
| 28 |
-
"
|
| 29 |
-
"
|
| 30 |
-
"
|
| 31 |
-
"
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"
|
| 35 |
-
"Cpl. Upham": {"status": "Alive", "death": (9, 999), "words": "FUBAR."}
|
| 36 |
}
|
| 37 |
-
|
| 38 |
-
def get_beta_visuals(self):
|
| 39 |
-
"""Generates the Graphical UI elements for Beta Mode"""
|
| 40 |
-
if self.mode == "Stable": return ""
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
visual = f"\n{'='*50}\n"
|
| 48 |
-
visual += f"π SECTOR: {self.missions[self.current_mission]['name'].upper()}\n"
|
| 49 |
-
visual += f"πΊοΈ MAP PROGRESS: |{bar}| {self.distance}m\n"
|
| 50 |
-
visual += f"π₯ SQUAD VITALS: {status_icons}\n"
|
| 51 |
-
visual += f"{'='*50}\n"
|
| 52 |
-
return visual
|
| 53 |
|
| 54 |
def get_chatter(self):
|
| 55 |
-
living = [n for n,
|
| 56 |
-
if not living: return "Miller: '
|
| 57 |
-
|
| 58 |
speaker = random.choice(living)
|
| 59 |
quotes = [
|
| 60 |
-
"
|
| 61 |
-
"
|
| 62 |
-
"
|
| 63 |
-
"
|
| 64 |
-
"
|
| 65 |
]
|
| 66 |
-
return f"{speaker}:
|
| 67 |
-
|
| 68 |
-
def
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
m_death, d_death = info["death"]
|
| 73 |
-
if self.current_mission == m_death and self.distance >= d_death and info["status"] == "Alive":
|
| 74 |
-
info["status"] = "Dead"
|
| 75 |
-
plot_log += f"\nπ¨ CINEMATIC EVENT: {name} has been hit! {info['words']}\n"
|
| 76 |
-
|
| 77 |
-
if name == "Capt. Miller":
|
| 78 |
-
self.miller_wounded = True
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
-
|
| 91 |
-
self.mode = mode
|
| 92 |
-
if self.game_over: return "The mission is over. James Ryan is home.", "End of File"
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
-
#
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
output += self.handle_plot()
|
| 102 |
-
|
| 103 |
-
# Check Mission Completion
|
| 104 |
-
if self.distance >= self.missions[self.current_mission]["goal"]:
|
| 105 |
-
if self.current_mission < self.total_missions:
|
| 106 |
-
self.current_mission += 1
|
| 107 |
-
self.distance = 0
|
| 108 |
-
output += f"\nβ
MISSION COMPLETE. Transitioning to {self.missions[self.current_mission]['name']}...\n"
|
| 109 |
-
else:
|
| 110 |
-
self.game_over = True
|
| 111 |
-
output += "\nποΈ FINAL SCENE: 'Tell me I'm a good man.' Ryan salutes the grave. Fade to black.\n"
|
| 112 |
-
|
| 113 |
-
# Special Death Logic for Miller
|
| 114 |
-
if self.miller_wounded:
|
| 115 |
-
output += "\n㪠[BETA]: P-51 Mustangs scream overhead. The Tiger tank is destroyed. Miller looks at Ryan... 'Earn this.'\n"
|
| 116 |
-
self.current_mission = 5 # Skip to Cemetery
|
| 117 |
-
self.distance = 0
|
| 118 |
-
self.miller_wounded = False
|
| 119 |
-
|
| 120 |
-
status_str = f"Mission {self.current_mission}: {self.missions[self.current_mission]['name']}"
|
| 121 |
-
return output, status_str
|
| 122 |
-
|
| 123 |
-
# UI Assembly
|
| 124 |
-
def run_app():
|
| 125 |
-
game_engine = SavingPrivateRyanDefinitive()
|
| 126 |
-
|
| 127 |
-
with gr.Blocks(theme=gr.themes.Monochrome(), title="Saving Private Ryan") as demo:
|
| 128 |
-
gr.Markdown("# ποΈ SAVING PRIVATE RYAN: THE DEFINITIVE CAMPAIGN")
|
| 129 |
-
gr.Markdown("Experience the full journey from Omaha Beach to the final sacrifice.")
|
| 130 |
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
| 135 |
with gr.Row():
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
)
|
| 144 |
|
| 145 |
-
demo
|
| 146 |
|
| 147 |
if __name__ == "__main__":
|
| 148 |
-
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
import random
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
+
class SavingPrivateRyanGame:
|
| 9 |
def __init__(self):
|
|
|
|
|
|
|
| 10 |
self.current_mission = 1
|
| 11 |
+
self.total_missions = 4
|
| 12 |
+
self.mission_names = [
|
| 13 |
+
"Omaha Beach Landing",
|
| 14 |
+
"Neuville Village: The Sniper",
|
| 15 |
+
"The Radar Station: The Field",
|
| 16 |
+
"Ramelle: The Bridge"
|
| 17 |
+
]
|
| 18 |
|
| 19 |
+
self.player_name = "Captain Miller"
|
| 20 |
+
self.inventory = ["Thompson SMG", "Colt 1911", "Sticky Bombs"]
|
| 21 |
+
|
| 22 |
+
# Real Movie Squad
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
self.squad = {
|
| 24 |
+
"Sergeant Horvath": {"status": "Alive", "death_trigger": 550, "mission": 4},
|
| 25 |
+
"Private Reiben": {"status": "Alive", "death_trigger": 999, "mission": 4}, # Survivor
|
| 26 |
+
"Private Jackson": {"status": "Alive", "death_trigger": 450, "mission": 4},
|
| 27 |
+
"Private Mellish": {"status": "Alive", "death_trigger": 500, "mission": 4},
|
| 28 |
+
"Private Caparzo": {"status": "Alive", "death_trigger": 200, "mission": 2},
|
| 29 |
+
"Medic Wade": {"status": "Alive", "death_trigger": 250, "mission": 3},
|
| 30 |
+
"Corporal Upham": {"status": "Alive", "death_trigger": 999, "mission": 4} # Survivor
|
|
|
|
| 31 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
self.miller_alive = True
|
| 34 |
+
self.ryan_found = False
|
| 35 |
+
self.game_over = False
|
| 36 |
+
self.output_text = ""
|
| 37 |
+
self.reset_mission_stats()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
def get_chatter(self):
|
| 40 |
+
living = [n for n, i in self.squad.items() if i["status"] == "Alive"]
|
| 41 |
+
if not living: return "Miller: 'Move up!'"
|
|
|
|
| 42 |
speaker = random.choice(living)
|
| 43 |
quotes = [
|
| 44 |
+
"'FUBAR, Captain. Absolute FUBAR.'",
|
| 45 |
+
"'Why are we risking eight guys for one?'",
|
| 46 |
+
"'I've got a bad feeling about that corner.'",
|
| 47 |
+
"'Just tell us where we're going, Cap.'",
|
| 48 |
+
"'Keep your head down, Upham!'"
|
| 49 |
]
|
| 50 |
+
return f"{speaker}: {random.choice(quotes)}"
|
| 51 |
+
|
| 52 |
+
def reset_mission_stats(self):
|
| 53 |
+
self.distance = 0
|
| 54 |
+
self.bunkers_destroyed = 0
|
| 55 |
+
self.tiger_tank_approaching = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
+
configs = {
|
| 58 |
+
1: {"map": "BEACH", "obj": 300},
|
| 59 |
+
2: {"map": "VILLAGE", "obj": 400},
|
| 60 |
+
3: {"map": "FOREST", "obj": 500},
|
| 61 |
+
4: {"map": "CITY (RAMELLE)", "obj": 600},
|
| 62 |
+
}
|
| 63 |
+
cfg = configs.get(self.current_mission)
|
| 64 |
+
self.map_type = cfg["map"]
|
| 65 |
+
self.mission_objective = cfg["obj"]
|
| 66 |
+
|
| 67 |
+
def process_plot_points(self):
|
| 68 |
+
"""Triggers scripted deaths and events exactly as they happen in the movie."""
|
| 69 |
+
event_log = ""
|
| 70 |
|
| 71 |
+
# Mission 2: Caparzo
|
| 72 |
+
if self.current_mission == 2 and self.distance >= 200 and self.squad["Private Caparzo"]["status"] == "Alive":
|
| 73 |
+
self.squad["Private Caparzo"]["status"] = "Dead"
|
| 74 |
+
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."
|
| 75 |
+
|
| 76 |
+
# Mission 3: Wade
|
| 77 |
+
elif self.current_mission == 3 and self.distance >= 250 and self.squad["Medic Wade"]["status"] == "Alive":
|
| 78 |
+
self.squad["Medic Wade"]["status"] = "Dead"
|
| 79 |
+
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)."
|
| 80 |
|
| 81 |
+
# Mission 4: The Final Stand
|
| 82 |
+
elif self.current_mission == 4:
|
| 83 |
+
if not self.ryan_found:
|
| 84 |
+
self.ryan_found = True
|
| 85 |
+
event_log = "β¨ PLOT EVENT: You found Private James Ryan. He refuses to leave his post. The squad stays to defend the bridge."
|
| 86 |
+
|
| 87 |
+
# Scripted deaths during the bridge defense
|
| 88 |
+
if self.distance >= 450 and self.squad["Private Jackson"]["status"] == "Alive":
|
| 89 |
+
self.squad["Private Jackson"]["status"] = "Dead"
|
| 90 |
+
event_log = "β οΈ Jackson is in the bell tower... A Tiger tank levels the tower. Jackson has fallen."
|
| 91 |
+
|
| 92 |
+
if self.distance >= 500 and self.squad["Private Mellish"]["status"] == "Alive":
|
| 93 |
+
self.squad["Private Mellish"]["status"] = "Dead"
|
| 94 |
+
event_log = "β οΈ Hand-to-hand combat in the ruins. Mellish is overwhelmed. Upham is frozen in the stairwell..."
|
| 95 |
+
|
| 96 |
+
if self.distance >= 550 and self.squad["Sergeant Horvath"]["status"] == "Alive":
|
| 97 |
+
self.squad["Sergeant Horvath"]["status"] = "Dead"
|
| 98 |
+
event_log = "β οΈ Horvath is hit multiple times. He fires one last round at the tank. 'I moved the rock...'"
|
| 99 |
|
| 100 |
+
return event_log
|
|
|
|
|
|
|
| 101 |
|
| 102 |
+
def miller_sacrifice(self):
|
| 103 |
+
"""The final scene on the bridge."""
|
| 104 |
+
self.miller_alive = False
|
| 105 |
+
self.game_over = True
|
| 106 |
+
sacrifice_text = """
|
| 107 |
+
ββββββββββββββββββββββββββββββ
|
| 108 |
+
π₯ THE FINAL SACRIFICE π₯
|
| 109 |
+
ββββββββββββββββββββββββββββββ
|
| 110 |
+
Miller is wounded on the bridge, leaning against a pillar.
|
| 111 |
+
A Tiger tank rolls toward him. He pulls his Colt 1911 and fires uselessly at the steel.
|
| 112 |
+
|
| 113 |
+
SuddenlyβBOOM! A P-51 Mustang 'Tank Buster' screams overhead and vaporizes the Tiger.
|
| 114 |
+
|
| 115 |
+
Reiben finds you. You pull James Ryan close.
|
| 116 |
+
Miller: 'James... earn this. Earn it.'
|
| 117 |
+
|
| 118 |
+
Captain John H. Miller has died.
|
| 119 |
+
ββββββββββββββββββββββββββββββ
|
| 120 |
+
"""
|
| 121 |
+
return sacrifice_text
|
| 122 |
+
|
| 123 |
+
def make_move(self, action):
|
| 124 |
+
self.output_text = ""
|
| 125 |
+
if self.game_over: return self.output_text, "Campaign Finished", gr.update(visible=False)
|
| 126 |
+
|
| 127 |
+
# Movement
|
| 128 |
+
adv = random.randint(40, 80) if action == "rush" else random.randint(20, 40)
|
| 129 |
+
self.distance += adv
|
| 130 |
|
| 131 |
+
# Check Plot
|
| 132 |
+
plot_event = self.process_plot_points()
|
| 133 |
+
if plot_event: self.print_to_output(f"\n{plot_event}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
| 135 |
+
self.print_to_output(f"π’ {self.get_chatter()}")
|
| 136 |
+
|
| 137 |
+
# The End Trigger
|
| 138 |
+
if self.current_mission == 4 and self.distance >= 590:
|
| 139 |
+
self.print_to_output(self.miller_sacrifice())
|
| 140 |
+
return self.output_text, "Earn This.", gr.update(visible=False)
|
| 141 |
+
|
| 142 |
+
# Standard Check
|
| 143 |
+
show_action = False
|
| 144 |
+
if self.distance >= (self.bunkers_destroyed + 1) * (self.mission_objective / 3):
|
| 145 |
+
show_action = True
|
| 146 |
+
self.print_to_output("\nβ οΈ CONTACT: Enemy forces blocking progress!")
|
| 147 |
+
|
| 148 |
+
if action == "assault":
|
| 149 |
+
self.bunkers_destroyed += 1
|
| 150 |
+
self.print_to_output("π₯ Clear! Advancing...")
|
| 151 |
+
|
| 152 |
+
self.check_transition()
|
| 153 |
+
self.print_status()
|
| 154 |
+
|
| 155 |
+
return self.output_text, "Awaiting Command", gr.update(visible=show_action)
|
| 156 |
+
|
| 157 |
+
def print_status(self):
|
| 158 |
+
living = [n for n, i in self.squad.items() if i["status"] == "Alive"]
|
| 159 |
+
self.print_to_output(f"\nπ LOCATION: {self.map_type} | DISTANCE: {self.distance}/{self.mission_objective}m")
|
| 160 |
+
self.print_to_output(f"π₯ SQUAD: Miller, {', '.join(living)}")
|
| 161 |
+
|
| 162 |
+
def check_transition(self):
|
| 163 |
+
if self.distance >= self.mission_objective and self.current_mission < 4:
|
| 164 |
+
self.current_mission += 1
|
| 165 |
+
self.reset_mission_stats()
|
| 166 |
+
self.print_to_output(f"\nβ
MISSION COMPLETE. Transitioning to {self.mission_names[self.current_mission-1]}...")
|
| 167 |
+
|
| 168 |
+
def print_to_output(self, text):
|
| 169 |
+
self.output_text += text + "\n"
|
| 170 |
+
|
| 171 |
+
def start_game(self, _):
|
| 172 |
+
self.output_text = "June 6, 1944. Dog One Sector. Omaha Beach.\n'CLEAR THE RAMP!'"
|
| 173 |
+
self.current_mission = 1
|
| 174 |
+
self.reset_mission_stats()
|
| 175 |
+
self.print_status()
|
| 176 |
+
return self.output_text, "Rangers Lead The Way", gr.update(visible=False)
|
| 177 |
+
|
| 178 |
+
def create_ui():
|
| 179 |
+
game = SavingPrivateRyanGame()
|
| 180 |
+
with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
|
| 181 |
+
gr.Markdown("# ποΈ SAVING PRIVATE RYAN: THE COMPLETE JOURNEY")
|
| 182 |
|
| 183 |
with gr.Row():
|
| 184 |
+
with gr.Column(scale=1):
|
| 185 |
+
start_btn = gr.Button("π Start Campaign", variant="primary")
|
| 186 |
+
rush_btn = gr.Button("β‘ Advance")
|
| 187 |
+
|
| 188 |
+
with gr.Column(visible=False) as action_row:
|
| 189 |
+
gr.Markdown("### β οΈ ENGAGEMENT")
|
| 190 |
+
assault_btn = gr.Button("π₯ Tactical Assault", variant="stop")
|
| 191 |
+
|
| 192 |
+
with gr.Column(scale=2):
|
| 193 |
+
output = gr.TextArea(label="Radio Log", lines=25)
|
| 194 |
+
status = gr.Textbox(label="Mission Status")
|
| 195 |
|
| 196 |
+
controls = [output, status, action_row]
|
| 197 |
+
start_btn.click(game.start_game, None, controls)
|
| 198 |
+
rush_btn.click(lambda: game.make_move("rush"), None, controls)
|
| 199 |
+
assault_btn.click(lambda: game.make_move("assault"), None, controls)
|
|
|
|
| 200 |
|
| 201 |
+
return demo
|
| 202 |
|
| 203 |
if __name__ == "__main__":
|
| 204 |
+
create_ui().launch()
|