diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2968b74 --- /dev/null +++ b/readme.md @@ -0,0 +1,484 @@ +# Code Annotator - Summary + +--- +## Pending +### - Funktion gibt Listen innerhalb einer Liste zurück, die das Feld darstellen. Die Listen sind mit Nullen befüllt, was bedeutet, dass alle Felder komplett leer sind. + +``` +def makeFeld(): + feld = [[[0] for i in range(matrixgr)] for e in range( + matrixgr)] # Erzeugt ein mit Nullen befülltes Feld, was bedeutet, + # dass dort weder ein (zerstörtes) Schiff ist, noch ein + # fehlgeschlagener Angriff stattgefunden hat + return feld +``` +### - In die Liste Output werden die auszugebenden Elemente eingetragen, damit sie am Ende alle gleichzeitig nebeneinander ausgegeben werden können + +``` + output = [[gegnerFeld[matrixgr]], [ + ' ' + eigenFeld[matrixgr]]] +``` +### - Der Bezeichner für die Zeile, da Zeilen bei Schiffe Versenken nicht nummeriert sondern alphabetisch sind. + +``` +NZeile = 'A' +``` +### - Grundsätzliche Logik: Während man beim eigenen Spielfeld immer genau sehen möchte, was auf welchem Feld ist (Schiff/versenktes Schiff/Fehlschuss...), sollte man beim gegnerischen Spielfeld nicht alle Infos haben (also nicht wissen, ob auf einem noch nicht abgeschossenen Feld ein Schiff ist oder nicht). Immer wenn f == 0, bedeutet das, dass es sich gerade um das gegnerische Feld handelt, bei f == 1 hat man es mit dem gegnerischen zu tun. + +``` +for f in range(2): +``` +### - 0 bedeutet leeres Feld, also wird beim eigenen Spielfeld das Feld als leer angezeigt, beim gegnerischen Spielfeld ist eine Tilde. + +``` + if n == [0]: + if f == 1: + zeile += ' [ ] ' + elif f == 0: + zeile += ' [ ~ ] ' +``` +### - Eine 1 bedeutet, dass auf eine leeres Feld geschossen wurde. Das möchte man auf beiden Spielfeldern sehen. + +``` + elif n == [1]: + zeile += ' [ . ] ' +``` +### - Die 2 steht für ungetroffene Schiffe. Seine eigenen möchte man sehen, die des Bots nicht. + +``` + elif n == [2]: + if f == 1: + zeile += ' [ ■ ] ' + elif f == 0: + zeile += ' [ ~ ] ' +``` +### - Die 3 steht für getroffene Felder, diese möchte man sowohl bei sich selbst als auch beim Bot sehen. + +``` + elif n == [3]: + zeile += ' [ X ] ' +``` +### - Die Funktion ord () liefert den Zahlenwert, welchen ein Zeichen in der ASCII-Tabelle hat. a hat den Wert 97 und da wir genau so viele Buchstaben wollen wie es Zeilen gibt, wird dieser Check gemacht. + +``` + if ord(NZeile.lower()) - 97 < matrixgr: + output[f] += [NZeile + ' ' + zeile] +``` +### - In die Variable zeile wird geschrieben, was sich auf jedem Feld in einer Zeile befindet. + +``` +zeile = '' +``` +### - Die Funktion chr () liefert, das Zeichen, welches den mitgegeben Wert in der ASCII-Tabelle hat. Hierdurch wird der Buchstabe "hochgezählt". + +``` +NZeile = chr(ord(NZeile) + 1) +``` +### - Erst jetzt, nachdem durch alle Zeilen der beiden Spielfelder durchgegangen wurde, wird alles auf einmal ausgegeben. output [0] beinhaltet das gegnerische Spielfeld, output [1] das eigene. + +``` + for i in range(0, len(output[0])): + print(output[0][i], ' ' + output[1][i]) + print("") +``` +### - Falls ein Feld abgeschossen wird, muss der Wert des Feldes um 1 erhöht werden. 0 --> 1, 2 --> 3. Dadurch, dass die 5 diese Funktion hat, muss nicht im Vorhinein geklärt werden, welchen Wert das gewählte Feld hat (und damit, ob es sich um einen Treffer handelt oder nicht), sondern das geschieht unabhängig davon hier in der Funktion für die Modifikation des Feldes. + +``` + if wert == 5: + wert = getFeld(spielerFeld, zeile, + spalte) + 1 +``` +### - Setzt den Wert des mitgegebenen Feldes auf den mitgegebenen Wert. + +``` + spielerFeld[zeile][spalte][0] = wert +``` +### - Gibt den aktuellen Wert des mitgegeben Feldes aus. + +``` +return spielerFeld[zeile][spalte][0] +``` +### - Addiert alle Werte des mitgegeben Spielfelds. + +``` +def summeFeld(spielerFeld): + summe = 0 + for i in spielerFeld: + for e in i: + for n in e: + summe += n + return summe +``` +### - Überprüft, ob im mitgegebenen Spielfeld 2er vorhanden sind. Wären keine mehr vorhanden, würde das bedeuten, dass keine ungetroffenen Schiffe mehr auf dem Spielfeld existieren und das Spiel somit beendet ist. + +``` +def zweicheck(spielerFeld): + for i in spielerFeld: + for e in i: + for n in e: + if n == 2: + return True +``` +### - Überprüft das mitgegebene Feld sowie die Felder "links", "rechts", "oben" und "unten" des Feldes auf 2er, also Schiffe. Falls das mitgegebene Feld schon ein Schiff beheimatet, kann direkt False zurückgegeben werden. + +``` +def checkUmfeld(spielerFeld, zeile, spalte): + if getFeld(spielerFeld, zeile, + spalte) == 2: + return False +``` +### - Überprüft, ob sich das Feld überhaupt auf dem Spielfeld befinden kann. + +``` + if matrixgr > zeile >= 0 and matrixgr > spalte >= 0: + +``` +### - Prinzip: Wenn sich das benachbarte Feld nicht außerhalb des Spielfeldes befindet oder es keine 2 beinhaltet, ist es frei. zeile + 1 kann als "rechter Nachbar", spalte + 1 als "unterer Nachbar" usw. gewertet werden. + +``` + if zeile + 1 < matrixgr: + if getFeld(spielerFeld, zeile + 1, spalte) != 2: + summe += 1 + else: + summe += 1 + + if spalte + 1 < matrixgr: + if getFeld(spielerFeld, zeile, spalte + 1) != 2: + summe += 1 + else: + summe += 1 + + if zeile - 1 >= 0: + if getFeld(spielerFeld, zeile - 1, spalte) != 2: + summe += 1 + else: + summe += 1 + + if spalte - 1 >= 0: + if getFeld(spielerFeld, zeile, spalte - 1) != 2: + summe += 1 + else: + summe += 1 +``` +### - Das Umfeld ist nur dann frei wenn sich oben, unten, links und rechts kein Schiff befindet, also summe exakt 4 ist. + +``` + if summe < 4: + return False + else: + return True +``` +### - Definiert, wie groß die Spielfelder sind, also 7x7. Hier ließe sich das ändern, trotzdem müssten an anderen Stellen auch noch Änderungen vorgenommen werden. + +``` +matrixgr = 7 +``` +### - groesse bestimmt, wie groß das zu setzende Schiff sein soll. Am Ende muss die Summe aller Felder des Spielfelds also der vorherigen Spielfeldsumme + der doppelten Größe des neuen Feldes betragen, da ein ungetroffenes Schiff den Wert 2 hat. + +``` +while summeFeld(spielerFeld) != feldsumme + groesse * 2: +``` +### - Weil das Schiff komplett zufällig platziert wird, kann es sein, dass das zufällig gewählte Feld nicht geeignet ist und sich der Platzieralgorithmus "verrennt". Um das vorzubeugen, wird das Feld nach 25 Versuchen komplett neu besetzt. Da diese Funktion nur ganz am Anfang und auch innerhalb einer Schleife aufgerufen wird, ist die Anzahl an Schiffen am Ende trotzdem richtig. + +``` +if versuch < 25: +``` +### - spitze[0] ist eine zufällig gewählte Zeilen-, spitze[1] die zufällig generierte Spaltennummer. + +``` + spitze = [random.randint(0, matrixgr - 1), + random.randint(0, matrixgr - 1)] +``` +### - Falls das Schiff nur ein Feld groß sein soll und das Umfeld des zufällig gewählten Feldes frei ist, kann das Schiff direkt gesetzt werden. + +``` + if groesse == 1: + setFeld(spielerFeld, spitze[0], spitze[1], 2) +``` +### - Auf dem belegFeld wird die Schiffsgröße an den gleichen Koordinaten eingetragen, an denen beim spielerFeld der Wert des Feldes steht. Das ist wichtig um später zu bestimmen, ob das Schiff schon komplett abgeschossen wurde. + +``` +setFeld(belegFeld, spitze[0], spitze[1], 1) +``` +### - orientierung entscheidet, ob das Schiff horizontal (0) oder vertikal (1) ausgerichtet werden soll. + +``` +orientierung = random.randint(0, 1) +``` +### - Der ursprüngliche zufällige Wert der Spitze, da die Liste gleich bearbeitet wird. + +``` +orig = spitze[1] +``` +### - counterind bestimmt, an welcher Stelle in spitze gecountet werden soll. + +``` +counterind = 1 +``` +### - Generelles Prinzip: Ab jetzt wird überprüft, ob das Schiff überhaupt in die ausgewählte Zeile oder Spalte hineinpasst. Ein Feld wird zu reihe hinzugefügt, falls sein Umfeld frei ist. Wenn nicht, wird reihe komplett zurückgesetzt. + +``` + while spitze[ + counterind] < matrixgr: + if checkUmfeld(spielerFeld, spitze[0], spitze[1]): + reihe += [spitze[counterind]] + else: + reihe = [] + summe = 0 +``` +### - .count zählt, wie oft eine mitgegebene Zahl in einer Liste vorkommt. Wenn die ursprüngliche zufällig gewählte Spitze vorkommt und die Liste mindestens so lang ist wie das Schiff groß sein soll, kann weitergemacht werden. + +``` + if len(reihe) >= groesse and reihe.count( + orig) == 1: +``` +### - Die generierte Liste wird jetzt von hinten durchgegangen und erneut wird überprüft, ob das Umfeld frei ist. + +``` + for i in range(len(reihe) - 1, + len(reihe) - groesse - 1, + -1): + if orientierung == 0: + if checkUmfeld(spielerFeld, spitze[0], i): + summe += 1 + else: + if checkUmfeld(spielerFeld, i, spitze[1]): + summe += 1 +``` +### - Falls die Anzahl an (freien) Feldern in der Liste mit der Soll-Größe übereinstimmt, kann das Schiff endgültig gesetzt werden. Auch hier wird im belegFeld die Schiffgröße für später eingetragen. + +``` + if summe == groesse: + for i in range(len(reihe) - 1, + len(reihe) - groesse - 1, -1): + if orientierung == 0: + setFeld(spielerFeld, spitze[0], i, 2) + setFeld(belegFeld, spitze[0], i, + groesse) + else: + setFeld(spielerFeld, i, spitze[1], 2) + setFeld(belegFeld, i, spitze[1], + groesse) +``` +### - Hier kann die gewünschte Anzahl und Größe der Schiffe konfiguriert werden. Standard: 1x 4er-Schiff, 2x 3er-Schiff, 2x 2er-Schiff, 3x 1er-Schiff. + +``` +def fuellFeld(Feld, belegFeld): + for i in range(1): + setSchiff(Feld, 4, belegFeld) + + for i in range(2): + setSchiff(Feld, 3, belegFeld) + + for i in range(2): + setSchiff(Feld, 2, belegFeld) + + for i in range(3): + setSchiff(Feld, 1, belegFeld) +``` +### - gr beinhaltet die größe des Schiffs, das gerade getroffen wurde, da das beim Setzen des Schiffes in belegFeld gespeichert wurde. + +``` + gr = getFeld(belegFeld, treffer[0], treffer[1]) + +``` +### - Falls das Schiff nur 1 groß war, werden alle umliegenden Felder als abgeschossen markiert, damit klar ist, dass dort kein Schiff mehr sein kann. + +``` + if gr == 1: + for i in range(-1, 2, 2): + if 0 <= treffer[0] + i < matrixgr: + setFeld(spielerFeld, treffer[0] + i, treffer[1], 1) + for i in range(-1, 2, 2): + if 0 <= treffer[1] + i < matrixgr: + setFeld(spielerFeld, treffer[0], treffer[1] + i, 1) + return True +``` +### - i bestimmt praktisch die Richtung und ist zuerst -1, dann +1. Es wird die Zeile und die Spalte durchgegangen, in denen sich das getroffene Feld befindet, bis ein Feld nicht mehr die gleiche größe hat wie das getroffene Schiff. So stehen am Ende der beiden for-Schleifen in der Liste schiff die Koordinaten aller Felder, auf denen sich das Schiff befindet/befand. + +``` + for i in range(-1, 2, 2): + if 0 <= treffer[0] + i < matrixgr: + if getFeld(belegFeld, treffer[0] + i, treffer[1]) == gr: + e = 1 + while 0 <= treffer[0]+e*i= wurf: + while getFeld(spielerFeld, feld[0], feld[1]) != 2: + feld = [random.randint(0, matrixgr - 1), + random.randint(0, matrixgr - 1)] + treffer = True + else: + while getFeld(spielerFeld, feld[0], feld[1]) != 0: + feld = [random.randint(0, matrixgr - 1), + random.randint(0, matrixgr - 1)] + +``` +### - Da der Bot getroffen hat, wird das Spiel für eine Sekunde pausiert, damit nicht alles auf einmal in die Konsole ausgegeben wird und damit es so wirkt, als würde der Bot "überlegen". Außerdem muss die Schwierigkeit wieder durch 10 geteilt werden (da sie am Anfang damit multipliziert wurde). Zusätzlich wird sie aber noch etwas kleiner gemacht als am Anfang, damit gerade bei höheren Schwierigkeiten nicht allzu viele Treffer hintereinander gemacht werden. + +``` + time.sleep(1) + botwahl(spielerFeld, belegSpFeld, diff / 10.25) +``` +### - Diese Funktion ist das Grundgerüst des Spiels, in dem alles zusammenkommt. Zunächst wird die Zeit zum Start des Spiels gespeichert, damit später die Differenz ermittelt werden kann. Dann wird die Schwierigkeit eingegeben und danach die Felder aufgebaut. + +``` +def start(): + startzeit = time.time() + level = SchwierigkeitsWahl() + Initialisiere() + printFelder() +``` +### - Die while True - Schleife endet nie, außer es kommt zu einem break. Das passiert nur, wenn in einem der beiden Felder keine 2er - also ungetroffene Schiffe - mehr vorhanden sind. außerdem wird nach dem eigenen Zug das Spiel für eine Sekunde pausiert, damit der Zug des Bots nicht direkt kommt. + +``` + while True: + feldWahl(gegnerFeld, belegGegFeld) + if not zweicheck(gegnerFeld): + break + time.sleep(1) + botwahl(eigenFeld, belegEigFeld, level) + if not zweicheck(eigenFeld): + break +``` +### - Wenn das Spiel vorbei ist, wird die Dauer ausgegeben und abgefragt, ob noch einmal gespielt werden soll. Wenn ja geht die Funktion nochmals von vorne los, wenn nein terminiert das Programm. + +``` + print("Das Spiel hat", int((time.time() - startzeit) // 60), "Minuten", + int(((time.time() - startzeit) % 60) // 1), "Sekunden gedauert.") + w = str(input("Noch eine Runde? [J: Ja/Irgendwas: Nein] ")) + if w.lower() == 'j': + start() +``` +### - Außer den Imports ist das die einzige Zeile Code, die keine Funktionsdeklaration (bzw. in einer) ist. Sie löst nur den Start des Spiels aus. +``` +start () +``` \ No newline at end of file diff --git a/schiffe-versenken.py b/schiffe-versenken.py index 715d6e1..efe92ab 100644 --- a/schiffe-versenken.py +++ b/schiffe-versenken.py @@ -1,52 +1,42 @@ import random, time -matrixgr = 7 # Größe des Spielfeldes (7x7) +matrixgr = 7 def makeFeld(): feld = [[[0] for i in range(matrixgr)] for e in range( - matrixgr)] # Erzeugt ein mit Nullen befülltes Feld, was bedeutet, - # dass dort weder ein (zerstörtes) Schiff ist, noch ein - # fehlgeschlagener Angriff stattgefunden hat + matrixgr)] return feld -# RAUS : -# noinspection PyTypeChecker def printFelder(): - # Von Feld1 werden nur die abgeschossenen Felder gezeigt, von Feld2 alle. output = [[gegnerFeld[matrixgr]], [ ' ' + eigenFeld[matrixgr]]] - # In die Liste Output werden die auszugebenden Elemente eingetragen, - # damit sie am Ende alle gleichzeitig nebeneinander ausgegeben werden können for i in output: i += [" 1 2 3 4 5 6 7 "] felder = [gegnerFeld, eigenFeld] - for f in range(2): # f als erstes 0, dann 1 - NZeile = 'A' # Bezeichner für die Zeile + for f in range(2): + NZeile = 'A' for i in felder[f]: zeile = '' for n in i: if n == [0]: - if f == 1: # Zeige ALLE Felder an - zeile += ' [ ] ' # Feld ist leer + if f == 1: + zeile += ' [ ] ' elif f == 0: zeile += ' [ ~ ] ' - # Zeige NUR TREFFER an, ~ um vom eigenen Feld zu differenzieren elif n == [1]: - zeile += ' [ . ] ' # Feld wurde abgeschossen + zeile += ' [ . ] ' elif n == [2]: - if f == 1: # Zeige ALLE Felder an + if f == 1: zeile += ' [ ■ ] ' - # Auf dem Feld ist ein ungetroffenes Schiff - elif f == 0: # Zeige NUR TREFFER an + elif f == 0: zeile += ' [ ~ ] ' elif n == [3]: zeile += ' [ X ] ' - # Das Schiff auf dem Feld wurde getroffen if ord(NZeile.lower()) - 97 < matrixgr: output[f] += [NZeile + ' ' + zeile] @@ -62,13 +52,11 @@ def printFelder(): def setFeld(spielerFeld, zeile, spalte, wert): if wert == 5: wert = getFeld(spielerFeld, zeile, - spalte) + 1 # Um den Wert im Feld um eins zu erhöhen + spalte) + 1 spielerFeld[zeile][spalte][0] = wert def getFeld(spielerFeld, zeile, spalte): - # zeile = ord(zeile.lower())-97 - # return spielerFeld [zeile][spalte-1][0] return spielerFeld[zeile][spalte][0] @@ -92,8 +80,6 @@ def zweicheck(spielerFeld): def checkUmfeld(spielerFeld, zeile, spalte): if getFeld(spielerFeld, zeile, spalte) == 2: - # Wenn auf dem Feld schon ein Schiff ist, kann der Check - # direkt abgebrochen werden return False summe = 0 if matrixgr > zeile >= 0 and matrixgr > spalte >= 0: @@ -135,24 +121,17 @@ def setSchiff(spielerFeld, groesse, belegFeld): spitze = [random.randint(0, matrixgr - 1), random.randint(0, matrixgr - 1)] if checkUmfeld(spielerFeld, spitze[0], spitze[1]): - # spitze[0] ZEILE, spitze[1] SPALTE if groesse == 1: setFeld(spielerFeld, spitze[0], spitze[1], 2) setFeld(belegFeld, spitze[0], spitze[1], 1) - # Damit später abgerufen werden kann, - # wie groß das Schiff auf dem Feld ist. else: reihe = [] counter = 0 - # Jetzt wird entschieden, ob das Schiff vertikal oder - # horizontal ausgerichtet sein soll. orientierung = random.randint(0, 1) - # 0: horizontal, 1: vertikal if orientierung == 0: orig = spitze[1] spitze[1] = counter counterind = 1 - # damit der Counter noch bearbeitet werden kann else: orig = spitze[0] spitze[0] = counter @@ -160,8 +139,6 @@ def setSchiff(spielerFeld, groesse, belegFeld): while spitze[ counterind] < matrixgr: -# Ab jetzt wird überprüft, ob in der ausgewählten Reihe überhaupt das Schiff -# hinpasst if checkUmfeld(spielerFeld, spitze[0], spitze[1]): reihe += [spitze[counterind]] else: @@ -169,9 +146,6 @@ def setSchiff(spielerFeld, groesse, belegFeld): summe = 0 if len(reihe) >= groesse and reihe.count( orig) == 1: - # .count zählt, wie oft die mitgegebene Zahl in der Liste vorkommt. Wenn - # sie vorkommt und die Liste mindestens so lang ist wie das Schiff sein - # soll, kann weitergemacht werden for i in range(len(reihe) - 1, len(reihe) - groesse - 1, -1): @@ -188,7 +162,6 @@ def setSchiff(spielerFeld, groesse, belegFeld): setFeld(spielerFeld, spitze[0], i, 2) setFeld(belegFeld, spitze[0], i, groesse) - # Damit später abgerufen werden kann, wie groß das Schiff auf dem Feld ist else: setFeld(spielerFeld, i, spitze[1], 2) setFeld(belegFeld, i, spitze[1], @@ -201,9 +174,6 @@ def setSchiff(spielerFeld, groesse, belegFeld): spielerFeld = makeFeld() versuch = 0 - if summeFeld(spielerFeld) != feldsumme + groesse * 2: - spielerFeld = makeFeld() - setSchiff(spielerFeld, groesse) def fuellFeld(Feld, belegFeld): @@ -246,16 +216,17 @@ def schiffVersenkt(spielerFeld, belegFeld, treffer): schiff += [[treffer[0] + e * i, treffer[1]]] e += 1 - for i in range(-1, 2, 2): - if 0 <= treffer[1] + i < matrixgr: - if getFeld(belegFeld, treffer[0], treffer[1] + i) == gr: - e = 1 - while 0 <= treffer[1]+e*i