Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| lehrkraefte:blc:informatik:ffprg1-2018:ffprg1-2018 [2018/06/12 12:42] – [Dienstag 5. 6. 2018] Ivo Blöchliger | lehrkraefte:blc:informatik:ffprg1-2018:ffprg1-2018 [2018/06/26 15:51] (current) – Ivo Blöchliger | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Freifach Einführung ins Programmieren ====== | ||
| + | |||
| + | Direkt-Link: | ||
| + | |||
| + | * Jeweils Dienstags, 16:25 - 17:58 im H21. | ||
| + | * Nach Möglichkeit soll ein eigener Laptop mitgebracht werden. | ||
| + | * Wir programmieren mit TigerJython. | ||
| + | |||
| + | ===== Programmier-Projekt ===== | ||
| + | Wer programmiert die schlauste Schlange? {{ : | ||
| + | |||
| + | Version 0.1 (vom 8.3.2017): {{ : | ||
| + | |||
| + | === Anleitung === | ||
| + | * Archiv entpacken (wirklich entpacken, nur weil Windows die Dateien im Archiv anzeigt, sind diese deswegen noch nicht entpackt). | ||
| + | * game.py in Tigerjython starten. | ||
| + | * Für eigene Strategie ivo_beispiel_strategy.py kopieren und wie folgt benennen: name_xyz_strategie.py, | ||
| + | * In game.py: | ||
| + | * Die eigene Strategie importen (ganz oben im Code) | ||
| + | * Eigene Strategie ganz unten in game.py eintragen. | ||
| + | * Code studieren, Fragen stellen. | ||
| + | |||
| + | |||
| + | === Dokumentation === | ||
| + | Entweder direkt Kommentare im Code oder [[https:// | ||
| + | |||
| + | ===== TigerJython ===== | ||
| + | * Lehrgang: http:// | ||
| + | * Download der Programmierumgebung: | ||
| + | |||
| + | ===== Programm ===== | ||
| + | ==== Dienstag 19. 6. 2018 ==== | ||
| + | === Vorschlag 1 === | ||
| + | Einblick in die " | ||
| + | |||
| + | Problem: Bestimme die durchschnittliche Anzahl Versuche beim Zahlenratespiel, | ||
| + | |||
| + | === Vorschlag 2 === | ||
| + | TicTacToe: | ||
| + | * Gegeben ein 3x3-Array mit Einträgen 0, 1 oder 2 (leer, x, o). | ||
| + | |||
| + | <code python tictactoe.py> | ||
| + | from gpanel import * | ||
| + | |||
| + | # leeres Feld | ||
| + | dim = 10 # Anzahl Felder | ||
| + | winlen = 5 # Anzahl fuer Gewinn | ||
| + | feld = [[0 for y in range(dim)] for x in range(dim)] | ||
| + | |||
| + | player = 1 | ||
| + | filled = 0 | ||
| + | winner = 0 | ||
| + | |||
| + | # Startpunkt x,y | ||
| + | # Vektor vx,vy | ||
| + | def testLine(x, | ||
| + | global winlen,dim | ||
| + | what = feld[x][y] | ||
| + | count = 1 if what>0 else 0 | ||
| + | while True: | ||
| + | x+=vx | ||
| + | y+=vy | ||
| + | if x<0 or y<0 or x>=dim or y>=dim: | ||
| + | break | ||
| + | if feld[x][y]==what: | ||
| + | count+=1 | ||
| + | else: | ||
| + | what = feld[x][y] | ||
| + | count=1 if what>0 else 0 | ||
| + | if feld[x][y]==0: | ||
| + | count = 0 | ||
| + | if count==winlen: | ||
| + | return what | ||
| + | return 0 | ||
| + | | ||
| + | def win(feld): | ||
| + | for x in range(dim): | ||
| + | combos = [[x, | ||
| + | [0,x,1,0],\ | ||
| + | [0,x,1,1],\ | ||
| + | [x,0,1,1],\ | ||
| + | [x, | ||
| + | [0,x,1,-1]] | ||
| + | for combo in combos: | ||
| + | winner = testLine(combo[0], | ||
| + | combo[2], combo[3], | ||
| + | if winner> | ||
| + | return winner | ||
| + | return 0 | ||
| + | |||
| + | |||
| + | def ausgabe(feld): | ||
| + | clear() | ||
| + | dim = len(feld) | ||
| + | for i in range(1, | ||
| + | line(0, | ||
| + | line(i, | ||
| + | for x in range(dim): | ||
| + | for y in range(dim): | ||
| + | if (feld[x][y]==1): | ||
| + | line(x+0.1, y+0.1, x+0.9, | ||
| + | line(x+0.1, y+0.9, x+0.9, | ||
| + | elif (feld[x][y]==2): | ||
| + | move(x+0.5, y+0.5) | ||
| + | circle(0.4) | ||
| + | | ||
| + | def onMousePressed(x, | ||
| + | global feld | ||
| + | global player | ||
| + | global filled | ||
| + | global winner | ||
| + | global dim | ||
| + | x = int(x) | ||
| + | y = int(y) | ||
| + | if winner>0 or filled == dim*dim: | ||
| + | feld = [[0 for y in range(dim)] for x in range(dim)] | ||
| + | player = 1 | ||
| + | filled = 0 | ||
| + | winner = 0 | ||
| + | elif feld[x][y]==0: | ||
| + | filled+=1 | ||
| + | feld[x][y] = player | ||
| + | player = 3-player | ||
| + | ausgabe(feld) | ||
| + | winner = win(feld) | ||
| + | if (winner> | ||
| + | print(" | ||
| + | elif (filled == dim*dim): | ||
| + | print(" | ||
| + | |||
| + | makeGPanel(0, | ||
| + | lineWidth(5) | ||
| + | ausgabe(feld) | ||
| + | |||
| + | </ | ||
| + | * Gegeben ein 3x3-Array wie oben. Stellen Sie fest, ob schon jemand gewonnen hat. | ||
| + | * Programmieren Sie ein Spiel für 2 menschliche Spieler, die abwechslungsweise die Koordinaten eingeben. Die Eingabe soll überprüft werden. | ||
| + | * Programmieren Sie ein Strategie, die gegen einen menschlichen Spieler spielt. | ||
| + | ==== Dienstag 12. 6. 2018 ==== | ||
| + | Zahlenraten: | ||
| + | |||
| + | Eingabe einer Zahl: ([[http:// | ||
| + | <code python> | ||
| + | zahl = inputInt(" | ||
| + | </ | ||
| + | |||
| + | Generierung einer Zufallszahl: | ||
| + | <code python> | ||
| + | from random import randint | ||
| + | |||
| + | zufall = randint(0, | ||
| + | </ | ||
| + | |||
| + | <hidden Lösungsvorschlag> | ||
| + | <code python zahlenraten.rb> | ||
| + | from random import randint | ||
| + | |||
| + | debug = True | ||
| + | |||
| + | mini = 0 | ||
| + | maxi = 100 | ||
| + | zahl = randint(mini, | ||
| + | if debug: | ||
| + | print(zahl) | ||
| + | |||
| + | versuche = 1 | ||
| + | while True: | ||
| + | msg = " | ||
| + | tip = inputInt(msg) | ||
| + | if zahl < tip: | ||
| + | maxi = tip | ||
| + | elif zahl > tip: | ||
| + | mini = tip | ||
| + | else: | ||
| + | msgDlg(" | ||
| + | break | ||
| + | versuche+=1 | ||
| + | | ||
| + | </ | ||
| + | ==== Snake ==== | ||
| + | |||
| + | |||
| + | Implementation Bestimmung des kürzesten Wegs. Statische Variante. | ||
| + | |||
| + | Vorgehen: | ||
| + | * Doppeltes Array mit -1 initialisieren für Distanzen (für jedes feld[x][y]). -1 heisst dann unerreichbares Feld | ||
| + | * Todo-Liste: Array mit Koordinatenpaaren. | ||
| + | * Apfel-Feld mit 0 initialisieren, | ||
| + | * Solange Todo-Liste nicht leer ist: | ||
| + | * Koordinatenpaar vorne von Todo-Liste entfernen -> p. | ||
| + | * Nachbarn von p " | ||
| + | |||
| + | Richtungsauswahl: | ||
| + | * Wenn ein Nachbarfeld vom Schlangenkopf mit Distanz >= 0 existiert, zum Feld mit kleinstmöglicher Distanz gehen. | ||
| + | * Sonst sich eine Überlebensstrategie ausdenken. | ||
| + | |||
| + | |||
| + | Variante: Vom Schlangenkopf aus die Distanzen bestimmen. Der kürzeste Weg zum Apfel muss dann allerdings von dort rückwärts gesucht werden. Dafür können dann Felder miteinbezogen werden, die wieder verschwinden. | ||
| + | |||
| + | |||
| + | ==== Dienstag 5. 6. 2018 ==== | ||
| + | Ausfall der Stunden | ||
| + | ==== Dienstag 29. 5. 2018 ==== | ||
| + | Ziele: | ||
| + | * Strategie mit Skalarprodukt von der letzten Stunde verstehen. | ||
| + | * Zusatzfunktion erstellen, die die Möglichkeit einer Kopf-an-Kopf-Kollision erkennt und diese Richtung(en) vermeidet. | ||
| + | * Weitere Verbesserungen, | ||
| + | |||
| + | <code python> | ||
| + | def kopfankopfok(self, | ||
| + | x,y = self.params.x, | ||
| + | for schlange in self.params.heads: | ||
| + | # schlange ist hier ein Array mit 2 Einträgen | ||
| + | if abs(x-schlange[0])+abs(y-schlange[1])> | ||
| + | if abs(a-schlange[0])+abs(b-schlange[1])==1: | ||
| + | return False # Achtung Gefahr | ||
| + | return True # Keine Gefahr | ||
| + | |||
| + | </ | ||
| + | ==== Dienstag 22. 5. 2018 ==== | ||
| + | Ziel: Beispielstrategie für Snake kopieren und verbessern, dass die Äpfel direkt gefressen werden, zumindest, wenn sich der Schlangenkopf auf der gleichen $x$ oder $y$-Koordinate wie der Apfel befindet. | ||
| + | |||
| + | Variante: Sei $P$ die Position der Schlange, $A$ die position des Kopfs und $\vec{v}_0$ bis $\vec{v}_3$ die 4 Richtungsvektoren. Das Skalarprodukt zwischen dem Vektor $\vec{PA}$ und einem Richtungsvektor ist dann positiv, wenn sich die Schlange so dem Apfel nähert. | ||
| + | |||
| + | Es gilt $\vec{u} \cdot \vec{v} = |\vec{u}| \cdot |\vec{v}| \cdot \cos(\alpha)$ mit $\alpha = \angle(\vec{u}, | ||
| + | |||
| + | |||
| + | <code python> | ||
| + | # Aktuelle Position ermitteln | ||
| + | x,y = self.params.x, | ||
| + | # print(" | ||
| + | | ||
| + | # Koordinaten vom Apfel | ||
| + | apfx = self.params.apple[0] | ||
| + | apfy = self.params.apple[1] | ||
| + | | ||
| + | # Richtungsvektoren durchlaufen | ||
| + | for i in range(4): | ||
| + | # Prüfen, ob nicht Gegenrichtung von d, egal, ist eh besetzt | ||
| + | # Komponenten vom Richtungsvektor | ||
| + | vx = Board.VECS[i][0] | ||
| + | vy = Board.VECS[i][1] | ||
| + | # Skalarprodukt berechnen, wenn positiv und moeglich, dann machen. | ||
| + | s = vx*(apfx-x)+vy*(apfy-y) | ||
| + | # Neue Position | ||
| + | a,b = Board.move(x, | ||
| + | if s>0 and (self.params.feld[a][b]==Board.EMPTY or self.params.feld[a][b]==Board.APPLE): | ||
| + | return i | ||
| + | </ | ||
| + | ==== Dienstag 8. 5. 2018 ==== | ||
| + | |||
| + | Als Vorbereitung auf das Snake-Spiel: | ||
| + | |||
| + | <code python wegfinden.py> | ||
| + | from gpanel import * | ||
| + | |||
| + | import random | ||
| + | import time | ||
| + | |||
| + | width = 40 | ||
| + | |||
| + | vecs = [[1, | ||
| + | |||
| + | makeGPanel(0, | ||
| + | |||
| + | # Array mit Distanzen fuer alle Punkte, gefuellt mit -1 | ||
| + | dist = [[-1 for y in range(width)] for x in range(width)] | ||
| + | |||
| + | |||
| + | for i in range(800): | ||
| + | x=random.randint(1, | ||
| + | y=random.randint(1, | ||
| + | fillRectangle(x, | ||
| + | | ||
| + | |||
| + | todo=[(0, | ||
| + | dist[0][0] = 0 | ||
| + | |||
| + | waitTime = 0.002 | ||
| + | |||
| + | maxDist = 0 | ||
| + | |||
| + | setColor(" | ||
| + | while len(todo)> | ||
| + | # | ||
| + | # | ||
| + | pt = todo.pop(0) #vorne entfernen | ||
| + | aktuelle_d = dist[pt[0]][pt[1]] | ||
| + | if aktuelle_d> | ||
| + | maxDist = aktuelle_d | ||
| + | setColor(" | ||
| + | move(pt[0]+0.5, | ||
| + | fillRectangle(0.5, | ||
| + | time.sleep(waitTime) | ||
| + | for d in range(4): | ||
| + | p = (pt[0]+vecs[d][0], | ||
| + | if (p[0]>=0 and p[0]< | ||
| + | #print(p, getPixelColor(p[0]+0.5, | ||
| + | if getPixelColor(p[0]+0.5, | ||
| + | setColor(" | ||
| + | move(p[0]+0.5, | ||
| + | fillRectangle(0.5, | ||
| + | todo.append(p) | ||
| + | dist[p[0]][p[1]] = aktuelle_d + 1 | ||
| + | time.sleep(waitTime) | ||
| + | |||
| + | print(maxDist) | ||
| + | |||
| + | for x in range(width): | ||
| + | for y in range(width): | ||
| + | move(x+0.5, | ||
| + | if dist[x][y]> | ||
| + | setColor(dist[x][y]/ | ||
| + | fillRectangle(0.5, | ||
| + | else: | ||
| + | setColor(" | ||
| + | fillRectangle(1, | ||
| + | | ||
| + | | ||
| + | </ | ||
| + | |||
| + | ==== Dienstag 8. 5. 2018 ==== | ||
| + | Primzahlen auf eine Spirale darstellen. | ||
| + | |||
| + | Die Spirale soll " | ||
| + | |||
| + | Man startet in der Mitte und geht dann reihum und zählt die Schritte (in Pixeln). Immer wenn die Schrittnummer prim ist, wird der entsprechende Pixel markiert. | ||
| + | |||
| + | {{: | ||
| + | |||
| + | === Implementation === | ||
| + | Die vier Richtungsvektoren werden in ein zweidimensionales Array gespeichert (Arrays sollten im Plural bezeichnet werden): | ||
| + | <code python> | ||
| + | vecs=[[1, | ||
| + | </ | ||
| + | Die aktuelle Richtung wird als Zahl zwischen 0 und 3 dargestellt. Eine Drehung um 90 Grad heisst also 1 addieren oder subtrahieren und nötigenfalls wieder auf den Bereicht 0 bis 3 anpassen. Dafür eignet sich die Modulo-Operation: | ||
| + | <code python> | ||
| + | dir = 0 | ||
| + | # | ||
| + | #... | ||
| + | # | ||
| + | dir = (dir+1)%4 | ||
| + | dir = (dir+3)%4 | ||
| + | dir = (dir+2)%4 | ||
| + | </ | ||
| + | Die aktuelle Position wird mit zwei Variablen gespeichert. Die Position kann dann in eine Richtung verschoben werden: | ||
| + | <code python> | ||
| + | x = 10 | ||
| + | y = 32 | ||
| + | # Verschiebung in Richtung dir (Zahl von 0 bis 3) | ||
| + | x = x+vecs[dir][0] | ||
| + | y = y+vecs[dir][1] | ||
| + | </ | ||
| + | |||
| + | <hidden Lösungsvorschlag> | ||
| + | <code python spirale.py> | ||
| + | from gpanel import * | ||
| + | |||
| + | x = 100 | ||
| + | y = 100 | ||
| + | dir = 0 | ||
| + | zahl = 1 | ||
| + | |||
| + | vecs = [[1, | ||
| + | |||
| + | makeGPanel(0, | ||
| + | |||
| + | |||
| + | def isPrime(z): | ||
| + | for i in range(2, | ||
| + | if z%i==0: | ||
| + | return False | ||
| + | return True | ||
| + | |||
| + | for l in range(1, | ||
| + | for i in range(2): | ||
| + | for s in range(l): | ||
| + | if isPrime(zahl): | ||
| + | point(x,y) | ||
| + | x+=vecs[dir][0] | ||
| + | y+=vecs[dir][1] | ||
| + | zahl+=1 | ||
| + | dir=(dir+1)%4 | ||
| + | | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | === Und mehr === | ||
| + | Implementieren Sie z.B. so was: http:// | ||
| + | ==== Dienstag 24. 4. 2018 ==== | ||
| + | Sieb des Eratosthenes. | ||
| + | |||
| + | Idee: Erst wird jede 2. Zahl weggestrichen (ausser die 2), dann jede dritte, jede fünfte, jede siebte, etc. Übrig bleiben die Primzahlen. | ||
| + | |||
| + | Implementation: | ||
| + | |||
| + | <code python eratosthenes.py> | ||
| + | import time | ||
| + | start = time.time() | ||
| + | n = 10000 | ||
| + | prim = [i>1 for i in range(n+1)] | ||
| + | zwischen = time.time()-start | ||
| + | |||
| + | p = 2 | ||
| + | while p*p<=n: | ||
| + | # Vielfache von p wegstreichen: | ||
| + | x = p*p | ||
| + | while (x<=n): | ||
| + | prim[x]=False | ||
| + | x+=p # Kurzform fuer i=i+p | ||
| + | # Naechste Primzahl bestimmen | ||
| + | p+=1 | ||
| + | while prim[p]==False: | ||
| + | p+=1 | ||
| + | |||
| + | berechnung = time.time()-start - zwischen | ||
| + | |||
| + | # Ausgabe: | ||
| + | for i in range(n+1): | ||
| + | if prim[i]: | ||
| + | print(i) | ||
| + | |||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | |||
| + | Mehr zu Listen und Arrays: [[http:// | ||
| + | ==== Dienstag 3. 4. 2018 ==== | ||
| + | Primzahlen auflisten: | ||
| + | <code python> | ||
| + | import math | ||
| + | import time | ||
| + | start = time.time() | ||
| + | for i in range(2, | ||
| + | prime = True | ||
| + | for t in range(2, | ||
| + | if i % t==0: | ||
| + | prime = False | ||
| + | break | ||
| + | if prime: | ||
| + | print i, | ||
| + | if i%50==0: | ||
| + | |||
| + | | ||
| + | |||
| + | print(time.time()-start) | ||
| + | </ | ||
| + | Optimierung: | ||
| + | |||
| + | ==== Dienstag 27. 3. 2018 ==== | ||
| + | **Lernziele: | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | |||
| + | === Mögliche Zusatzaufgaben === | ||
| + | * Zeichnen Sie $n$ Kreise, die alle durch einen gegebenen Punkt gehen und deren Zentren gleichmässig auf einem gegebenen Kreis verteilt sind. Die Hüllkurve, die man erhält ist eine der Kardioide verwandte Form (bzw. genau jene, wenn der gegebene Punkt auf dem gegebenen Kreis liegt). | ||
| + | * Schreiben Sie ein Programm, das nacheinander Primzahlen ausgibt. Hinweis: '' | ||
| + | ==== Dienstag 5. 3. 2018 ==== | ||
| + | * Freifach freiwillig bereits ab 15:35 (um den ersten Dienstag nachzuholen). Das Zimmer wird offen sein, ich werde aber wohl noch ca. 30 min für eine Sitzung weg sein... | ||
| + | |||
| + | **Lektionsziele** | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | |||
| + | ==== Dienstag 27. 2. 2018 ==== | ||
| + | * Freifach freiwillig bereits ab 15:35 (um den ersten Dienstag nachzuholen). | ||
| + | |||
| + | **Lektionsziele** (mit " | ||
| + | * {{http:// | ||
| + | * {{http:// | ||
| + | * {{http:// | ||
| + | * {{http:// | ||
| + | |||
| + | |||
| + | === Dateien mit Funktionen einbinden === | ||
| + | <code python importtest.py> | ||
| + | def hello(): | ||
| + | print(" | ||
| + | |||
| + | | ||
| + | if __name__==" | ||
| + | print(" | ||
| + | </ | ||
| + | |||
| + | <code python othertest.py> | ||
| + | import importtest | ||
| + | |||
| + | importtest.hello() | ||
| + | </ | ||
| + | |||
| + | <code python yetanother.py> | ||
| + | import hello from importtest | ||
| + | hello() | ||
| + | </ | ||