2025 – Osterprojekt

Ostern steht stand vor der Tür und da habe ich mich mal wieder an Python gesetzt. Meine Frage war, wie ist denn die Laufzeit von Autoreifen. Die Frage lässt sich natürlich sehr schnell mit dem Blick ins Internet beantworten. In diversen Reifentests werden die ungefähren Laufzeiten der Reifen angegeben. Auch die Berechnung ist nicht sonderlich kompliziert, allerdings hatte ich mal wieder Lust mich ein wenig mit Python zu beschäftigen. Zugeben, meine Kenntnisse sind sehr rudimentär und außerdem auch eingerostet. Aus diesem Grund zog ich die „KI“ zur Hilfe heran. Man kann sagen, mit der richtigen Fragestellung kam ein lauffähiger Code heraus. Diesen habe ich dann noch etwas angepasst und erweitert. In dem Programm kann man seine Jahreskilometer eingeben. Außerdem ist da noch der Wert des Abbriebs. Der hängt natürlich von vielen Faktoren ab und ist nicht genau, also eher eine Annäherung. Ich habe den Wert nach kurzer Recherche aus dem Netz gefischt. Man kann ihn aber auch in der Eingabebox ändern. Als Ergebnis kommt dann folgender Plot heraus, am Ende findet ihr den Code zur freien Verfügung.

import tkinter as tk
from tkinter import messagebox
import matplotlib.pyplot as plt

def get_user_input():
    class InputDialog(tk.Tk):
        def __init__(self):
            super().__init__()
            self.lift()
            self.attributes('-topmost', True)
            self.after_idle(self.attributes, '-topmost', False)
            self.focus_force()

            self.title("ReifenSim")
            self.resizable(False, False)
            self.result = None

            tk.Label(self, text="Jahreskilometer:").grid(row=0, column=0, padx=10, pady=(15, 0), sticky="w")
            self.km_entry = tk.Entry(self)
            self.km_entry.insert(0, "15000")
            self.km_entry.grid(row=0, column=1, pady=(15, 0))

            tk.Label(self, text="Abrieb pro km (mm):").grid(row=1, column=0, padx=10, pady=(10, 0), sticky="w")
            self.abrieb_entry = tk.Entry(self)
            self.abrieb_entry.insert(0, "0.0001")
            self.abrieb_entry.grid(row=1, column=1, pady=(10, 0))

            info = "Hinweis: Der Wert des Abriebs von: 0,0001 mm/km\nist eine Annäherung"
            tk.Label(self, text=info, fg="gray").grid(row=2, column=0, columnspan=2, padx=10, pady=10)

            tk.Button(self, text="OK", width=10, command=self.on_ok).grid(row=3, column=0, columnspan=2, pady=10)

        def on_ok(self):
            try:
                km = int(self.km_entry.get())
                abrieb = float(self.abrieb_entry.get())
                if not (1000 <= km <= 100000):
                    raise ValueError("Jahreskilometer außerhalb des Bereichs (1000-100000)")
                if not (0.00001 <= abrieb <= 1.0):
                    raise ValueError("Abrieb außerhalb des Bereichs (0.000001-1.0)")
                self.result = (km, abrieb)
                self.destroy()
            except Exception as e:
                messagebox.showerror("Fehler", f"Bitte gültige Werte eingeben!\n\n{e}", parent=self)

    dialog = InputDialog()
    dialog.mainloop()
    return dialog.result

class Reifen:
    def __init__(self, profiltiefe_mm=8.0, abrieb_pro_km_mm=0.0001):
        self.profiltiefe = profiltiefe_mm
        self.abrieb_pro_km = abrieb_pro_km_mm
        self.kilometer = 0

    def fahren(self, km):
        max_km_bis_3mm = (self.profiltiefe - 3.0) / self.abrieb_pro_km
        if km > max_km_bis_3mm:
            km = max_km_bis_3mm
            self.profiltiefe = 3.0
            self.kilometer += km
        else:
            self.profiltiefe -= self.abrieb_pro_km * km
            self.kilometer += km
        return km

