# Learning Wizzard Project diary
###### tags: `fh`
A project developed by Sebastian Doiber & Johannes Schwinger
:::info
Our code is Online! Have a look @[GitHub](https://github.com/One-Step-Ahead/Learning_Wizzard)
:::
# 24.10.2019 (ILV Group A)
## Brainstorming
Our project started in the exercise on the 24.10.2019.
We brainstormed for a while to come up with good ideas. We knew we wanted to do something with machine learning/ Artificial intelligence.
We considered to implement the following Games and let a machine learn how to play them:
* Connect 4
* Chess
* Battleships
* Snake
* Black Jack
* Wizzard
The last one was a flash of inspiration and both of us immediately took a liking to it.
The rest of the exercise we started to think about what tools to use, how to coordinate and also started with our first UML Diagrams, Use-Case to be specific.
# 30.10.2019
## SRS-Document
This was the date we took on the task of writing our first version of the SRS Document. We thought of every requirement that could possibly come to mind and wrote them down in Trello. Afterwards we tried to articulate every requirement in the right way, and added them according to the MOSCOW principle as well as dividing them up in functional and non-functional requirements. We also added a Use-Case Diagram which we developed in the previous exercise unit at the FH.
# 04.11.2019 (ILV Group A)
## HLD-Document
* finished UML: Sequence Diagramm
* finished UML: Activity Diagramm
We have done some research about some examples on how to apply Machine Learning. We also discussed on which programmjng language to use. Python is used most of the time for AI so we decided to use Python only for our project.
# 07.11.2019
## Started with first lines of code
To get the project started I just implemented all the different cards that are being used in Wizzrad.
# 8.11.2019
Implementation of the most basic objects. Created the players, the cards and the rounds that will be played in the game. Also implemented the card dealing (1 more card for every new round that is going to be played). Now it is also possible to play the game in the full range of intended players (although 4 players are standard).
# 13.11.2019
Today we finished writing the "High Level Design Document" and discussed the written code.
# 27.11.2019
Cleaned up (refactored) some code and thought about implementing methods to store played rounds (eventually exporting them in the future). Also implemented the player queue that is used to play a Stich.
# 28.11.2019
Implemented the logic of how a Stich is going to be played out. The system is now also able to identify incorrect moves (playing the wrong card at the wrong moment).
# 02.12.2019 (ILV Group A)
Nach einem gespräch mir Fr. Schefer-Wenzl haben wir beschlossen das Tagebuch von nun an auf Deutsch zu führen.
Des Weiteren werden wir es nun tatsächlich zu einem Projekttagebuch machen, oder dies zumindest versuchen, nachdem es bisher eher ein Dokumentation war.
Bei der heutigen Arbeit versuchen wir die Regeln weiter zu Implementieren. Bei der Überpfüng des Stiches um herauszufinden welches Spieler mit welcher Karte den Stich gewinnt kämpfen wir damit:
1. Den Code übersichtlich zu halten indem wir jeder Überprüfung eine eigenen Funktion zuordnen:
a. Sieg durch Zauberer?
b. Sieg durch Atut?
c. Sieg durch Farbzwang?
d. Sieg durch Narren (nur bei 4 Spielern möglich)?
2. und auch zeitgleich festzustellen WELCHER der Spieler durch die erfüllte Win-Condition gewonnen hat.
Problem dabei ist, dass wir mit folgender Funktion herausfinden wollen, welcher Spieler den Stich macht.
```python=
def check_stich_winner(cards_in_play: list, player_q: list) -> Player:
check_wizzad_win()
check_atut_win()
check_colored_win()
check_narr_win()
```
---
Nachdem wir versucht haben eine einfache Schleife zu kreieren um alle Win-Conditions eines Stiches abzuprüfen haben wir feststellen müssen, dass das nicht so einfach ist wie gedacht. Damit die Schleife nicht zu groß wird und dann im endeffekt die Funktion 200 Zeilen lang ist, haben wir begonnen die Funktion in weitere kleine Funktionen aufzuteilen.
Das Problem das dabei entsteht ist, dass ich mit der Funktion versuche einen Player zu returnen. Jedoch sind wir an einen Punkt gestoßen, wo wir gleichzeiting abfragen wollten **ob** eine Win-Condition auftritt und bei **welchem** Spieler die Win-Conditin auftritt. Erster Gedanke:
```python
return True, Player
```
Haben dann nachgefragt und erfahren, dass eine Funktion immer genau eine Aufgabe erfüllt aka. *ein* `return`.
Der neue Ansatz ist nun eine neue Klasse `Stich` anzulegen die immer weiß;
* Wer den Stich gewonnen hat
* Die Spieler Reihenfolge
* Die gespielten Karten
Das erstellte Objekt, kann dann nach dem Spiel aufgerufen werden um Statistiken zu erstellen. (Yes collect **ALL** the data.)
# 4.12.2019
Nachdem ich ein wenig überlegt habe wie ich die check_stich_winner function umsetzen soll, stellte ich fest, dass ich schon alles was ich brauch in der Stich-Klasse habe. Deshalb habe ich mich dazu entschieden die gesamte Logik aus `rules.py` zu entfernen und sie in die Klasse `Stich` einzufügen. Nun muss ich mich nicht mehr mit unendlich vielen Outputs plagen, denn ich lege nun in der Klasse selber den Gewinner ab.
```python=
class Stich:
def __init__(self, player_q: deque):
self.player_q = player_q
self.cards_in_play = list()
self.winner = Player
```
Hier überprüfe ich beispielsweise ob ein Spieler mit eimem Zauberer gewonnen hat:
```python=
def check_wizard_win(self):
player_number = 0
for i in self.cards_in_play:
if isinstance(i, SpecialCard):
if i.cardType == 'z':
self.winner = self.get_player(player_number)
return True
player_number += 1
return False
```
Wie man in Zeile 6 & 7 sehen kann, bin ich nun in der Lage gleichzeiting ein `True` zu returnen und kann aber auch im gleichen Moment den Gewinner festlegen.
---
Ich habe nun die Logik abgeschlossen, welche nach einem gespielten Stich den gewinner ausmacht. Für mich war das bis jetzt die schwerste aufgabe bisher. Ich bin mir nicht sicher ob es funktioniert, da ich es (noch) nicht getestet habe. Aus diesem Grund habe ich einen `RuntimeError` eingebaut, der getriggert wird, sollte etwas nicht passen. Um ehrlich zu sein, weiß ich noch nicht wie ich das ganze System testen soll, es ist mirlerweile schon recht komplex.
Der Code sollte sehr übersichtlich gestaltet werden und ich habe es so gut wie möglich versucht. Jedoch an der Stelle wo ich schauen will wer den Stich gewinnt ist der Code extrem unübersichtlich geworden. Ich weiß nicht ganz wie ich den besser hinbekomme, so dass (wer auch immer) sich den Code ansieht oder bearbeiten muss sich nicht 30min damit auseinandersetzen muss. Hier ist das Beispiel:
```python=
def check_colored_win(self):
"""
This method checks for:
Atut
if Farbzwang got satisfied
if there are only Narren being played
"""
dominant_color = None
for i in self.cards_in_play:
if isinstance(i, SpecialCard):
continue
elif isinstance(i, ColoredCard):
dominant_color = i.card_color
else:
self.winner = self.get_player(0)
return True
strongest_card = None
for i in self.cards_in_play:
if isinstance(i, ColoredCard):
if strongest_card is None:
strongest_card = i
continue
elif i.card_color != dominant_color:
continue
elif strongest_card.card_color == self.atut.card_color and i.card_color != self.atut.card_color:
continue
elif i.card_color == self.atut.card_color:
if strongest_card.card_color != self.atut.card_color:
strongest_card = i
continue
else:
if i.card_value > i.card_color:
strongest_card = i
continue
else:
continue
else:
if i.card_value > i.card_color:
strongest_card = i
continue
else:
continue
self.winner = self.get_player(self.cards_in_play.index(strongest_card))
return True
```
# 12.12.2019
Je mehr ich an dem Projekt arbeite, desto unzufriedener werde ich bezüglich meines Codes den ich am Beginn des Projektes geschrieben habe. Er ist oft einfach unübersichtlich und nicht gut strukturiert. Ich habe beispielsweise in der GameBoard-Klasse eine Value gesetzt welche das Objekt für die aktuelle Runde besitzt. Beim Konstruktior ist diese Value dann auf `Null` gesetzt, was beim Autocomplete in PyCharm probleme machte. Also habe ich einfach eine *get-Funktion* geschrieben, die mir einfach das gewünschte Objekt ausgiebt.
## TDD
Nachdem wir nun begonnen hatten die Logik zu implementieren dachte ich daran diese zu testen. Nachdem ich schon vorher selber ein File geschrieben habe, welches ich zum Test verwendet habe, hatte ich ursprünglich überlegt die weiteren Tests auch dort zu machen. Jedoch wäre das die optimale Gelegenheit JunitTests zu machen. Also werde ich die Logik mit Unittests testen.
Nachdem ich ein wenig getestet habe, stelle ich schon einpaar Fehler fest die ich in der Logik gemacht habe. (Aus Dokumentations-Zwecken werde ich die Fehler nicht aus dem Blog entfernen. **[Hier](https://github.com/One-Step-Ahead/Learning_Wizzard/commit/b4e1600e01ed641ca7ef16abdb8896875a371f6b)** sind die Änderungen die ich vorgenommen habe. Siehe`game/stich.py`)
# 22.12.2019 & 23.12.2019
Nachdem ich ein paar neue Tests geschireben habe, habe ich gleich feststellen können, dass mein Code eigentlich nicht gut ist. Ich habe die `check_colored_win()` Funktion noch einmal komplett überarbeitet. Sie ist nun etwas kürzer und funktioniert (zumindest sind alle meine 6 Unit-Tests OK). Ich bin diesmal mit einer anderen Denkweise herangegeangen und habe mit den `if`-Statements mir eine cleverere Struktur überlegt. Ich frage nun explizit bestimmte Konditionen, hab um auszuschließen, dass ich nicht etwas anderes überprüfen muss.
Nachdem ich selber den Test zu meiner Funktion geschrieben habe (White-Box-Test), werden wir Rollen tauschen, um dann einen Black-Box-Test zu schreiben.
# 28.12.2019
Ich habe in zwei Schritten den User Input verbesser/überarbeitet. Zwei schritte deshalb weil ich beim 1. überarbeiten zwar daran gedacht habe, dass User zwar potentiell ein falsches Zeichen wie "a" eingeben könnten, aber nicht, dass User auch einen Wert eingeben könnten welcher außerhalb der Liste liegt -> IndexError.
Beide Errors werden nun gecatched. Draufgekommen bin ich durch Unittesting wobei die Tests nun natürlich sinnlos sind da sie einfach nur die erneute Eingabeaufforderung immer und immer wieder ausspucken.
Johannes hat außerdem implementiert, dass der Sieger des letzten Stiches mit dem Ausspielen des neuen Stiches beginnt, und auch gleich einen Test dafür geschrieben.
Des Weiteren hat er begonnen damit eine Methode zu implementieren welche aufzeichnet welcher Spieler wieviele Stiche gewonnen hat.
Wir haben außerdem beschlossen, da abzusehen ist, dass die AI vermutlich nicht mehr im Rahmen des möglichen liegen wird, dass wir noch ein simples Graphisches User Interphace machen. Wir verwenden dazu "PyQt5".
# 30.12.2019
Nachdem wir uns für ein Interface entschieden haben, hab ich begonnen das Programm umzuschreiben damit die Inputs (welche bisher über das CLI getätigt wurden) nun über das Interface gemacht werden können. Das bedeutet ich mache die Eingabe über das CLI optional und verbessere noch zusätzlich die ein oder andere Problematik die der Input über das CLI hat. Da ich nicht ganz weiß wie man am besten "Einstellungen" in ein File schreibt, werden alle Einstellungen bis weilen in die `engine.py` kommen.
## git
Ich finde git nach wie vor kompliziert. Es ist eigentlich nicht wirklich intuitiv. Nachdem ich meisents immer die integrierten VCS-Plugins in der PyCharm IDE verwende, möchte ich doch noch einmal [GitKraken](https://www.gitkraken.com/) ausprobieren. Bis jetzt bin ich eher für GitKraken. Es ist etwas übersichlticher und einfacher. Außerdem gibt es eine gute [Dokumentation](https://support.gitkraken.com/)
## QT
Um die GUI so einfach wie möglich erstellen zu können habe ich mir einige Tutorials zu PyQt5 angesehe und bin draufgekommen, dass es bei den tools eines gibt, welches den Entlickler dazu in die Lage versetzt die Oberfläche via drag&drop zu erstellen und anschließend in Code umzuwandeln um die Logik einzufügen. Ich denke dass das ein großer Vorteil ist da man nicht ständig den code ausführen muss um zu sehen was man gerade verändert hat/ was die Veränderung bewirkt hat. Abgesehen davon kann man so angeblich auch leicht das Design verändern, sodass es nicht aussieht wie eine steinalte Anwendung aus Windows95.
# 5.1.2020
## It's fixing time
Nachdem die Grundfunktionen alle laufen sollten (code ist fertig), geht es nun daran darauf zu achten, dass der Code der geschrieben wurde - zu testen. Zur Zeit arbeiten wir gleichzeitig an der GUI und am fixen von Fehlern. Ich habe es ehrtlich gesagt nicht erwartet, aber ich habe doch einige Fehler beim Programmieren gemacht und jetzt muss ich alles in Ordnung bringen. (Vielleicht hätten wir doch von anfang an mit UnitTests arbeiten sollen, denn ich kann mir nun nicht ganz sicher sein, dass *alles* funktioniert)
## es ist herrlich
Dank dem wunderbaren QTdesigner ist die GUI fertig. Also zumindest der Code. über 400 Zeilen die ich nicht hardcoden musste dank dieser enorm tollen software. Nun fehlt nurnoch das Einbinden der Spiellogik in die GUI und THEORETISCH sollte alles laufen. Ob dem so ist und wie "einfach" das sein wird sehen wir am Mittwoch dem 08.01.
# 8.1.2020
Wir haben mehrmals das Design der GUI geändert da bei unserer Vorherigen Entwurf/Version alle Spieler ständig die Karten der anderen Spieler einsehen konnten. Die Idee ist nun, dass immer nur die Karten von einem Spieler angezeigt werden und dieser beim Spielerwechsel nicht mehr auf den Bildschirm schaut. Dadurch sieht die GUI auch vom Design her schöner aus.
Zusätzlich haben wir uns beide weiter mit QT auseinandergesetzt. Leider ist die Dokumentation etwas unübersichtlich/verstreut im Netz.
## weil ichs immer vergess:
```
pyuic5 -x inputfile.ui -o outputfile.py
```
> [color=blue]und ja basti der dateityp gehört dazugeschrieben. lg past-basti
# VO am 9.1.2020
Folgende Fragen:
* Wird fertige UI erwartet?
* nice to have aber nicht must
* Was ist das am Montag? Präsentation?
* gegenseitiges reviewen
* Wie soll die fertige Software aussehen? Soll es eine .exe haben?
* .exe wär nice aber wenn nicht zumindest beschreibung "readme" wies ausführbar ist.
Anschließend habe ich versucht für die Buttons in unserer GUI events zu erstellen um die einbindung der Spiele logik zu erleichtern. Wie sich herausstellt ist die Dokumentation auf der Python Seite FALSCH weil mit @pyqtSlot() funktioniert das original NICHT.
Auch wenn ich eine funktion erstelle und anschließend den knopf über
`self.button.clicked.connect(funktion)`
verbinden will funktioniert es nicht.
Korrektur, nach einigen Stunden suchen auf StackOverflow bin ich draufgekommen dass diese Lösung richtig ist UND funktioniert (probieren geht über studieren i guess?). Es scheint nur so zu sein, dass PyCharm manchmal Probleme mit PyQt hat und Sachen nicht autocompleted/erkennt -> connect gibt es und funktioniert.
Als nächstes wende ich mich noch dem einstellen eines Bildes auf den Buttons zu. Damit wir enstprechend der Karte welche der Button repräsentiert das richtige Bild einfügen können.
# 10.01.2020
Habe heute funktionen für die Push Buttons erstellt und wollte auch schaun wie ich es zusammenbekomme, dass sich Label während der Laufzeit verändern, allerdings sind in der Spiellogik einige Probleme aufgetreten die dringender Behoben werden mussten.
Habe dann die zuvor veränderte get_winner funktion versucht in complete_game reinzuschuhstern was garnicht so einfach war/ist.Bin nach einer HALBEN Stunde draufgekommen dass es einfach einen Typo gab -.-
---
Ich muss festsltellen, dass sich doch einige Bugs ins Programm eingeschlichen haben. Villeicht hätter wir etwas mehr Zeit zum Testen einplanen sollen...
### stackoverflow post
Mein erster StackOverfolw post:
https://stackoverflow.com/questions/59651598/pycharm-no-autocompleting-typehints-global-variable
# 11.01.2020
Nachdem wir den ganzen Tag damit verbracht haben zu versuchen die GUI zum laufen zu bringen und die GameLogic reinzuflechten mussten wir am Nachmittag beschließen diesen Plan aufzugeben. Da wir beide davor nie mit PyQt5 gearbeitet haben hat sich der Prozess leider als wesentlich Zeitaufwendiger herausgestellt als zuvor vermutet.
Daher machten wir uns daran eine .exe file zu generieren welches wir dann am 12. abgeben können was auch noch ohne komplikationen verlief.
Anschließend haben wir das Spiel testen und kritisieren lassen. Der Plan für morgen ist, um die Präsentation am Montag etwas aufzuwerten noch folgendes zu schaffen:
1. Ein Readme File mit Regeln und informationen zu WIP
2. Ein Rudimentäres Menü für die CLI (Anzahl der Spieler auswählen und ev. Karten verstecken)
3. Und einbischen polishing.
# 12.01.2020
## It is release day!
### Interface
Eigentlich habe ich damit gerechnet etwas länger an den CLI Menü zu sitzen, aber tatsächlich dauerte das nur etwa 2 Stunden. Auch hier wieder: Ich habe noch nie zuvor ein solches Interface programmiert... Ein paar Optionen habe ich hinter "developer Options" versteckt da die Funktionen nicht für den end user gedacht sind, aber nachdem ich die Funktionen im Programm geschrieben habe, wollte ich, dass sie im Programm abrufbar sind.
### Round Up
Ich bin mit dem Projekt in seinem aktuellen Zustand sehr zufrieden. Bis jetzt konnten keine Bugs (mehr) gefunden werden, was bedeutet, das Programm rennt ohne Abstürze, und macht immer was vorgesehen ist. Leider ist das Programm nur über die CL spielbar, aber ich finde das nicht so schlimm, denn:
Das Spiel ist komplett umgesetzt worden, genau so, wie es in der Spielanleitung beschrieben wird. Es gibt auch keine Regel die missachtet werden kann da immer überprüft wird ob ein Spieler sich richtig verhällt.
Ich habe einiges an Zeit in das Projekt gesteckt und mir als Ziel gesetzt neues zu lernen. Oft habe ich versucht etwas anzuwenden, wovon ich schon einmal gehört habe (ein Beispiel wäre da eine [queue](https://docs.python.org/3/library/collections.html?highlight=deque#collections.deque)) um Neues kennen zu lernen. Dieses Ziel habe ich definitiv erreicht. Ich würde behaupten ich habe die Basics von Python verstanden. Was mich an Python sehr erfreut ist eine gute [Dokumentation](https://docs.python.org/3/), die ich wirklich oft benutzt habe.