Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import folium
|
3 |
+
from streamlit_folium import folium_static
|
4 |
+
import time
|
5 |
+
import math
|
6 |
+
import random
|
7 |
+
|
8 |
+
# Wide mode
|
9 |
+
st.set_page_config(layout="wide")
|
10 |
+
|
11 |
+
# Deer hunting locations and resorts (approximated coordinates)
|
12 |
+
deer_hunting_spots = [
|
13 |
+
("Buffalo County, WI", 44.3661, -91.7535, "Big Buck Capital", "Trophy bucks, rugged terrain"),
|
14 |
+
("Winona County, MN", 44.0500, -91.6393, "Southeast MN", "Big bucks, public WMAs"),
|
15 |
+
("Houston County, MN", 43.6666, -91.4929, "Southeast MN", "Thriving herds, farmland"),
|
16 |
+
("Trempealeau County, WI", 44.3040, -91.3593, "Western WI", "Solid deer population"),
|
17 |
+
("Crawford County, WI", 43.2396, -90.9315, "Western WI", "Driftless Area, deer paradise"),
|
18 |
+
("Rutting Ridge Outfitters, WI", 44.4119, -91.8638, "Buffalo Co.", "Trophy hunts, rut focus"),
|
19 |
+
("Deer Haven Acres, WI", 43.8769, -89.4825, "Adams Co.", "238-acre preserve"),
|
20 |
+
("Nodak Lodge, MN", 47.5148, -94.8800, "Bena, MN", "Near Superior Nat’l Forest"),
|
21 |
+
("Wigwam Resort, MN", 48.6029, -93.4037, "Baudette, MN", "Northern woods, big deer"),
|
22 |
+
("Big River Resort, WI", 44.5896, -91.9988, "Alma, WI", "Chill vibes, near Rutting Ridge")
|
23 |
+
]
|
24 |
+
|
25 |
+
# Initialize session state
|
26 |
+
if 'hunter_position' not in st.session_state:
|
27 |
+
st.session_state.hunter_position = [44.3661, -91.7535] # Start at Buffalo County
|
28 |
+
if 'is_traveling' not in st.session_state:
|
29 |
+
st.session_state.is_traveling = False
|
30 |
+
if 'start' not in st.session_state:
|
31 |
+
st.session_state.start = deer_hunting_spots[0]
|
32 |
+
if 'destination' not in st.session_state:
|
33 |
+
st.session_state.destination = deer_hunting_spots[1]
|
34 |
+
if 'start_time' not in st.session_state:
|
35 |
+
st.session_state.start_time = None
|
36 |
+
if 'elapsed_time' not in st.session_state:
|
37 |
+
st.session_state.elapsed_time = 0
|
38 |
+
if 'arrived' not in st.session_state:
|
39 |
+
st.session_state.arrived = False
|
40 |
+
if 'score' not in st.session_state:
|
41 |
+
st.session_state.score = 0
|
42 |
+
if 'deer_remaining' not in st.session_state:
|
43 |
+
st.session_state.deer_remaining = 0
|
44 |
+
|
45 |
+
# Distance calculation (Haversine in miles)
|
46 |
+
def calculate_distance(lat1, lon1, lat2, lon2):
|
47 |
+
R = 3958.8
|
48 |
+
lat1_rad, lon1_rad = math.radians(lat1), math.radians(lon1)
|
49 |
+
lat2_rad, lon2_rad = math.radians(lat2), math.radians(lon2)
|
50 |
+
dlat = lat2_rad - lat1_rad
|
51 |
+
dlon = lon2_rad - lon1_rad
|
52 |
+
a = math.sin(dlat / 2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2)**2
|
53 |
+
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
54 |
+
return R * c
|
55 |
+
|
56 |
+
# Create map
|
57 |
+
def create_map():
|
58 |
+
m = folium.Map(location=st.session_state.hunter_position, zoom_start=8, tiles="CartoDB Positron")
|
59 |
+
for spot in deer_hunting_spots:
|
60 |
+
folium.Marker(
|
61 |
+
location=[spot[1], spot[2]],
|
62 |
+
popup=f'<b>{spot[0]}</b><br>{spot[3]}<br>{spot[4]}',
|
63 |
+
icon=folium.Icon(color='green', icon='tree', prefix='fa')
|
64 |
+
).add_to(m)
|
65 |
+
folium.Marker(
|
66 |
+
location=st.session_state.hunter_position,
|
67 |
+
popup='Hunter’s Rig',
|
68 |
+
icon=folium.Icon(color='blue', icon='truck', prefix='fa')
|
69 |
+
).add_to(m)
|
70 |
+
return m
|
71 |
+
|
72 |
+
# Move hunter's rig
|
73 |
+
def move_rig(total_distance, step_time):
|
74 |
+
target = [st.session_state.destination[1], st.session_state.destination[2]]
|
75 |
+
current = st.session_state.hunter_position
|
76 |
+
remaining_distance = calculate_distance(current[0], current[1], target[0], target[1])
|
77 |
+
speed = total_distance / 25.0 # miles per second (25s total travel)
|
78 |
+
step_distance = speed * step_time
|
79 |
+
total_lat_diff = target[0] - current[0]
|
80 |
+
total_lon_diff = target[1] - current[1]
|
81 |
+
total_hyp = math.sqrt(total_lat_diff**2 + total_lon_diff**2)
|
82 |
+
if total_hyp > 0:
|
83 |
+
lat_step = (total_lat_diff / total_hyp) * step_distance / (calculate_distance(current[0], current[1], current[0] + 0.01, current[1]) / 0.01)
|
84 |
+
lon_step = (total_lon_diff / total_hyp) * step_distance / (calculate_distance(current[0], current[1], current[0], current[1] + 0.01) / 0.01)
|
85 |
+
st.session_state.hunter_position[0] += lat_step
|
86 |
+
st.session_state.hunter_position[1] += lon_step
|
87 |
+
if remaining_distance < 0.1:
|
88 |
+
st.session_state.hunter_position = target
|
89 |
+
st.session_state.is_traveling = False
|
90 |
+
st.session_state.arrived = True
|
91 |
+
|
92 |
+
# Simple 3D hunting simulation (text-based for Streamlit)
|
93 |
+
def hunt_deer(location):
|
94 |
+
if st.session_state.deer_remaining == 0:
|
95 |
+
st.session_state.deer_remaining = random.randint(3, 6) # Deer at location
|
96 |
+
st.write(f"Hunting at {location[0]}: {st.session_state.deer_remaining} deer spotted.")
|
97 |
+
action = st.radio("Choose your action:", ("Aim and Shoot", "Wait", "Move"))
|
98 |
+
if action == "Aim and Shoot":
|
99 |
+
if random.random() < 0.7: # 70% success rate
|
100 |
+
st.session_state.score += 50
|
101 |
+
st.session_state.deer_remaining -= 1
|
102 |
+
st.success("🎯 Deer hunted! +50 points")
|
103 |
+
else:
|
104 |
+
st.warning("Missed the shot!")
|
105 |
+
elif action == "Wait":
|
106 |
+
st.write("You wait quietly... deer are still around.")
|
107 |
+
elif action == "Move":
|
108 |
+
st.session_state.deer_remaining -= random.randint(0, 2) # Some deer might flee
|
109 |
+
st.write("You move, startling some deer.")
|
110 |
+
if st.session_state.deer_remaining <= 0:
|
111 |
+
st.write("No more deer in this area. Travel to another spot!")
|
112 |
+
st.write(f"Score: {st.session_state.score}")
|
113 |
+
|
114 |
+
# Layout
|
115 |
+
left_col, right_col = st.columns([3, 1])
|
116 |
+
|
117 |
+
# Left column: Map and controls
|
118 |
+
with left_col:
|
119 |
+
st.markdown("### 🦌 Deer Hunter’s Basecamp 🌲")
|
120 |
+
m = create_map()
|
121 |
+
folium_static(m, width=960, height=540)
|
122 |
+
|
123 |
+
col1, col2, col3 = st.columns(3)
|
124 |
+
with col1:
|
125 |
+
if st.button("🚚 Launch Rig"):
|
126 |
+
st.session_state.is_traveling = True
|
127 |
+
st.session_state.start_time = time.time()
|
128 |
+
st.session_state.arrived = False
|
129 |
+
with col2:
|
130 |
+
if st.button("🛑 Stop Rig"):
|
131 |
+
st.session_state.is_traveling = False
|
132 |
+
st.session_state.start_time = None
|
133 |
+
with col3:
|
134 |
+
if st.button("🔄 Reset to Start"):
|
135 |
+
st.session_state.hunter_position = [st.session_state.start[1], st.session_state.start[2]]
|
136 |
+
st.session_state.is_traveling = False
|
137 |
+
st.session_state.start_time = None
|
138 |
+
st.session_state.elapsed_time = 0
|
139 |
+
st.session_state.arrived = False
|
140 |
+
st.session_state.deer_remaining = 0
|
141 |
+
st.rerun()
|
142 |
+
|
143 |
+
if st.session_state.arrived:
|
144 |
+
st.success(f"🎯 Arrived at {st.session_state.destination[0]}!")
|
145 |
+
hunt_deer(st.session_state.destination)
|
146 |
+
|
147 |
+
# Right column: Stats and navigation
|
148 |
+
with right_col:
|
149 |
+
st.markdown("### 📡 Hunt Status")
|
150 |
+
total_distance = calculate_distance(st.session_state.start[1], st.session_state.start[2],
|
151 |
+
st.session_state.destination[1], st.session_state.destination[2])
|
152 |
+
remaining_distance = calculate_distance(st.session_state.hunter_position[0], st.session_state.hunter_position[1],
|
153 |
+
st.session_state.destination[1], st.session_state.destination[2])
|
154 |
+
speed = total_distance / (25.0 / 3600) # mph
|
155 |
+
st.write(f"**Start**: {st.session_state.start[0]}")
|
156 |
+
st.write(f"**Destination**: {st.session_state.destination[0]}")
|
157 |
+
st.write(f"**Total Distance**: {total_distance:.1f} miles")
|
158 |
+
st.write(f"**Remaining**: {remaining_distance:.1f} miles")
|
159 |
+
st.write(f"**Speed**: {speed:.1f} mph")
|
160 |
+
st.write(f"**ETA**: 25.0 seconds")
|
161 |
+
st.write(f"**Score**: {st.session_state.score}")
|
162 |
+
|
163 |
+
start_col, dest_col = st.columns(2)
|
164 |
+
with start_col:
|
165 |
+
st.write("**🚦 Start Points**")
|
166 |
+
for spot in deer_hunting_spots:
|
167 |
+
if st.button(f"📍 {spot[0]}", key=f"start_{spot[0]}"):
|
168 |
+
st.session_state.start = spot
|
169 |
+
st.session_state.hunter_position = [spot[1], spot[2]]
|
170 |
+
st.session_state.is_traveling = False
|
171 |
+
st.session_state.arrived = False
|
172 |
+
st.session_state.deer_remaining = 0
|
173 |
+
st.rerun()
|
174 |
+
with dest_col:
|
175 |
+
st.write("**🏁 Destinations**")
|
176 |
+
for spot in deer_hunting_spots:
|
177 |
+
if st.button(f"🎯 {spot[0]}", key=f"dest_{spot[0]}"):
|
178 |
+
st.session_state.destination = spot
|
179 |
+
st.session_state.is_traveling = False
|
180 |
+
st.session_state.arrived = False
|
181 |
+
st.session_state.deer_remaining = 0
|
182 |
+
st.rerun()
|
183 |
+
|
184 |
+
# Movement logic
|
185 |
+
if st.session_state.is_traveling:
|
186 |
+
move_rig(total_distance, 0.5)
|
187 |
+
if st.session_state.start_time:
|
188 |
+
st.session_state.elapsed_time = time.time() - st.session_state.start_time
|
189 |
+
time.sleep(0.5)
|
190 |
+
st.rerun()
|