def simuliere_reifenabrieb(jahreskilometer, abrieb_pro_km):
    MONATE_PRO_JAHR = 12
    MONATSKILOMETER = jahreskilometer / MONATE_PRO_JAHR

    reifen = Reifen(abrieb_pro_km_mm=abrieb_pro_km)
    profiltiefen = [reifen.profiltiefe]
    monate = [0]
    monat = 0

    while reifen.profiltiefe > 3.0:
        reifen.fahren(MONATSKILOMETER)
        monat += 1
        profiltiefen.append(reifen.profiltiefe)
        monate.append(monat)

    jahre = monat / MONATE_PRO_JAHR
    lebensdauer_text = f"Laufzeit: {monat} Monate ≈ {jahre:.2f} Jahre"
    return monate, profiltiefen, reifen.kilometer, lebensdauer_text, jahre

def plot_ergebnis(monate, profiltiefen, jahreskilometer, abrieb_pro_km, ges_km, lebensdauer_text, jahre):
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [3, 1]}, sharex=True)

    manager = plt.get_current_fig_manager()
    manager.set_window_title("ReifenSim")

    # Hauptplot
    ax1.plot(monate, profiltiefen, label='Profiltiefe am Monatsende', linewidth=2)
    for m, p in zip(monate, profiltiefen):
        if m % 3 == 0 or m == monate[-1]:
            ax1.text(m, p + 0.05, f"{p:.2f}", ha='center', va='bottom', fontsize=8, color='dimgray')

    ax1.set_ylabel('Profiltiefe (mm)')
    ax1.set_title(
        f"Reifenabrieb-Simulation\n"
        f"{jahreskilometer} km/Jahr, Abrieb: {abrieb_pro_km} mm/km\n"
        f"Gefahrene Kilometer: {ges_km:.0f}",
        fontsize=14
    )
    ax1.axhline(1.6, color='red', linestyle='--', label='Gesetzliche Mindestprofiltiefe (1,6 mm)')
    ax1.axhline(3.0, color='orange', linestyle=':', label='Empfohlene Mindestprofiltiefe (3,0 mm)')
    ax1.legend()
    ax1.grid(True, linestyle='--', alpha=0.6)
    ax1.text(0.98, 0.98, lebensdauer_text,
             transform=ax1.transAxes,
             fontsize=13, color='navy',
             ha='right', va='top',
             bbox=dict(boxstyle="round,pad=0.4", fc="lightyellow", ec="navy", lw=1))

    if jahre > 10:
        alter_hinweis = "Achtung: Reifen sollten spätestens nach 10 Jahren wegen Alterung gewechselt werden!"
        ax1.text(0.5, 0.10, alter_hinweis,
                 transform=ax1.transAxes,
                 fontsize=13, color='crimson',
                 ha='center', va='center',
                 bbox=dict(boxstyle="round,pad=0.4", fc="mistyrose", ec="crimson", lw=2))
        
    # Subplot
    ax2.bar(monate, profiltiefen, color='cornflowerblue', width=0.8)
    ax2.set_xlabel('Monate')
    ax2.set_ylabel('Profiltiefe (mm)')
    ax2.set_title('Profiltiefe am Monatsende')
    ax2.axhline(1.6, color='red', linestyle='--', label='Gesetzliche Mindestprofiltiefe')
    ax2.axhline(3.0, color='orange', linestyle=':', label='Empfohlene Mindestprofiltiefe')
    ax2.legend()
    ax2.grid(True, linestyle='--', alpha=0.5)

    plt.tight_layout()
    plt.show()

# Hauptprogramm
if __name__ == "__main__":
    werte = get_user_input()
    if werte is None:
        print("Abgebrochen.")
        exit()
    jahreskilometer, abrieb_pro_km = werte
    monate, profiltiefen, ges_km, lebensdauer_text, jahre = simuliere_reifenabrieb(jahreskilometer, abrieb_pro_km)
    plot_ergebnis(monate, profiltiefen, jahreskilometer, abrieb_pro_km, ges_km, lebensdauer_text, jahre)