import streamlit as st import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsRegressor # --------------------------- # Page config # --------------------------- st.set_page_config( page_title="Neural Tuner – Car Mod Performance Estimator", page_icon="πŸ”₯", layout="wide" ) # --------------------------- # SESSION STATE INITIALIZATION (Fixes Yellow Warning) # --------------------------- def init_session_state(): # Only set these defaults if they don't exist yet defaults = { "engine_disp": 2.0, "engine_layout": "I6", "base_hp": 240, "weight_kg": 1500, "weight_reduction": 0, "intake": "Stock", "headers": "Stock", "exhaust": "Stock", "exhaust_dia": 60, "cam": "Stock", "intake_manifold": "Stock", "induction": "None", "boost_psi": 0, "turbo_size": "N/A", "intercooler": "None", "meth": False, "tune": "None", "fuel": "93", "altitude": 200, "traction_mode": "Spirited" } for key, value in defaults.items(): if key not in st.session_state: st.session_state[key] = value # Run init immediately init_session_state() # --------------------------- # Global CSS (Fixes "Cutting Out" / Rounds) # --------------------------- st.markdown( """ """, unsafe_allow_html=True ) # --------------------------- # Data & model # --------------------------- def generate_dataset(n=400): np.random.seed(42) data = [] for _ in range(n): engine = np.random.choice([1.6, 2.0, 2.5, 3.0, 3.5, 5.0]) cyl = np.random.choice([4, 6, 8]) base_hp = int(engine * cyl * np.random.uniform(18, 22)) intake = np.random.choice([0, 1, 2]) exhaust = np.random.choice([0, 1, 2]) induction = np.random.choice([0, 1, 2]) fuel = np.random.choice([0, 1, 2, 3]) tune = np.random.choice([0, 1, 2]) altitude = np.random.uniform(0, 2000) hp_gain = ( intake * np.random.uniform(3, 10) + exhaust * np.random.uniform(5, 20) + induction * np.random.uniform(25, 100) + tune * np.random.uniform(10, 35) + fuel * np.random.uniform(2, 8) - altitude * 0.01 + np.random.uniform(-3, 3) ) data.append([engine, cyl, base_hp, intake, exhaust, induction, fuel, tune, altitude, hp_gain]) columns = [ "engine", "cyl", "base_hp", "intake", "exhaust", "induction", "fuel", "tune", "altitude", "hp_gain" ] return pd.DataFrame(data, columns=columns) df = generate_dataset() X = df.drop("hp_gain", axis=1) y = df["hp_gain"] scaler = StandardScaler() X_scaled = scaler.fit_transform(X) model = KNeighborsRegressor(n_neighbors=5, weights="distance") model.fit(X_scaled, y) # --------------------------- # PRESET CALLBACKS # --------------------------- def preset_daily(): s = st.session_state s.engine_disp = 1.6 s.engine_layout = "I4" s.base_hp = 120 s.weight_kg = 1350 s.weight_reduction = 0 s.intake = "Stock" s.headers = "Stock" s.exhaust = "Stock" s.exhaust_dia = 50 s.cam = "Stock" s.intake_manifold = "Stock" s.induction = "None" s.boost_psi = 0 s.turbo_size = "N/A" s.intercooler = "None" s.meth = False s.tune = "None" s.fuel = "87" s.altitude = 200 s.traction_mode = "Daily" def preset_street(): s = st.session_state s.engine_disp = 2.0 s.engine_layout = "I4" s.base_hp = 190 s.weight_kg = 1400 s.weight_reduction = 5 s.intake = "Cold Air" s.headers = "Shorty" s.exhaust = "Cat-back" s.exhaust_dia = 60 s.cam = "Stage 1 – Road" s.intake_manifold = "High-flow" s.induction = "Turbo" s.boost_psi = 12 s.turbo_size = "Small 45-55mm" s.intercooler = "Air-to-Air" s.meth = False s.tune = "Stage 1" s.fuel = "93" s.altitude = 150 s.traction_mode = "Spirited" def preset_drag(): s = st.session_state s.engine_disp = 5.0 s.engine_layout = "V8" s.base_hp = 420 s.weight_kg = 1500 s.weight_reduction = 18 s.intake = "Performance" s.headers = "Long Tube" s.exhaust = "Straight Pipe" s.exhaust_dia = 90 s.cam = "Stage 2 – Aggressive" s.intake_manifold = "High-flow" s.induction = "Twin-Turbo" s.boost_psi = 24 s.turbo_size = "Big 66-75mm" s.intercooler = "Front-mount High-Flow" s.meth = True s.tune = "Kill Mode" s.fuel = "E85 / Race Blend" s.altitude = 100 s.traction_mode = "Track / Drag" def preset_max(): s = st.session_state s.engine_disp = 6.0 s.engine_layout = "V12" s.base_hp = 650 s.weight_kg = 1400 s.weight_reduction = 25 s.intake = "Performance" s.headers = "Long Tube" s.exhaust = "Straight Pipe" s.exhaust_dia = 100 s.cam = "Stage 3 – Race" s.intake_manifold = "Individual throttle bodies" s.induction = "Twincharged" s.boost_psi = 30 s.turbo_size = "XL 76mm+" s.intercooler = "Front-mount High-Flow" s.meth = True s.tune = "Kill Mode" s.fuel = "E85 / Race Blend" s.altitude = 0 s.traction_mode = "Track / Drag" # --------------------------- # HERO SECTION # --------------------------- st.markdown( """
βš™οΈ KNN model + synthetic dyno data
πŸŽ›οΈ Live tuning playground
Neural Tuner – Car Mod Performance Lab

