Problem mit index()

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
DeinError404
User
Beiträge: 4
Registriert: Sonntag 3. Mai 2020, 22:05

Liebe Leute,

ich nerve mal wieder mit einer Anfängerfrage.
Ich habe zur Übung ein Zwei-Spieler-Hangman-Spiel programmiert. Das Spiel funktioniert tadellos, es gibt nur eine etwas unschöne Sache, die ich gerne begradigt hätte.
Vom Prinzip funktioniert der Anfang des Spiels so:

Über import getpass gibt Spieler 1 verdeckt ein Wort ein.
Dieses Wort wird in eine Liste umgewandelt.
Anschließend wird für jeden Buchstaben im Wort ein "_" ausgegeben.
Nun wollte ich gerne, zur Übersichtlichkeit, zusätzlich zu jedem "_" seine Position im Wort ausgeben lassen. Z.B. Wort ist "heute", Ausgabe:
"1_
2_
3_
4_
5_"

Hier ist das Stück Code, das ich dafür geschrieben habe:

Code: Alles auswählen

import getpass
    word = getpass.getpass("Geben Sie das zu erratende Wort ein (nur kleine Buchstaben): ")
    liste = []
    strike = 0
    for buchstabe in word:
        liste.append(buchstabe)
    for n in liste:
        print (liste.index(n)+1, "_")
Das funktioniert auch soweit, nur gibt index() immer nur den Index des ersten Mals aus, in der ein Element in der Liste auftaucht. Die Ausgabe im Fall von "heute" ist daher:

"1_
2_
3_
4_
2_ "

Daher die Frage: Gibt es einen einfachen Weg, diesen unschönen Nebeneffekt zu umgehen?

Vielen Dank im Voraus für Antworten!

P.S.:

Für den Fall, dass mehr von dem Code benötigt wird: https://gist.github.com/Ewwas17/59d805b ... 974e9363da
nezzcarth
User
Beiträge: 1764
Registriert: Samstag 16. April 2011, 12:47

Schau dir mal die Doku zu 'index' an. Entweder die Kurzfassung im Interpreter mit help() oder die etwas ausführlichere in der offiziellen Python-Doku. Es gibt einen Parameter 'start', der dir hier weiterhilft. Allerdings brauchst du das eigentlich gar nicht; was du da machst, ist meiner Meinung nach zu kompliziert. Wenn du fortlaufend etwas zählen möchtest, nimmt besser enumerate. Hier brauchst du aber doch eher nur die Länge des Strings, also reicht eine Zählschleife.

Edit:
Ansonsten: Dein Beispiel hat einen Einrückungsfehler. Du mischst deutsch- und englischsprachige Bezeichner und 'liste' ist kein guter Bezeichner, da er nichts inhaltliches aussagt. Vermutlich musst du hier den String gar nicht in eine Liste umwandeln, da man auf Strings wie auf andere Sequenzen auch zugreifen kann. Und ansonsten geht das auch in dem du einfach list(word) verwendest, d.h. ohne Schleife.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

`liste` kann man einfacher per `list` erzeugen:

Code: Alles auswählen

liste = list(word)
Das sollte aber gar nicht nötig sein, da man den String genauso gut verwenden kann.

Code: Alles auswählen

for character in word:
    print(word.index(character) + 1, "_")
oder wie Du eigentlich willst:

Code: Alles auswählen

for n in range(1, len(word) + 1):
    print(n, "_")
DeinError404
User
Beiträge: 4
Registriert: Sonntag 3. Mai 2020, 22:05

@nezzcarth @sirius3


Danke für eure Antworten.

Das hilft tatsächlich enorm weiter.
Was die Lösung angeht, es einfach direkt über einen string zu machen, könnte ich vor Scham im Boden versinken. Auf die Idee bin ich tatsächlich gar nicht gekommen. Vermutlich einfach deshalb, weil das letzte Tutorial, das ich geguckt habe, über list comparisons war und ich dann dachte, so kann ich gut überprüfen, ob ein von Spieler 2 geratener Buchstabe im Wort von Spieler 1 vorkommt. Und dann habe ich überlegt, wie kann ich einen string in eine Liste verwandeln, damit ich eine list comparison machen kann. Funktioniert auch, ist aber natürlich unnötig kompliziert.

Was den Misch-Masch von Deutsch und Englisch und die inhaltsleeren Namen wie "Liste" angeht, habt ihr natürlich auch Recht. Das sollte ich mir besser gleich vernünftig angewöhnen.

Bezüglich der Einrückung weiß ich ehrlich gesagt nicht, was da passiert ist. In meinem Original, aus dem ich einfach nur kopiert habe, sind "import getpass" und "word =..." auf derselben Ebene. Dass "import getpass" eingerückt ist, liegt daran, dass es in eine while-Schleife eingebunden ist, die ich nicht mit rein genommen habe, weil sie für das Problem irrelevant ist.

