====== - Solving Advanced Problems with Python ====== **Idee & Ziele:** * Verschiedene anspruchsvolle Probleme aus dem **mathematisch-naturwissenschaftlichen Bereich** mit Python lösen. * Mit **Jupyter-Notebooks** arbeiten ===== - Jupyter-Widgets & Markdown ===== Zeit: 1 Woche 1. Studiere das kurze Tutorial zu [[talit:python_advanced#jupyter_notebook|Jupyter Notebooks]] und **installiere beide Varianten**, also Jupyter im Browser und Jupyter in VSCode. 1. Lade das PDF mit dem [[talit:python_advanced#magic_cheat_sheet|Jupyter-Cheat Sheet]] herunter und verschaffe dir eine grobe Übersicht. 1. Lade das folgende Jupyter-Notebook herunter (zuerst entzippen) und arbeite es durch: {{ :talit:01_intro_widgets.ipynb.zip |}} ===== - Einfache Differentialgleichungen (DGL) ===== Zeit: 1 Woche Erstelle ein Jupyter-NB mit Namen `02_einfache_dgl.ipynb` im entsprechenden Repo. ==== Einführung ==== Bei einer **Gleichung** (hier: quadratische Gleichung) wie z.B. $$x^2 = 20 - x$$ sucht man alle *Zahlen*, die man für die Variable $x$ einsetzen kann, so dann die Gleichung erfüllt ist. Die Gleichung in diesem Beispiel hat zwei Lösungen: $x=-5$ und $x=4$. Eine **Differentialgleichungen (DGL)** ist vom Prinzip her ähnlich. Allerings sucht man als Lösung(en) nicht Zahlen, sondern **Funktionen**, die eine gewisse Bedingung erfüllen. Ein Beispiel wäre: $$y'(x) = \frac{2 \cdot y(x)}{x}$$ Hier suchen wir alle Funktionen $y(x)$, deren Ableitung $y'(x)$ gleich ist wie zwei mal die ursprüngliche Funktion dividiert durch die Variable $x$. Die Lösung von dieser Differentialgleichung ist die Funktion $$y(x) = k \cdot x^2$$ Dabei ist $k$ eine beliebige Zahl. Da wir für $k$ unendlich viele Möglichkeiten haben, gibt es also unendlich viele Lösungen. Zum Schluss wollen wir uns noch vergewissern, dass die gefundene Funktion tatsächlich die DGL löst: $$y'(x) = 2 k x = \frac{2kx^2}{x} = \frac{2 \cdot y(x)}{x}$$ Differentialgleichungen von Hand zu lösen kann sehr anspruchsvoll und oft sogar unmöglich sein. Wie dem auch sei, dies ist nicht das, was wir hier machen wollen - dies wird im PHAM behandelt. Hier geht es um das **numerische** Lösen von Differentialgleichungen, wir überlassen also dem Computer das Lösen. ==== DGL lösen mit odeint ==== === Beispiel === Wir betrachten die Differentialgleichung $$y'(x) = - k y(x)$$ wobei $k$ irgend eine konstante Zahl ist. Wir suchen also eine Funktion $y(x)$, die, wenn man sie ableitet, minus sich selbst mal eine fixe Konstante ergibt. Wir wollen nun diese DGL mithilfe von Python lösen. Dazu verwenden wir den **ODEINT** Solver. ODE steht für Ordinary Differential Equation (Gewöhnliche Differentialgleichungen) und INT für Integrate. ODEs sind Differentialgleichungen, die von *nur einer Variablen*, hier $x$, abhängen. Im Gegensatz dazu hängen Partielle Differentialgleichung von mehreren Variablen ab. Beachte: Es gibt *unendlich viele* Funktionen $y(x)$, die die DGL erfüllen, alle haben aber die gleiche Form. Um eine *eindeutige Lösung* zu erhalten, muss man deshalb eine **Anfangsbedingung** festlegen. Zum Beispiel kann man sagen, dass für $x=0$ der Funktionswert $y=5$ sein soll. Diese Anfangsbedingung muss dem Solver als Argument übergeben werden. Die gesamte Information, die man benötigt, um eine eindeutige Lösung zu finden, sieht dann wie folgt aus: $$y'(x) = - k y(x)$$ $$y(0) = 5$$ Wichtig ist, dass in der DGL die Ableitung alleine auf der linken Seite steht. Links: * Manual ODEINT: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html Lösung mit odeint: from scipy.integrate import odeint import numpy as np import matplotlib.pyplot as plt def dgl(y,x): k = 0.4 y_prime = - k * y return y_prime X = np.linspace(0,20,100) y0 = 15 Y = odeint(dgl,y0,X) plt.plot(X,Y) ==== Lernziele ==== * Wissen, was DGL sind ... * und wozu sie nützlich sind * Wissen, warum DGL Anfangsbedingungen brauchen * DGL mit odeint lösen können * Mit matplotlib ansprechende Figuren erstellen können ==== Aufgaben B ==== === Aufgabe B1: Beispiel oben === == Part I == 1. Gehe das Beispiel oben Schritt für Schritt durch, stelle sicher, dass du alles verstehst. Studiere dazu z.B. das Manual von ODEINT. 1. Um was für eine Funktion $y(x)$ handelt es sich hier? 1. Spiele mit dem Wert für den Parameter $k$. Welchen Einfluss hat dieser auf die Funktion? 1. Was kann im real life durch eine solche Funktion beschrieben werden? Mache ein Beispiel == Part II == 1. Verschönere die Figur, unter anderem sollst du folgendes machen: 1. Achsen beschriften 1. Kurve dicker und andere Farbe 1. Legende hinzufügen 1. Titel hinzufügen 1. Nun soll die DGL für verschiedene Werte für $k$ gelöst werden. Finde dazu einen Weg, wie dem odeint-Solver ein zusätzliches Argument übergeben werden kann. Visualisiere dann alle Funktionsgraphen in einer Figur und beschrifte die Graphen in der Legende. == Part III == Mache eine Kopie des Codes und füge einen Slider ein, mit dem man den Wert von $k$ einstellen kann. === Aufgabe B2 === 1. Löse die Differentialgleichung $$f'(x) = a$$ mit odeint, wobei $a$ ein beliebiger Parameter ist. 1. Stelle die Funktion $f(x)$ graphisch dar. 1. Um was für eine Funktion handelt es sich bei $f(x)$? === Aufgabe B3 === 1. Löse die DGL vom Theorieteil am Anfang des Kapitels: $$f'(x) = \frac{2 \cdot f(x)}{x}$$ 1. Stelle die Lösung graphisch dar und vergleiche sie mit der exakten Lösung $f(x) = k x^2$. === Aufgabe B4: Matplotlib === Finde heraus, wie du die Darstellung deiner Figur verändern kannst, z.B.: 1. Grösse der Figur 1. Farbe und Liniendicke von Graph 1. Hintergrundfarbe 1. Ticks auf Achsen 1. Beschriftungen: Titel, Achsen, Graph, Legende 1. ... ===== - Bewegungsgleichungen ===== Zeit: 1 Woche In der Physik beschreibt man die Bewegungen von Objekten (Beispiele: Stein fällt nach unten, Masse an Feder die oszilliert, ...) durch sogenannte **Bewegungsgleichungen**, welche **Differentialgleichungen (DGL)** sind. ==== Theorie ==== === Position, Geschwindigkeit & Beschleunigung === Dabei ist es wichtig zu wissen, wie Position, Geschwindigkeit und Beschleunigung zusammenhängen: Sei $x(t)$ die **Position** eines Objekts als Funktion der Zeit. Die **Geschwindigkeit** des Objekts zu jedem Zeitpunkt ist dann gegeben durch die *Ableitung der Position*: $$v(t) = x'(t)$$ Die Geschwindigkeit gibt also an, wie fest sich *die Position mit der Zeit verändert*. Ganz analog ist die **Beschleunigung** die *Änderung der Geschwindigkeit* und ist deshalb gegeben durch $$a(t) = v'(t) = x''(t)$$ Die Beschleunigung ist also die *zweite Ableitung* der Position. == Harmonischer Oszillator == Aus dem Physikunterricht kennst du sicher das **Hookesche Gesetz**: $$ F = - k x $$ Es beschreibt die Bewegung eines **Federpendels ohne Reibung**. Das Hookesche Gesetz gibt uns die Federkraft $F$ eines Federpendels an. Es besagt, dass die Federkraft gegeben ist durch minus die Federkonstante $k$ mal die Auslenkung der Feder aus ihrer Gleichgewichtsposition. Das Minus sagt, dass die Kraft immer *gegen* der Auslenkung wirkt: Ist die Feder gedehnt, so will die Kraft die Feder zusammendrücken. Ist sie aber zusammengedrückt, will die Kraft sie wieder auseinander ziehen. Das Hookesche Gesetz können wir nun als *Differentialgleichung* schreiben. Die Beschleunigung der Feder $a$ und die Federkraft $F$ hängen über das **zweite Newtonsche Gesetz** zusammen: $F = m a$, wobei $m$ die Masse des Objekts ist, welches an der Feder befestigt ist. Damit können wir das Hookesche Gesetz wie folgt umschreiben: $$F = - k x$$ $$m a = - k x$$ $$m x''(t) = - k x(t)$$ $$x''(t) = - \frac{k}{m} x(t)$$ Wir haben nun also wieder eine Differenzialgleichung. Im Gegensatz zu den DGL von der letzten Übung enthält diese aber eine *zweite Ableitung*. Zur Erinnerung: Die DGL von der letzten Übung, welche erste Ableitungen beinhaltet haben, hatten immer *unendlich* viele Lösungen. Wir mussten deshalb *eine Randbedingung* festlegen. Löst man nun die neue DGL (Hook'sches Gesetz), so erhält man die Position der Feder als Funktion der Zeit $x(t)$. Hier gibt es wieder unendlich viele Möglichkeiten. Da die neue DGL eine *zweite Ableitung beinhaltet*, muss man nun **zwei Randbedingungen** angeben - eine für $x(t)$ und eine für deren Ableitung $x'(t)$, z.B.: $$x(0) = x_0$$ $$x'(0) = 0$$ Man muss also einen Zeitpunkt (typischerweise den Anfang) wählen und sagen *wo* und *wie schnell* das Federpendel dann ist. Wir legen also fest, dass die Feder zum anfänglichen Zeitpunkt $t=0$ an der Position $x_0$ ist und die Geschwindigkeit dann $=0$ ist. Dies macht Sinn, wenn man von Hand eine Feder spannt und zum Zeitpunkt $t=0$ los lässt. Diese Differentialgleichung kann man analytisch, also exakt, lösen. Mit den obigen Randbedingungen erhält man $$x(t) = x_0 \cos\left(\sqrt{\frac{k}{m}} \cdot t\right)$$ Ein System, welches so schwingt, nennt man einen **harmonischen Oszillator** und seine Bewegung eine **harmonische Bewegung**. Diese Bewegung ist *ungedämpft*, das Federpendel schwingt also unendlich lange. == DGL mit 2. Ableitungen lösen == Die DGL für den Harmonischen Oszillator enthält eine zweite Ableitung. Wie können wir solche mit **odeint** lösen? Der Trick ist folgender: Wandle die eine DGL mit einer zweiten Ableitung um in **zwei DGL mit nur ersten Ableitungen**: Anstelle von $$x''(t) = - \frac{k}{m} x(t)$$ betrachten wir $$x'(t) = v(t)$$ $$v'(t) = - \frac{k}{m} x(t)$$ Nun können wir diese beiden DGL zusammen mit odeint lösen. Die Funktion, die odeint übergeben wird, sieht dann wie folgt aus: def harmonic(x_v,t,k,m): x = x_v[0] v = x_v[1] return [v, -k/m * x] Beachte, dass *x_v* eine Liste ist. Das erste Element beinhaltet die Position $x$, das zweite die Geschwindigkeit $v$ (also die Ableitung von $x$). == Gedämpfter harmonischer Oszillator == Der harmonische Oszillator ist gut und schön, in Realität tritt aber meist Reibung auf, welche die Bewegung dämpft. Die Amplitude (Auslenkung) eines Federpendels nimmt aufgrund von Reibungskräften wie Luftwiderstand kontinuierlich ab. Wir können in unserer DGL nun Reibung ganz einfach einbauen, in dem wir einen entsprechende **Kraftterm in unsere DGL einbauen**. Zum Beispiel hängt der Luftwiderstand von der Geschwindigkeit ab: Je höher die Geschwindigkeit, desto grösser der Luftwiderstand. Wir führen deshalb einen Reibungsterm ein, der *linear* ist in der Geschwindigkeit: $$F = - k x - c v$$ Dabei ist $c$ eine Konstante, die uns angibt, wie stark der Luftwiderstand ist. ==== Aufgaben C ==== Erstelle ein Jupyter-NB mit Namen `03_bewegungsgleichungen.ipynb` im entsprechenden Repo. === Aufgabe C1: Harmonischer Oszillator === 1. Beweise, dass $x(t) = x\_0 \cos\left(\sqrt{\frac{k}{m}} \cdot t\right)$ eine Lösung des harmonischen Oszillators ist: Setze einfach in die DGL ein und zeige, dass links und rechts das Gleiche herauskommt. 2. Simuliere nun einen harmonischen Oszillator $x''(t) = - \frac{k}{m} x(t)$ wie ein Federpendel ohne Reibung, in dem du die DGL mit odeint löst. 3. Stelle die Lösungsfunktion $x(t)$ dann mit matplotlib schön graphisch dar. 4. Vergleiche nun deine numerische Lösung mit der analytischen Lösung $x(t) = x_0 \cos\left(\sqrt{\frac{k}{m}} \cdot t\right)$ und stelle sicher, dass diese übereinstimmen. === Aufgabe C2: Gedämpfter harmonischer Oszillator === 1. Simuliere nun den gedämpften harmonischen Oszillator mit einem Reibungsterm, der linear von der Geschwindigkeit abhängt. 1. Füge nun Slider (widgets) hinzu, mit denen du die Werte für die Konstanten $k,m,c$, die Anfangsposition $x_0$, sowie die Zeit-Range einstellen kannst. Sobald ein Slider geändert wurde, soll der Graph entsprechend angepasst werden. Siehe Tipps unten. Tipps für Widgets: * Verwende `widgets.interactive(update_plot,param1,param2,...)` * definiere Funktion `update_plot(param1,param2,...)`, die Lösung der DGL und Darstellung der Lösung enthält * `param1` usw. sind die Parameter, die mit Widgets (z.B. Slider) eingestellt werden ===== - Das Zweikörperproblem ===== Zeit: 2 Wochen Gegeben sind zwei Massen $m_1$ und $m_2$ mit ... * Anfangsposition $\vec{r}_1(t=0)$ und $\vec{r}_2(t=0)$ * Anfangsgeschwindigkeit $\vec{v}_1(t=0)$ und $\vec{v}_2(t=0)$ {{:talit:two_body_problem.png?400|}} ++++Code Graph| \begin{tikzpicture}[scale=1,cap=round,>=latex] \pgfmathsetmacro{\Rone}{0.5} \pgfmathsetmacro{\Rtwo}{0.75} \pgfmathsetmacro{\F}{3} \pgfmathsetmacro{\d}{10} \draw[very thick,fill=blue!50] (7,-2) circle(0) node[below] {$0$}; \draw[very thick,fill=blue!50] (0,0) circle(\Rone); \draw[very thick,fill=yellow!50] (8,2) circle(\Rtwo); \draw[black,fill=black] (0,\Rone) node[above] {$m_1$}; \draw[black,fill=black] (8,2+\Rtwo) node[above] {$m_2$}; \draw[->,ultra thick,cyan] (0,0) -- (8,2) node[above,midway] {$\vec{r}_{12}$}; \draw[->,ultra thick,magenta,dashed] (0,0) -- ++(2,0.5) node[above,pos=0.8] {$\vec{F}_{12}$}; \draw[->,ultra thick,magenta,dashed] (8,2) -- ++(-2,-0.5) node[above,pos=0.8] {$\vec{F}_{21}$}; \draw[->,ultra thick] (7,-2) -- (0,0) node[below,midway] {$\vec{r}_1$}; \draw[->,ultra thick] (7,-2) -- (8,2) node[right,midway] {$\vec{r}_2$}; \draw[->,ultra thick] (7,-2) -- (5,1.25) node[right,pos=0.6] {$\vec{R}$}; \draw[very thick,fill=black] (5,1.25) circle(0.07); \end{tikzpicture} ++++ **Ziel:** Bestimme Position der beiden Massen zu jedem Zeitpunkt, also $\vec{r}_1(t) = ?$ und $\vec{r}_2(t) = ?$ ==== Die Differenzialgleichungen ==== Die beiden Massen ziehen sich natürlich gegenseitig mit der Gravitationskraft an: $$F_G = G \frac{m_1 \cdot m_2}{d^2}$$ wobei $d$ der Abstand zwischen den beiden Massen ist. Da eine Kraft sowohl eine Stärke als auch eine Richtung hat, wird sie als *Vektor* dargestellt. Die Formel oben, die du bereits aus dem GF Physik kennst, gibt allerdings nur die Stärke an. Wir brauchen aber die *Vektor-Version*: Die Gravitationskraft, die wegen $m_2$ auf $m_1$ wirkt, ist: $$\vec{F}_{12} = G \frac{m_1 \cdot m_2}{|\vec{r}_{12}|^3} \vec{r}_{12}$$ wobei $\vec{r}_{12} = \vec{r}_2 - \vec{r}_1$ der *Verschiebungsvektor* ist, der von $m_1$ zu $m_2$ zeigt. Damit ergibt sich: $$\vec{F}_{12} = G \frac{m_1 \cdot m_2}{|\vec{r}_2 - \vec{r}_1|^3} (\vec{r}_2 - \vec{r}_1)$$ Wahrscheinlich fragst du dich, woher das "hoch 3" kommt! Dies macht Sinn, wenn man die Stärke dieses Vektors bestimmt: $$|\vec{F}_{12}| = G \frac{m_1 \cdot m_2}{|\vec{r}_{12}|^3} |\vec{r}_{12}| = G \frac{m_1 \cdot m_2}{|\vec{r}_{12}|^2}$$ Die Differenzialgleichungen (DGL), welche die Position der Masse $m_1$ bestimmt ist dann wie immer durch das **2. Newtonsche Gesetz** gegeben: $$m_1 \, \ddot{\vec{r}}_1 = \vec{F}_{12} = G \frac{m_1 \cdot m_2}{|\vec{r}_2 - \vec{r}_1|^3} (\vec{r}_2 - \vec{r}_1)$$ und gleichermassen für die zweite Masse: $$m_2 \, \ddot{\vec{r}}_2 = \vec{F}_{21} = G \frac{m_1 \cdot m_2}{|\vec{r}_1 - \vec{r}_2|^3} (\vec{r}_1 - \vec{r}_2)$$ In einem Zweikörpersystem bewegen sich die beiden Massen *immer in einer Ebene*. Wir können daher das Problem in 2D anstelle 3D betrachten. Die beiden Positionsvektoren haben dann also je eine $x-$ und $y-$ Komponente: $$\vec{r}_1 = \begin{pmatrix} x_1 \\ y_1 \end{pmatrix} \,, \quad \vec{r}_2 = \begin{pmatrix} x_2 \\ y_2 \end{pmatrix}$$ Die DGL sind dann also: $$m_1 \begin{pmatrix} \ddot{x}_1 \\ \ddot{y}_1 \end{pmatrix} = G \frac{m_1 \cdot m_2}{d^3} \begin{pmatrix} x_2 - x_1 \\ y_2 - y_1 \end{pmatrix}$$ $$m_2 \begin{pmatrix} \ddot{x}_2 \\ \ddot{y}_2 \end{pmatrix} = G \frac{m_1 \cdot m_2}{d^3} \begin{pmatrix} x_1 - x_2 \\ y_1 - y_2 \end{pmatrix}$$ $$d = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}$$ Beachte, dass es sich um zwei Vektor-DGL handelt, es sind insgesamt also $4$ DGL: $$\ddot{x}_1 = \frac{G \cdot m_2}{d^3} (x_2 - x_1)$$ $$\ddot{y}_1 = \frac{G \cdot m_2}{d^3} (y_2 - y_1)$$ $$\ddot{x}_2 = \frac{G \cdot m_1}{d^3} (x_1 - x_2)$$ $$\ddot{y}_2 = \frac{G \cdot m_1}{d^3} (y_1 - y_2)$$ **Löst man nun diese DGL** (z.B. mit odeint), so erhält man die Position der beiden Massen. ==== Der Schwerpunkt ==== Der Schwerpunkt (barycenter) der beiden Massen ist gegeben durch: $$\vec{R} = \frac{m_1\,\vec{r}_1 + m_2\,\vec{r}_2}{m_1 + m_2}$$ Wie oben besprochen geht es im Zweikörperproblem darum, die zu DGL lösen, welche durch das **2. Newtonsche Gesetz** gegeben sind: $$m_1\,\ddot{\vec{r}}_1 = \vec{F_{12}} = \text{Kraft, die Masse 2 auf Masse 1 ausübt}$$ $$m_2\,\ddot{\vec{r}}_2 = \vec{F_{21}} = \text{Kraft, die Masse 1 auf Masse 2 ausübt}$$ Dase **3. Newtonsche Gesetz** besagt, dass $$\vec{F_1} = - \vec{F_2}$$ und damit $$\vec{F_1} + \vec{F_2} = 0$$ Es gilt also: $$m_1\,\ddot{\vec{r}}_1 + m_2\,\ddot{\vec{r}}_2 = 0 \quad | \quad :(m_1+m_2)$$ $$\frac{m_1\,\ddot{\vec{r}}_1 + m_2\,\ddot{\vec{r}}_2}{m_1 + m_2} = 0$$ $$\ddot{\vec{R}} = 0$$ Das bedeutet also, dass der *Schwerpunkt* des 2-Körpersystems *nicht beschleunigt* ist. Seine Geschwindigkeit $\vec{v} = \dot{\vec{R}}$ ist also *konstant*. Wenn man die Lösung des Zweikörperproblems graphisch darstellt, so macht es Sinn, dafür ein **Bezugssystem** zu wählen, in welchem der sich der *Schwerpunkt in Ruhe*. Dafür geht man wie folgt vor: 1. **Löse die DGL**, welche für die beiden Massen durch die Newtonsche Gravitationskraft gegeben sind, ganz normal mit odeint. 2. Berechne dann mithilfe der erhaltenen Lösung für jeden Zeitpunkt den **Schwerpunkt** des Zweikörpersystems. 3. **Subtrahiere** den Schwerpunkt von den Positionen der beiden Massen. 4. **Plotte** die Bahnen der beiden Massen. Du solltest jetzt sehen, dass sie sich auf **Ellipsen** bewegen. ==== Aufgaben D ==== 1. **Simuliere die Umlaufbahnen** zweier Planeten im Zweikörpersystem. Rechne den Schwerpunkt aus den $x$- und $y$-Koordinaten heraus. Verwende folgende Werte: 1. $G=1$ 1. Massen: z.B. in Grössenordnung $1-10$. 1. Startposition: ebenfalls $1-10$. 1. Anfangsgeschwindigkeit eher $0.05-0.5$ 1. Finde dann durch ausprobieren möglichst **unterschiedliche Orbits**. U.a. sollen die folgenden reproduziert werden: {{:talit:two_body_problem_orbits.png?400|}} 1. **Zweikörpersimulator mit Widgets:** Füge einige Widgets ein, mit denen man den Orbit festlegen kann. Sinn würde z.B. folgendes machen: 1. Massen m1 und m2 1. Distanz $d$: m1 hat dann Anfangsposition (-d/2,0) und m2 hat dann Anfangsposition (d/2,0). 1. Anfangsgeschwindigkeiten der beiden, aber immer in $y$-Richtung. 1. **(Optional) Animation:** Umlaufbahnen sollen animiert dargestellt werden ===== - Game Of Life ===== ==== Version 1 ==== Zeit: 1 Woche * Studiere nur [[https://de.wikipedia.org/wiki/Conways_Spiel_des_Lebens#Die_Spielregeln|Spielregeln]] * Implementiere das Game of Life mit Python (normal oder Jupyter) * Wie du es visualisierst, ist dir überlassen: Konsole, PyGame, PyQt, Jupyter, ... * Programmiere **ganz alleine** (keine Inspiration von Google, weiter unten auf Wiki, ...) ==== Version 2 ==== Zeit: 1 Woche ++++Vorgaben Version v02| * Mache eine Kopie von v01 oder starte neu und gebe einen passenden Namen, z.B. `game_of_life_v02.py` (manchmal einfacher: neues File, dann Code Stück für Stück hinein kopieren) * Modell überarbeiten / neu schreiben * Die Anzeige soll in **Konsole** gemacht werden (PyGame o.ä. später, Idee ist, dass man dann das *genau gleiche* Modell auch für PyGame oder andere GUI-Libraryverwenden kann) * muss **komplett objektorientiert** sein (siehe unten empfohlene Klassen) * **unendlich grosse Welt:** Zellen können ausserhalb des auf Bildschirm angezeigten Bereichs leben * speichere nur die lebendigen Zellen, die toten werden aus Liste entfernt * **Model und View komplett getrennt**. * Bitte ausreichend **Kommentieren** ++++ ++++Theorie Model vs. View| * Ist ein **Programmierparadigma** ("Programmier-Philosophie") * **Model:** Abbildung des Game-States im Code (Objekte, Listen, ...) * **View:** kümmert sich um alles was mit Input und Output zu tun hat * Output: Anzeige des Game-States (z.B. Console-View, PyGame, ...) * Input: User-Eingaben verarbeiten, hier wahrscheinlich irrelevant * Die beiden sollten *strikt getrennt sein* * Vorteil: Kann Modell 1x programmieren und dann in verschiedenen Views verwenden, z.B. Console-App, PyGame, Webgame, ... ++++ ++++Empfohlene Klassen| # MODELL """ Die ersten beiden Klassen bilden das Modell und haben nichts mit der Darstellung zu tun. Deshalb dürfen diese KEINE print, PyGame usw. Befehle beinhalten. """ class World: """ Create only one object of this class. Contains list with all cells that are alive, updated them, ... """ def __init__(self,...): self.cells = [] # saves all cells that are ALIVE ... def update(self): pass .... class Cell: """ describes a single cell """ def __init__(self,...): self.x = ... # x coordinate self.y = ... # y coordinate self.is_alive = ... # is currently alive self.stays_alive = ... # will still be alive in next round self.neighbours = [] .... # VIEW """ Die Klasse(n) hier ist für die Darstellung zuständig. """ class ConsoleApp: # or PyGameApp pass # EXECUTE PROGRAM """ Könnte etwa wie folgt aussehen: """ if __name__ == "__main__": world = World("static 1") # erstelle Welt mit vordefiniertem Preset, weitere Möglichkeiten: "glider 1", "random", ... console_app = ConsoleApp(world) # erstelle eine Console App (oder PyGame, ...) console_app.animate() # führe aus ++++ ==== Version 3 ==== Zeit: 1 Woche **Ziel:** Identischen Model-Code von V2 verwenden für mind. zwei verschiedene Views: Console (aus V2) und GUI (z.B. PyGame/PyQT/Matplotlib) 1. Verbessere falls nötig Code von V2: **Model- und View-Code müssen strikt getrennt sein** und alles **muss strikt objektorientiert programmiert** sein. 1. Strukturiere nun Code um. Je ein **eigenes File für**: 1. Ganzen Model-Code, z.B. `game_of_life_model.py` 1. Code für Console-View, z.B. `game_of_life_console.py` 1. Code für GUI-View, z.B. `game_of_life_pygame.py` 1. Importiere in den beiden Files für die View den Model-Code, z.B. `from game_of_life_model import *` 1. Programmiere nun die beiden Views aus. 1. Optional: Coole Features einbauen ===== - Winter Wonderland Christmas Game ===== Erstelle im Eiltempo (2L und ein bisschen zuhause) ein einfaches PyGame-Game, welches an Moorhuhnjagd erinnern soll: * Statisches Hintergrundbild mit Winterlandschaft * Es werden zufällig Schneeflocken am oberen Rand erzeugt, die in unterschiedlichen Tempi herunter fallen. * Es werden zufällig Geschenke am oberen Rand erzeugt, die in unterschiedlichen Tempi herunter fallen. * Durch Mausklick soll man diese einfangen können: Verschwinden bei Klick, Score erhöht sich um 1. Weitere optionale Idee: * Verschiedene Geschenke, gewisse Typen beinhalten Bomben -> Abzug oder Leben verlieren bis Game Over * Santa der mit Schlitten quer durch den Bildschirm reitet * Power-Ups * Animationen, z.B. wenn Bombe explodiert, ... * Musik & SFX * Eigene Ideen, sei kreativ! Anforderungen Programmieren: * Muss strikt **objektorientiert** sein. Mache z.B. Klasse `Item` sowohl für Geschenke als auch Schneeflocken. Allerdings sollen nur die Geschenke einsammelbar sein. Mache dazu z.B. eine Flag `is_collectable`, welche `True` für Geschenke und `False` für Schneeflocken ist. * Tipp: Starte mit **Template** von [[python_pygame#template|hier]]. * Verwende z.B. diese Bilder oder finde/erzeuge eigene: {{:talit:winterwonderland.png?200|}} {{:talit:snowflake.png?100|}} {{:talit:present.png?100|}} ===== - Flask-Tutorial ===== Erstelle mit Flask eine einfache Website mit folgenden Features: 1. **Clicker:** Programmiere (nicht serverseitig) einen Counter. Jedes Mal wenn ein Button gedrückt wird, erhöht sich dieser um 1. 1. **Zufallszahl:** Auf Knopfdruck wird eine Zufallszahl angezeigt. Diese wird serverseitig erzeugt. 1. **Encryption:** In Textfeld kann Text eingegeben werden. Dieser wird in real time (also direkt mit Eingabe) serverseitig verschlüsselt. Verwende dazu einen Algorithmus, denn du im Grundlagenfach bereits programmiert hast (Cäsar, Vigenere, ...). Der verschlüsselte Text wird dann immer gleich angezeigt. 1. **Automatisierter Counter** oder automatisierte Zufallszahl: Jede Sekunde soll der Wert neu gesetzt werden. Syntax: async function loop() { while (true) { // await new Promise(resolve => setTimeout(resolve, 1000)); // Sleep for 1 second (1000 milliseconds) // } } loop() Nützliche Links: * [[talit:flask_webserver]] * [[gf_informatik:web_sca:websites]] * [[informatik:website_with_js]] * [[informatik:javascript_cheat_sheet]] ===== - Web Game of Life mit Flask ===== * Programmiere eine Web-Version des Game of Life mit Flask. * Verwende dazu dein bereits vorhandenes Game of Life Model. * Jede Sekunde (oder so) soll das Frontend (JS) den Zustand der nächsten Iteration vom Backend (Flask) verlangen. * Sobald das Frontend diesen (in Form eines Dicts) erhalten hat, soll diese angezeigt werden -> View * Tipp: Für die View kann man z.B. ein [[gf_informatik:web_sca:websites#optionale_theorieresponsive_layout_mit_grid|Grid]] verwenden.