Mash intakes, boost, tunes and fuel in real time. Watch horsepower climb and the rev limiter dance. Built to make car nerds go feral. 🐺

Virtual
Dyno
No hardware, all chaos
""", unsafe_allow_html=True ) st.markdown("") # --------------------------- # Layout # --------------------------- left, right = st.columns([1.15, 1]) # ========= LEFT SIDE ========= with left: st.markdown('
', unsafe_allow_html=True) st.subheader("πŸŽ›οΈ Tune Your Build") # Preset buttons bcol1, bcol2, bcol3, bcol4 = st.columns(4) bcol1.button("πŸš™ Daily Driver", on_click=preset_daily) bcol2.button("πŸŒƒ Street Fun", on_click=preset_street) bcol3.button("🏁 Drag Strip", on_click=preset_drag) bcol4.button("πŸ”₯ Max Attack", on_click=preset_max) st.markdown("") tabs = st.tabs(["Core Specs", "Bolt-Ons", "Boost & Cooling", "ECU & Fuel"]) # NOTE: I removed 'index=...' from these selectboxes. # They now just rely on the key being present in st.session_state (handled by init_session_state) # --- Core specs --- with tabs[0]: col1, col2 = st.columns(2) engine_disp_options = [0.8,1.0,1.2,1.4,1.6,1.8,2.0,2.2,2.4,2.5, 2.8,3.0,3.2,3.5,4.0,4.4,5.0,6.0,7.0,8.0] # We manually update value if it's missing (failsafe), though init handles it. if "engine_disp" not in st.session_state: st.session_state.engine_disp = 2.0 engine_disp = col1.selectbox( "Engine Displacement (L)", engine_disp_options, key="engine_disp" ) engine_layout = col2.selectbox( "Engine Layout", ["I3","I4","I6","V6","V8","V10","V12"], key="engine_layout" ) layout_to_cyl = {"I3":3,"I4":4,"I6":6,"V6":6,"V8":8,"V10":10,"V12":12} cyl = layout_to_cyl[engine_layout] base_hp = st.number_input( "Base Horsepower (stock dyno)", min_value=60, max_value=3000, key="base_hp" ) col3, col4 = st.columns(2) weight_kg = col3.number_input( "Vehicle Weight (kg)", min_value=700, max_value=4000, key="weight_kg" ) weight_reduction = col4.slider( "Weight Reduction (%)", 0, 40, key="weight_reduction" ) # --- Bolt-ons --- with tabs[1]: col1, col2, col3 = st.columns(3) intake = col1.selectbox("Intake", ["Stock", "Cold Air", "Performance"], key="intake") headers = col2.selectbox("Headers", ["Stock", "Shorty", "Long Tube"], key="headers") exhaust = col3.selectbox("Exhaust", ["Stock", "Cat-back", "Straight Pipe"], key="exhaust") exhaust_dia = st.slider("Exhaust Diameter (mm)", 40, 120, key="exhaust_dia") cam = st.selectbox( "Cam Profile", ["Stock", "Stage 1 – Road", "Stage 2 – Aggressive", "Stage 3 – Race"], key="cam" ) intake_manifold = st.selectbox( "Intake Manifold", ["Stock", "High-flow", "Individual throttle bodies"], key="intake_manifold" ) # --- Boost & cooling --- with tabs[2]: induction = st.selectbox( "Forced Induction Setup", ["None", "Turbo", "Twin-Turbo", "Supercharger", "Twincharged"], key="induction" ) boost_psi = st.slider("Target Boost (psi)", 0, 40, key="boost_psi") turbo_size = st.selectbox( "Turbo Size", ["N/A", "Small 45-55mm", "Medium 56-65mm", "Big 66-75mm", "XL 76mm+"], key="turbo_size" ) intercooler = st.selectbox( "Intercooler Type", ["None", "Air-to-Air", "Air-to-Water", "Front-mount High-Flow"], key="intercooler" ) meth = st.checkbox("Methanol Injection Kit", key="meth") # --- ECU & fuel --- with tabs[3]: tune = st.selectbox( "ECU Tune Level", ["None", "Mild Street", "Stage 1", "Stage 2", "Kill Mode"], key="tune" ) fuel = st.selectbox( "Fuel Type", ["87", "91", "93", "E85 / Race Blend"], key="fuel" ) altitude = st.slider("Altitude (meters)", 0, 3500, key="altitude") traction_mode = st.radio( "Traction Mode Vibe", ["Daily", "Spirited", "Track / Drag"], horizontal=True, key="traction_mode" ) st.markdown( '

Numbers are synthetic; this is a dyno-inspired playground, not a tuning bible. πŸ§ͺ

', unsafe_allow_html=True ) st.markdown('
', unsafe_allow_html=True) # ========= MODEL & CALCS ========= intake_map = {"Stock":0, "Cold Air":1, "Performance":2} exhaust_map = {"Stock":0, "Cat-back":1, "Straight Pipe":2} induction_model_map = {"None":0, "Turbo":1, "Twin-Turbo":1, "Supercharger":2, "Twincharged":2} fuel_map = {"87":0, "91":1, "93":2, "E85 / Race Blend":3} tune_base_map = {"None":0, "Mild Street":1, "Stage 1":1, "Stage 2":2, "Kill Mode":2} input_for_model = np.array([[ engine_disp, cyl, base_hp, intake_map.get(intake,0), exhaust_map.get(exhaust,0), induction_model_map.get(induction,0), fuel_map.get(fuel,0), tune_base_map.get(tune,0), altitude ]]) input_scaled = scaler.transform(input_for_model) pred_base = float(model.predict(input_scaled)[0]) cam_gain_map = { "Stock":0.0, "Stage 1 – Road":6.0, "Stage 2 – Aggressive":12.0, "Stage 3 – Race":20.0 } headers_gain_map = {"Stock":0.0, "Shorty":5.0, "Long Tube":9.0} intake_man_gain_map = { "Stock":0.0, "High-flow":5.0, "Individual throttle bodies":10.0 } intercooler_gain_map = { "None":0.0, "Air-to-Air":4.0, "Air-to-Water":6.5, "Front-mount High-Flow":9.0 } turbo_size_map = { "N/A":0.0, "Small 45-55mm":5.0, "Medium 56-65mm":12.0, "Big 66-75mm":20.0, "XL 76mm+":28.0 } cam_gain = cam_gain_map.get(cam, 0.0) headers_gain = headers_gain_map.get(headers, 0.0) intake_man_gain = intake_man_gain_map.get(intake_manifold, 0.0) intercooler_gain = intercooler_gain_map.get(intercooler, 0.0) turbo_size_gain = turbo_size_map.get(turbo_size, 0.0) if induction in ["Turbo", "Twin-Turbo"]: boost_gain = boost_psi * 1.8 elif induction in ["Supercharger", "Twincharged"]: boost_gain = boost_psi * 1.4 else: boost_gain = 0.0 meth_gain = 12.0 if meth else 0.0 exhaust_dia_gain = max(0.0, (exhaust_dia - 55) * 0.1) extra_gain = ( cam_gain + headers_gain + intake_man_gain + intercooler_gain + turbo_size_gain + boost_gain * 0.9 + meth_gain + exhaust_dia_gain ) traction_multiplier = {"Daily":0.9, "Spirited":1.0, "Track / Drag":1.05}[traction_mode] pred_total_gain = (pred_base + extra_gain) * traction_multiplier pred_total_gain = max(pred_total_gain, -5.0) new_hp = max(base_hp + pred_total_gain, 40.0) effective_weight = weight_kg * (1 - weight_reduction / 100.0) hp_per_ton = new_hp / (effective_weight / 1000.0) hype_raw = np.clip(pred_total_gain / 120 * 100, 0, 100) hype_level = int(hype_raw) if hype_level < 20: verdict = "Sleeper grocery getter πŸš™" elif hype_level < 40: verdict = "Respectable street build πŸŒƒ" elif hype_level < 70: verdict = "Serious weekend weapon βš”οΈ" else: verdict = "Full send, tyres cry for mercy 🏁" # compute base angle for rev gauge (-90deg to +90deg) angle_deg = -90 + (hype_level / 100.0) * 180 # ========= RIGHT SIDE ========= with right: st.markdown('
', unsafe_allow_html=True) st.subheader("πŸ’₯ Build Outcome") m1, m2, m3 = st.columns(3) m1.metric("Estimated HP Gain", f"{pred_total_gain:.1f} HP") m2.metric("New Output", f"{new_hp:.1f} HP") m3.metric("HP per Ton", f"{hp_per_ton:.1f}") # Animated rev limiter gauge st.markdown('
Rev Limiter Vibes
', unsafe_allow_html=True) st.markdown( f"""
RPM
REDLINE
""", unsafe_allow_html=True ) st.markdown('
Hype Meter (relative craziness of this build)
', unsafe_allow_html=True) st.progress(hype_level) st.markdown(f"**Verdict:** {verdict}") st.markdown("") st.bar_chart( pd.DataFrame( {"Horsepower": [base_hp, new_hp]}, index=["Stock", "Tuned"] ) ) st.markdown( f"""
{engine_disp}L {engine_layout} Weight: {weight_kg} kg β–Έ βˆ’{weight_reduction}% Intake: {intake} Headers: {headers} Exhaust: {exhaust} ({exhaust_dia}mm)
Induction: {induction} @ {boost_psi} psi IC: {intercooler} Tune: {tune} Fuel: {fuel}
""", unsafe_allow_html=True ) st.markdown("---") breakdown = pd.DataFrame({ "Component": [ "Model base (from dataset)", "Cam profile", "Headers", "Intake manifold", "Intercooler", "Turbo size", "Boost (net)", "Meth kit", "Exhaust diameter tweak" ], "Approx HP": [ pred_base, cam_gain, headers_gain, intake_man_gain, intercooler_gain, turbo_size_gain, boost_gain * 0.9, meth_gain, exhaust_dia_gain ] }) st.caption("πŸ”¬ Where is that extra power coming from?") st.dataframe(breakdown, use_container_width=True, height=260) st.markdown( '

Model: distance-weighted KNN on synthetic dyno-style data + heuristic math for advanced mods.

', unsafe_allow_html=True ) st.markdown('
', unsafe_allow_html=True) # ========= FOOTER ========= st.markdown( """
Presets + rev limiter + neon dyno… if this doesn’t make people go mad, we can push it even further. πŸ”§πŸ”₯
""", unsafe_allow_html=True )