Schiffe versenken Code

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
PirateD11
User
Beiträge: 1
Registriert: Samstag 18. Juni 2022, 23:37

Hallo Leute ich bin neu im Python Forum und bin Python Anfänger, ich habe einen Schiffe versenken Code Geschrieben jedoch funktioniert dieser jetzt nicht mehr, jedoch weiß ich nicht genau wo das Problem ist, könnte jemand bitte drüber schauen danke :)

board = []
boardw = []

def player_one():
name = str(input("SP1 vergib dir einen Namen: "))
return name

z = player_one()

def player_two():
nametwo = str(input("SP2 vergib dir einen Namen: "))
return nametwo

zz = player_two()

waage = int(input((z)+ " geben Sie größe Waagerecht ein: "))
while waage > 10:
print("Das Feld darf nicht größer als 10 sein.")
waage = int(input((z)+ " geben Sie größe Waagerecht ein: "))

senk = int(input((z)+ " geben Sie größe Senkrecht ein: "))
while senk > 10:
print("Das Feld darf nicht größer als 10 sein.")
senk = int(input((z) +" geben Sie größe Senkrecht ein: "))

for x in range(senk):
board.append([" "]*waage)

def print_board(board):
for row in board:
print("|".join(row))

for x in range(senk):
boardw.append([" "]*waage)

def print_boardw(boardw):
for row in boardw:
print("|".join(row))

def playertwo_row(boardw):
ship_roww = int(input("Platzieren Sie Ihr Schiff1: "))
return (ship_roww)

def playertwo_col(boardw):
ship_colw = int(input("Platzieren Sie Ihr Schiff1: "))
return (ship_colw)

def playertwo_rowwe(boardw):
ship_rowwe = int(input("Platzieren Sie Ihr Schiff2: "))
return (ship_rowwe)

def playertwo_colwe(boardw):
ship_colwe = int(input("Platzieren Sie Ihr Schiff2: "))
return (ship_colwe)

def playertwo_rowwee(boardw):
ship_rowwee = int(input("Platzieren Sie Ihr Schiff3: "))
return (ship_rowwee)

def playertwo_colwee(boardw):
ship_colwee = int(input("Platzieren Sie Ihr Schiff3: "))
return (ship_colwee)


def anzahl_Schiffe():
ship = int(input("Mit wie vielen Schiffen wollen Sie spielen (Maximal 3)"))
while ship > 3:
print("Bitte beachten Sie, dass Sie nicht mehr als 3 Schiffe aussuchen dürfen.")
ship = int(input("Mit wie vielen Schiffen wollen Sie spielen (Maximal 3)"))
if ship == 1:
playertwo_row(boardw)
playertwo_col(boardw)
elif ship == 2:
playertwo_row(boardw)
playertwo_col(boardw)
playertwo_rowwe(boardw)
playertwo_colwe(boardw)
elif ship == 3:
playertwo_row(boardw)
playertwo_col(boardw)
playertwo_rowwe(boardw)
playertwo_colwe(boardw)
playertwo_rowwee(boardw)
playertwo_colwee(boardw)

anzahl_Schiffe()


def playerturnone(board,ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee):
print_board(board)
for turn in range(8):
print("Turn", turn + 1)

guess_roww = int(input((zz) + " Guess Row:"))
guess_colw = int(input((zz) + " Guess Col:"))

if guess_roww == ship_roww and guess_colw == ship_colw:
print("Congratulations! You sank my Battleship!")
print(playerturnone(board, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))

elif guess_roww == ship_rowwe and guess_colw == ship_colwe:
print("Congratulations! You sank my Battleship!")
print(playerturnone(board, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))

elif guess_roww == ship_rowwee and guess_colw == ship_colwee:
print("Congratulations! You sank my Battleship!")
print(playerturnone(board, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))
else:
if (guess_roww < 0 or guess_roww > waage) or (guess_colw < 0 or guess_colw > senk):
print("Oops, that's not even in the ocean.")
elif (board[guess_roww][guess_colw] == "X"):
print("You guessed that one already.")
else:
print("You missed my battleship!")
board[guess_roww - 1][guess_colw - 1] = "X"
print(playerturnone(board, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))




def playerturntwo(boardw, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee):
print_boardw(boardw)
for turn in range(8):
print("Turn", turn + 1)

guess_roww = int(input((zz)+" Guess Row:"))
guess_colw = int(input((zz)+" Guess Col:"))

if guess_roww == ship_roww and guess_colw == ship_colw:
print("Congratulations! You sank my Battleship!")
print(playerturntwo(boardw, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))

elif guess_roww == ship_rowwe and guess_colw == ship_colwe:
print("Congratulations! You sank my Battleship!")
print(playerturntwo(boardw, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))

elif guess_roww == ship_rowwee and guess_colw == ship_colwee:
print("Congratulations! You sank my Battleship!")
print(playerturntwo(boardw, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))
else:
if (guess_roww < 0 or guess_roww > waage) or (guess_colw < 0 or guess_colw > senk):
print("Oops, that's not even in the ocean.")
elif (boardw[guess_roww][guess_colw] == "X"):
print("You guessed that one already.")
else:
print("You missed my battleship!")
boardw[guess_roww-1][guess_colw-1] = "X"
print(playerturntwo(boardw, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))


print("Let's play Battleship!")

#kann nicht ausgeführt werden keine ahnung warum
Print(playerturnone(board, ship_roww, ship_colw, ship_rowwe, ship_colwe, ship_rowwee, ship_colwee))
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Code gehört zwischen Code-Tags. Die erscheinen automatisch, wenn du den </> Button im vollständigen Editor drückst. Dort zwischen gehört dein Code dann bleibt auch die Einrückung erhalten.

Dann fehlt die Fehlermeldung. Was heißt "kann nicht ausgeführt werden"?

Warum ich mir deinen Code nicht anschaue:

Du verwendest globale Variablen.
Auf Moduleebene (also uneingerückt) gehören ausschließlich die She-Bang, Importe, die Definition von Konstanten (also Variablen, die sich nie! ändern und die schreibt man dann KOMPLETT_GROSS), Funktionen und Klassen und am Ende des Scripts ein:

Code: Alles auswählen

if name == "__main__":
    main()
Die Funktion main() ist dann der Einstiegspunkr für dein Programm.

Funktionen bekommen alles, was sie für ihren Zweck brauchen als Parameter übergeben und geben ihr Ergebnis mit return zurück.
Verwendest du in einer Funktion eine Variable, die nicht als Parameter übergeben wurde, ist das falsch.

Wenn du das umgesetzt hast, also die globalen Variablen los bist, hast du auch gleich automatisch dafür gesorgt, dass diese grausame Mischung von Funktionen und dazwischen versteckten Code auf Modulebene verschwindet.

Und wenn du eh am Umbauen bist, dann solltest du auch gleich passende Namen vergeben. Namen die sprechend und dem Leser des Codes verständlich sind. Funktionen sind dabei in der Regel nach ihren Tätigkeiten benannt.
Ich habe nämlich keine Lust mir zu überlegen, was wohl z, zz, ship_rowe und ship_rowwee sein könnte.

Funktionen, die sich nur marginal unterscheiden, davon hast du viele, unter andere die Frage nach dem Spielernamen, fasst man zu einer einzigen Funktionen zusammen. Die Unterscheidung macht man dann über die Parameter. Zum Beispiel indem du den Text der Frage beim Aufruf als Parameter mit gibst.

Edit:
Ich sehe gerade, dass du dir wahrscheinlich einen Teil des Codes von einem Codeacademy-Projekt geborgt hast. Keine Ahnung ob du da einen Kurs mit machst, aber wenn die Teilnahme zu so einem Code führt, zweifel ich die Qualität an.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wo das Problem ist, sagt Dir die Fehlermeldung die Du bekommst. Ein wichtiger Teil beim Programmierenlernen ist es auch, Fehlermeldungen interpretieren zu können.
Da Du ja anscheinend eine Variante hattest, die funktioniert hat, kannst Du ja vergleichen, was Du verändert hast, und kommst so dem Fehler vielleicht etwas näher.

Mit dem Rückgabewert einer Funktion muß man auch was machen, bei `player_one` und `player_two` zeigst Du ja, dass Du das Prinzip verstanden hast, bei `playertwo_row`, `playertwo_rowwe` und `playertwo_rowweee` dagegen machst Du nichts mit dem Rückgabewert.

Du hast 110 Zeilen Code geschrieben, ohne dass Du eine einzige Funktion wirklich getestet hast.
Ein Programm muß man Stück für Stück entwickeln, und nicht hoffen, dass man einmal von oben nach unten was runterschreibt und das funktioniert dann, wird es nicht.
Um das machen zu können, braucht man einen Plan, wie das Programm aufgebaut sein soll.
Was für Informationen hat ein Spieler? Name, Spielbrett und Schiffe, wobei ja im klassischen Schiffeversenken die Schiffe auf dem Spielbrett markiert werden, was bei Dir das auch Programm um einiges einfacher machen würde.