Also besten Dank für eure Anregungen!
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Importe gehören an den Anfang der Datei, nicht in irgendwelche while-Schleifen.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DeinError404: Ich habe mir mal den gesamten Quelltext in dem verlinkten Gist angeschaut.

Um Bedingungen bei ``while`` (und auch bei ``if``/``elif``) gehören keine Klammern.

Du hast eine sehr inkonsistente Leerzeichensetzung vor Aufrufklammern. Mal ist da eins, mal nicht. Da gehört keins hin.

Das die äusserste ``while``-Schleife läuft solange `game` wahr ist und abbricht wenn `game` unwahr ist, finde ich ziemlich verwirrend. Man würde das genau umgekehrt erwarten.

`user` heisst auf deutsch „Benutzer“/„Anwender“. Es wird aber bei keinem `input()` ein Benutzer oder Anwender eingegeben. Zudem sind die Namen für die Eingaben durchnummeriert, was ein Zeichen mehr dafür ist, das man sich hier *passende* Namen überlegen sollte.

`input()` liefert bereits eine Zeichenkette, da macht ein `str()`-Aufruf keinen Sinn.

Der Code-Teil mit der Frage ob der Anwender noch eine Runde spielen möchte ist drei mal im Quelltext vorhanden. Man vermeidet als Programmierer sowohl Daten- als auch Code-Kopien im Quelltext. Das macht das Programm aufgeblähter und fehleranfälliger, weil man Änderungen immer an allen Kopien und bei allen gleichwertig machen muss. Da besteht die Gefahr das man eine Kopie vergisst, oder leicht anders ändert als die anderen. Die Frage ob noch eine Runde gespielt werden soll gehört hinter die Schleife die für die Runde zuständig ist. Immer wenn man Code kopiert und einfügt ist das ein „code smell“ und man sollte sich überlegen ob man das durch eine andere Strukturierung, eine Schleife, eine Funktion/Methode, oder eine Kombination davon vermeiden kann.

Das ``elif strike == 10`` ist überflüssig, ein ``else`` tut es hier auch.

Auch ber der Erhöhung und Auswertung der `strikes` ist wieder Code kopiert worden.

Das `erraten` auf `True` gesetzt wird wenn man 10 Fehlversuche hatte, also das Wort gar nicht erraten wurde ist irreführend. `erraten` braucht man aber eigentlich auch gar nicht. Man kann da eine ”Endlosschleife” (``while True:``) schreiben, die mit ``break`` abgebrochen wird wenn das Wort erraten oder 10 Fehlversuche erreicht sind. Und auch `game` kann man so loswerden.

Die Anzahl der Versuche steht sowohl als Zahl, als auch in einer Zeichenkette im Code. Das würde man als Konstante definieren.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import getpass

MAX_STRIKES = 10


def main():
    while True:
        word = getpass.getpass(
            "Geben Sie das zu erratende Wort ein (nur kleine Buchstaben): "
        )
        for i in range(1, len(word) + 1):
            print(i, "_")

        strikes = 0
        while True:
            guessed_character = input("Geben Sie einen Buchstaben ein: ")
            if guessed_character in word:
                strike = 0
                print(guessed_character, "ist dabei")
                for i, character in enumerate(word, 1):
                    print(
                        i,
                        guessed_character
                        if character == guessed_character
                        else "_",
                    )
                answer = input("Wollen Sie raten? 'ja' oder 'nein'? ")
                if answer == "ja":
                    guessed_word = input("Wie lautet das Wort? ")
                    if word == guessed_word:
                        print(" Das ist korrekt! Herzlichen Glückwunsch!")
                        break

                    strike = 1
                    print("Leider nicht korrekt")
            else:
                strike = 1
                print(guessed_character, "ist nicht dabei")

            if strike:
                strikes += strike
                if strikes < MAX_STRIKES:
                    print(strikes, "von", MAX_STRIKES, "Fehlversuchen.")
                else:
                    print("Leider verloren! Das Wort war: ", word)
                    break

        while True:
            answer = input("Noch eine Runde 'ja' oder 'nein'? ")
            if answer == "ja":
                print("Neues Spiel, neues Glück!")
                break

            if answer == "nein":
                print("Vielen Dank fürs Spielen. Auf Wiedersehen!")
                return

            print("Bitte geben Sie 'ja' oder 'nein' ein!")


if __name__ == "__main__":
    main()
Für meinen Geschmack ist das zu lang für eine Funktionen. Man könnte mindestens das Spielen einer Runde in eine eigene Funktion heraus ziehen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten