import streamlit as st
import folium
from streamlit_folium import folium_static
import time
import math
import random
# Wide mode
st.set_page_config(layout="wide")
# Deer hunting locations and resorts (approximated coordinates)
deer_hunting_spots = [
("Buffalo County, WI", 44.3661, -91.7535, "Big Buck Capital", "Trophy bucks, rugged terrain"),
("Winona County, MN", 44.0500, -91.6393, "Southeast MN", "Big bucks, public WMAs"),
("Houston County, MN", 43.6666, -91.4929, "Southeast MN", "Thriving herds, farmland"),
("Trempealeau County, WI", 44.3040, -91.3593, "Western WI", "Solid deer population"),
("Crawford County, WI", 43.2396, -90.9315, "Western WI", "Driftless Area, deer paradise"),
("Rutting Ridge Outfitters, WI", 44.4119, -91.8638, "Buffalo Co.", "Trophy hunts, rut focus"),
("Deer Haven Acres, WI", 43.8769, -89.4825, "Adams Co.", "238-acre preserve"),
("Nodak Lodge, MN", 47.5148, -94.8800, "Bena, MN", "Near Superior Natβl Forest"),
("Wigwam Resort, MN", 48.6029, -93.4037, "Baudette, MN", "Northern woods, big deer"),
("Big River Resort, WI", 44.5896, -91.9988, "Alma, WI", "Chill vibes, near Rutting Ridge")
]
# Initialize session state
if 'hunter_position' not in st.session_state:
st.session_state.hunter_position = [44.3661, -91.7535] # Start at Buffalo County
if 'is_traveling' not in st.session_state:
st.session_state.is_traveling = False
if 'start' not in st.session_state:
st.session_state.start = deer_hunting_spots[0]
if 'destination' not in st.session_state:
st.session_state.destination = deer_hunting_spots[1]
if 'start_time' not in st.session_state:
st.session_state.start_time = None
if 'elapsed_time' not in st.session_state:
st.session_state.elapsed_time = 0
if 'arrived' not in st.session_state:
st.session_state.arrived = False
if 'score' not in st.session_state:
st.session_state.score = 0
if 'deer_remaining' not in st.session_state:
st.session_state.deer_remaining = 0
# Distance calculation (Haversine in miles)
def calculate_distance(lat1, lon1, lat2, lon2):
R = 3958.8
lat1_rad, lon1_rad = math.radians(lat1), math.radians(lon1)
lat2_rad, lon2_rad = math.radians(lat2), math.radians(lon2)
dlat = lat2_rad - lat1_rad
dlon = lon2_rad - lon1_rad
a = math.sin(dlat / 2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return R * c
# Create map
def create_map():
m = folium.Map(location=st.session_state.hunter_position, zoom_start=8, tiles="CartoDB Positron")
for spot in deer_hunting_spots:
folium.Marker(
location=[spot[1], spot[2]],
popup=f'{spot[0]}
{spot[3]}
{spot[4]}',
icon=folium.Icon(color='green', icon='tree', prefix='fa')
).add_to(m)
folium.Marker(
location=st.session_state.hunter_position,
popup='Hunterβs Rig',
icon=folium.Icon(color='blue', icon='truck', prefix='fa')
).add_to(m)
return m
# Move hunter's rig
def move_rig(total_distance, step_time):
target = [st.session_state.destination[1], st.session_state.destination[2]]
current = st.session_state.hunter_position
remaining_distance = calculate_distance(current[0], current[1], target[0], target[1])
speed = total_distance / 25.0 # miles per second (25s total travel)
step_distance = speed * step_time
total_lat_diff = target[0] - current[0]
total_lon_diff = target[1] - current[1]
total_hyp = math.sqrt(total_lat_diff**2 + total_lon_diff**2)
if total_hyp > 0:
lat_step = (total_lat_diff / total_hyp) * step_distance / (calculate_distance(current[0], current[1], current[0] + 0.01, current[1]) / 0.01)
lon_step = (total_lon_diff / total_hyp) * step_distance / (calculate_distance(current[0], current[1], current[0], current[1] + 0.01) / 0.01)
st.session_state.hunter_position[0] += lat_step
st.session_state.hunter_position[1] += lon_step
if remaining_distance < 0.1:
st.session_state.hunter_position = target
st.session_state.is_traveling = False
st.session_state.arrived = True
# Simple 3D hunting simulation (text-based for Streamlit)
def hunt_deer(location):
if st.session_state.deer_remaining == 0:
st.session_state.deer_remaining = random.randint(3, 6) # Deer at location
st.write(f"Hunting at {location[0]}: {st.session_state.deer_remaining} deer spotted.")
action = st.radio("Choose your action:", ("Aim and Shoot", "Wait", "Move"))
if action == "Aim and Shoot":
if random.random() < 0.7: # 70% success rate
st.session_state.score += 50
st.session_state.deer_remaining -= 1
st.success("π― Deer hunted! +50 points")
else:
st.warning("Missed the shot!")
elif action == "Wait":
st.write("You wait quietly... deer are still around.")
elif action == "Move":
st.session_state.deer_remaining -= random.randint(0, 2) # Some deer might flee
st.write("You move, startling some deer.")
if st.session_state.deer_remaining <= 0:
st.write("No more deer in this area. Travel to another spot!")
st.write(f"Score: {st.session_state.score}")
# Layout
left_col, right_col = st.columns([3, 1])
# Left column: Map and controls
with left_col:
st.markdown("### π¦ Deer Hunterβs Basecamp π²")
m = create_map()
folium_static(m, width=1280, height=1024)
col1, col2, col3 = st.columns(3)
with col1:
if st.button("π Launch Rig"):
st.session_state.is_traveling = True
st.session_state.start_time = time.time()
st.session_state.arrived = False
with col2:
if st.button("π Stop Rig"):
st.session_state.is_traveling = False
st.session_state.start_time = None
with col3:
if st.button("π Reset to Start"):
st.session_state.hunter_position = [st.session_state.start[1], st.session_state.start[2]]
st.session_state.is_traveling = False
st.session_state.start_time = None
st.session_state.elapsed_time = 0
st.session_state.arrived = False
st.session_state.deer_remaining = 0
st.rerun()
if st.session_state.arrived:
st.success(f"π― Arrived at {st.session_state.destination[0]}!")
hunt_deer(st.session_state.destination)
# Right column: Stats and navigation
with right_col:
st.markdown("### π‘ Hunt Status")
total_distance = calculate_distance(st.session_state.start[1], st.session_state.start[2],
st.session_state.destination[1], st.session_state.destination[2])
remaining_distance = calculate_distance(st.session_state.hunter_position[0], st.session_state.hunter_position[1],
st.session_state.destination[1], st.session_state.destination[2])
speed = total_distance / (25.0 / 3600) # mph
st.write(f"**Start**: {st.session_state.start[0]}")
st.write(f"**Destination**: {st.session_state.destination[0]}")
st.write(f"**Total Distance**: {total_distance:.1f} miles")
st.write(f"**Remaining**: {remaining_distance:.1f} miles")
st.write(f"**Speed**: {speed:.1f} mph")
st.write(f"**ETA**: 25.0 seconds")
st.write(f"**Score**: {st.session_state.score}")
start_col, dest_col = st.columns(2)
with start_col:
st.write("**π¦ Start Points**")
for spot in deer_hunting_spots:
if st.button(f"π {spot[0]}", key=f"start_{spot[0]}"):
st.session_state.start = spot
st.session_state.hunter_position = [spot[1], spot[2]]
st.session_state.is_traveling = False
st.session_state.arrived = False
st.session_state.deer_remaining = 0
st.rerun()
with dest_col:
st.write("**π Destinations**")
for spot in deer_hunting_spots:
if st.button(f"π― {spot[0]}", key=f"dest_{spot[0]}"):
st.session_state.destination = spot
st.session_state.is_traveling = False
st.session_state.arrived = False
st.session_state.deer_remaining = 0
st.rerun()
# Movement logic
if st.session_state.is_traveling:
move_rig(total_distance, 0.5)
if st.session_state.start_time:
st.session_state.elapsed_time = time.time() - st.session_state.start_time
time.sleep(0.5)
st.rerun()