So ein Spiel besteht immer aus zwei Ebenen: den Nutzereingaben und der eigentlichen Spiellogik.
Zweiteres ist meist komplizierter und interessanter, weshalb man damit anfängt:
Spielbrett initialisieren, Schiffe setzen, Treffer setzen und Abfragen ob ein Schiff versenkt ist und ob alle Schiffe versenkt sind.
Da hast Du also schon 6 Funktionen, die Du schreiben mußt, und auch gleichzeitig Tests, die prüfen, ob die Funktionen das richtige machen.
Wenn dann das eigentliche Spiel fertig ist, kommen noch die Funktionen zur Eingabe dazu.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PirateD11: Ergänzend zu den Anmerkungen von sparrow:

Warum die Zeile mit dem Kommentar sie könne nicht ausgeführt werden, nicht ausgeführt werden kann, ist eigentlich recht offensichtlich. Die Fehlermeldung lautet: „NameError: name 'Print' is not defined“. Und `Print` ist tatsächlich nicht definiert. `Print` ist was anderes als `print` ist was anderes als `PrInT` ist was anderes als `pRiNt`, und so weiter. Gross-/Kleinschreibung ist wichtig.

Wenn man das korrigiert, dann sind in der Zeile aber noch 6 andere Namen undefiniert. Dieses mal nicht weil sie falsch geschrieben wären (von sparrow' berechntigtem Einwand das sie total kryptisch sind mal abgesehen), sondern weil sie in dem Namensraum nicht definiert sind. Jede Funktion hat ihren eigenen Namensraum der entsteht wenn sie aufgerufen wird, und wieder verschwindet wenn sie abgearbeitet wurde. Und lokale Namen sind nur innerhalb dieses Zeitraums und innerhalb der Funktion sichtbar. Nicht ausserhalb. Das ist ja gerade der Sinn von Funktionen, das man ein überschaubares, in sich geschlossenes Stück Code hat, das man einzeln betrachten und testen kann, ohne sich Gedanken darüber machen zu müssen was für Namen alle anderen Funktionen in dem Programm wohl benutzen und wofür.

Der viele Code scheint zum grossen Teil durch kopieren und einfügen und dann leicht verändern entstanden zu sein. Gewöhn Dir so etwas gar nicht erst an. Beim Programmieren versucht man genau das Gegenteil: Code- und Datenwiederholungen im Quelltext zu vermeiden. Auch das ist ein Grund für Funktionen, das man keinen Code ähnlich wiederholen muss, sondern die Gemeinsamkeiten in einde Funktion steckt, und Unterschiede heraus zieht und als Argument(e) übergeben kann.

Beispiel für die Eingabe der Spielernamen. Statt zwei Funktionen die sich von der Struktur her gar nicht unterscheiden, sondern nur in einem einzelnen Zeichen in einer Zeichenkette, würde man eine Funktion schreiben, die den Unterschied — die Nummer des Spielers — als Argument übergeben bekommt:

Code: Alles auswählen

def player_one():
    name = str(input("SP1 vergib dir einen Namen: "))
    return name


def player_two():
    nametwo = str(input("SP2 vergib dir einen Namen: "))
    return nametwo

# =>

def ask_player_name(player_number):
    return input(f"SP{player_number} vergib dir einen Namen: ")
Hier habe ich auch gleich das überflüssige `str()` entfernt, denn `input()` liefert bereits eine Zeichenkette. Und man muss auch nicht jedes Zwischenergebnis an einen Namen binden, insbesondere wenn das einzige was damit gemacht wird, in der nächsten Zeile eine Rückgabe per ``return`` ist.

Wirklich unsinnig sind Funktionen die strukturell komplett gleich sind:

Code: Alles auswählen

def print_board(board):
    for row in board:
        print("|".join(row))


def print_boardw(boardw):
    for row in boardw:
        print("|".join(row))
Hier kann die zweite einfach ersatzlos wegfallen, weil die genau das gleiche wie die erste macht.

Allerdings wird sie nicht funktionieren. Dass Du das noch nicht bemerkt hast, ist ein Zeichen, dass Du falsch vorgehst. Man schreibt nicht so viel Code ohne den zu testen. Man schreibt eine Funktion und testet die, und erst wenn die funktioniert, macht man mit der nächsten Funktion weiter. Denn wenn `print_board()` nicht funktioniert, dann wird auch eine Funktion die das verwendet, nicht funktionieren können, also muss man erst `print_board()` zum laufen bringen. Programmentwicklung heisst, dass man das Schritt für Schritt entwickelt, und nicht in einem Stück erst das gesamte Programm runterschreibt, und erst gegen Ende erst ausprobiert ob das überhaupt alles so funktioniert wie man sich das gedacht hat.

Code auf Modulebene der nicht dazu dient Konstanten, Funktionen, und Klassen zu definieren ist ja an sich schon keine gute Idee, aber Funktionsdefinitionen und Code vom Hauptprogramm zu vermischen, also das Programm zwischen Funktionsdefinitionen zu verstreuen ist noch mal extra unübersichtlich. Wenn das zumindest in einem zusammenhängenden Zeilenblock am Ende stehen würde, sähe man auch viel deutlicher das da undefinierte Namen verwendet werden, eben weil da keine Zeile steht wo die definiert würden.

Die ganzen Funktionen zur Eingabe der Schiffskoordinaten bekommen ein Board übergeben, verwenden das aber gar nicht. Es wäre in den Funktionen eigentlich auch wünschenswert, dass die a) prüfen ob die eingegebenen Koordinaten innerhalb des Spielfeldes sind, und b) sich keine Schiffe überlappen, beziehungsweise an der gleichen Stelle platziert werden.

Da Python keine Syntax für eine nachprüfende Schleife hat, drückt man das durch eine ”Endlosschleife” aus, die bei Eintritt der gewünschten Bedingung mit ``break`` verlassen wird. Auch hier ist wieder die Motivation Codewiederholung zu vermeiden, dass man den Code der vor der Schleife steht, nicht noch einmal in der Schleife wiederholen muss:

Code: Alles auswählen

    ship = int(input("Mit wie vielen Schiffen wollen Sie spielen (Maximal 3)"))
    while ship > 3:
        print("Bitte beachten Sie, dass Sie nicht mehr als 3 Schiffe aussuchen dürfen.")
        ship = int(input("Mit wie vielen Schiffen wollen Sie spielen (Maximal 3)"))
    
    # =>
    
    while True:
        ship = int(input("Mit wie vielen Schiffen wollen Sie spielen (Maximal 3)"))
        if ship <= 3:
            break
        print("Bitte beachten Sie, dass Sie nicht mehr als 3 Schiffe aussuchen dürfen.")
Der Code danach ist auch wieder von Wiederholungen im Quelltext geplagt, die man durch eine Schleife ausdrücken würde. Wozu man allerdings auch hier erst einmal wieder strukturell gleiche Funktionen zusammenfassen müsste. Und dann muss man auch etwas mit den Rückgabewerten dieser Funktion machen, die werden momantan ja komplett ignoriert.

`boardw` kommt in der Funktion auch aus dem ”nichts”, und wenn man keine globalen Variablen mehr verwendet, geht das so auch gar nicht mehr. Alles was Funktionen (und Methoden) ausser Konstanten benötigen, bekommen sie als Argument(e) übergeben.

`playerturnone()` braucht da beispielsweise mindestens noch `zz`, `waage`, und `senk` als Argumente. Wobei `waage` und `senk` redundant wären, weil man sich diese Werte aus dem `board` holen kann.

Die Funktion ruft sich selbst auf, was falsch ist. Nicht nur das Rekursion kein Ersatz für eine einfache Schleife zur Wiederholung ist — hier handelt es sich auch noch um eine Endlosrekursion die nie aufhört.

Python kann Vergleichsoperatoren verketten, wie das in der Mathematik üblich ist. Die Bereichsprüfung ob die Koordinaten innerhalb des Spielfeldes liegen, lassen sich so lesbarer schreiben, und da man die Koordinaten jeweils nur einmal schreiben muss, auch ein bisschen weniger fehleranfällig:

Code: Alles auswählen

            if (guess_roww < 0 or guess_roww > waage) or (guess_colw < 0 or guess_colw > senk):
            
            # =>
            
            if not (0 <= guess_roww < waage and 0 <= guess_colw < senk):
Da ist eine ziemlich offensichtliche Diskrepanz zwischen dem Test auf einen vorhandenen Fehlschuss auf dem Board und dem Eintragen eines solchen.

Bei der Grösseneingabe für das Spielfeld gibt es keine Untergrenze. Also nicht nur das negative Feldgrössen oder 0 keinen Sinn machen, aber auch ein 1×1 Feld ist sicher keine so gute Idee. Die minimale Feldgrösse hängt auch mit der Anzahl der Schiffe zusammen. Es muss ja mindestens so viel Platz sein, dass alle Schiffe ohne Überlappung platziert werden können.

Es gibt an so einigen Stellen unnötige Klammern um Ausdrücke die nur aus einem einzelnen Namen bestehen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten