Schiffchen-Versenkis

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
Magnetonstar
User
Beiträge: 14
Registriert: Montag 16. November 2015, 18:46

Hallo zusammen,

Ich lerne ja die Programmiersprache auf der Webseite Codecademy.
Dort musste man als Übung ein Schiffchen-Versenkis programmieren. Nun wollte ich euch fragen, ob mein programmiertes Schiffchen-Vesenkis optimal ist.
Könnt ihr mir also sagen, an welchen Stellen ich es viel einfacher programmieren könnte?

Code: Alles auswählen

print("Wilkommen zu Schiffchen-Versenkis!")
print("")

reihen_brett = int(input("Wie gross sollte das Feld sein? "))
brett = []

for reihe in range(reihen_brett):
	brett.append(['O'] * reihen_brett)

def print_brett(brett):
	for reihe in range(reihen_brett):
		print(" ".join(brett[reihe]))

from random import randint
boot_vertikal = randint(1, reihen_brett)
boot_horizontal = randint(1, reihen_brett)

for versuch in range(1, 5):
	print("")
	print("")
	print("Versuch " + str(versuch))
	print_brett(brett)
	guess = [int(input("Reihe: ")), int(input("Stelle: "))]
	print("")

	if guess[0] == boot_horizontal and guess[1] == boot_vertikal:
		print("Du hast mein Boot versenkt!")
		print("Herzlichen Glückwunsch, du hast gewonnen!")
		break

	elif versuch == 4:
		print("Du hast alle deine Versuche verspielt")
		print("Game Over")

	elif guess[0] < 0 or guess[0] > reihen_brett or guess[1] < 0 or guess[1] > reihen_brett:
		print("Das ist garnicht im Feld!")

	elif brett[guess[0]-1][guess[1]-1] == 'X':
		print("Dort hast du schon einmal geschossen.")

	else:
		print("Du hast mein Schiff verfehlt.")
		brett[guess[0]-1][guess[1]-1] = 'X'

input("Drücke Enter um zu Beenden")
Ja ich weiss, das "Drücke Enter..." muss eigentlich nicht sein, aber es hat seine Gründe.

Lg Jerome
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Magnetostar
Ok, was mir aufgefallen ist:
  1. Importe werden in aller Regel an den Anfang eines Moduls (unter Shebang und eventueller Encoding Info) gesetzt. Damit sieht man dann einfach schon mal auf einen Blick, was von außen kommt.
  2. Auch das Starten des Spiels würde ich in eine Funktion stecken. Dadurch hast Du die Möglichkeit, das Programm auch innerhalb der Pythonkonsole zu Testzwecken immer wieder zu starten.
  3. Statt einer `for` Schleife würde ich eine Endlosschleife, die nach einer bestimmten Anzahl Durchläufen verlassen wird, verwenden.
  4. Ich würde das Spielbrett einfach über eine flache Liste darstellen, ohne darin die Reihen und Spalten abzubilden.
  5. Verwende doch durchgängig englische Namen...
Hab' Deinen Code mal dahingehend geändert:

Code: Alles auswählen

#!/usr/bin/env python3

from random import randint

ATTEMPTS = 5

 
def display(dimension, mark):
    cursor = 0
    while cursor < dimension * dimension:
        cursor += 1
        print('~' if cursor != mark else 'X',
              end = '' if cursor % dimension  else '\n')

def get_guess(dimension):
    while True:
        row = int(input('Reihe: '))
        col = int(input('Spalte:'))
        guess = (row - 1) * dimension + col
        if guess > dimension * dimension:
            print('Außerhalb des Feldes!', end='\n\n')
        else:
            return guess
 
def main():
    print('Wilkommen zu Schiffchen-Versenkis!', end='\n\n')
    dimension = int(input('Wie gross sollte das Feld sein? '))
    boat_position = randint(1, dimension * dimension)
    attempts = 0
    guesses = []
    while True:
        print('Versuch: {}/{}'.format(ATTEMPTS-attempts, ATTEMPTS), end='\n\n')
        attempts += 1
        guess = get_guess(dimension)
        display(dimension, guess)
        if guess == boat_position:
            print('Du hast mein Boot versenkt!')
            print('Herzlichen Glückwunsch, du hast gewonnen!')
            break
        elif attempts >= ATTEMPTS:
            print('Du hast alle deine Versuche verspielt')
            print('Game Over')
            break
        elif guess in guesses:
            print('Dort hast du schon einmal geschossen.')
        else:
            print('Du hast mein Schiff verfehlt.')
            guesses.append(guess)

if __name__ == '__main__':
    main()
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Magnetonstar: Importe sollten immer ganz am Anfang stehen, dann weiß man sofort, von was das Skript abhängt. Alles was Du auf oberster Ebene an ausführbaren Code hast, sollte in eine Funktion wandern, dann hast Du auch nicht mehr das Problem, dass die Funktion so versteckt ist. Bei print_brett iterierst Du über einen Index, statt direkt über die Liste. Wenn Du die Schiffsposition in einem Tuple speicherst, werden die Vergleiche einfacher. Mit ein paar weiteren Gimmicks kommt ungefähr das raus:

Code: Alles auswählen

from random import randint

def print_field(field):
    for row in field:
        print(" ".join(row))

def create_field(size):
    return [['O'] * size for _ in range(size)]

def main():
    print("Wilkommen zu Schiffchen-Versenkis!")
    print("")

    size = int(input("Wie gross sollte das Feld sein? "))
    field = create_field(size)

    ship_position = randint(1, size), randint(1, size)

    for try_count in range(1, 5):
        print("")
        print("")
        print("Versuch {}",format(try_count))
        print_field(field)
        guess = int(input("Reihe: ")), int(input("Stelle: "))
        print("")

        if guess == ship_position:
            print("Du hast mein Boot versenkt!")
            print("Herzlichen Glückwunsch, du hast gewonnen!")
            break
        elif 0 < guess[0] <= size and 0 < guess[1] <= size:
            if field[guess[0]-1][guess[1]-1] == 'X':
                print("Dort hast du schon einmal geschossen.")
            else:
                print("Du hast mein Schiff verfehlt.")
                field[guess[0]-1][guess[1]-1] = 'X'
        else:
            print("Das ist garnicht im Feld!")
    else:
        print("Du hast alle deine Versuche verspielt")
        print("Game Over")

if __name__ == '__main__':
    main()
@mutetella: was genau macht jetzt eine lineare Liste einfacher? Statt einer while-Schleife und händischem Zählen ist ein for-range-Konstrukt viel lesbarer.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Sirius3
Ich finde halt, dass `field` durch die Angabe, wie groß das Feld sein soll, ausreichend dargestellt ist. Wenn ich also weiß, dass mein Feld z. B. die Ausmaße 5 x 5 hat, weshalb dann noch diese Liste erstellen?

Und die `for` Schleife an Stelle einer `while` Schleife... vielleicht hast Du Recht...

Wahrscheinlich Geschmacksache..., oder?
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Der Struktur des Spielbretts entspricht eine 2D-Liste eher als einer flachen Liste. Man kann dann Reihe und Spalte angeben und die Berechnung der Zelle Python überlassen statt das von Hand selber zu machen. Man kann dann auch Rechtecke ganz einfach als Brett verwenden und zwar ohne das man noch einmal extra die Dimensionen mit der Liste übergeben muss.
Magnetonstar
User
Beiträge: 14
Registriert: Montag 16. November 2015, 18:46

Sirius3 hat geschrieben:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Vielen dank für eure Ratschläge! Was ich aber noch nicht verstehe ich das hier. Für was ist das?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Magnetonstar
Das kann man als Weiche verstehen: Wenn Du das Modul in eine Pythonshell importierst, wird der Name `__name__` an den Namen des Moduls, z. B. 'schiffe', wenn die Datei `schiffe.py` heißt, gebunden. Wenn Du das Modul allerdings direkt ausführst, verweist `__name__` auf '__main__'. Also:

Code: Alles auswählen

Python 3.4.2 (default, Oct  8 2014, 10:45:20) 
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import schiffe
>>> schiffe.__name__
'schiffe'

Code: Alles auswählen

if __name__ == '__main__':
    print(__name__)
    # main()

Code: Alles auswählen

$ python3 ./schiffe.py
__main__
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Magnetonstar hat geschrieben:Für was ist das?
Du kannst auch deine selbst geschriebenen Python Codes per import in anderen Codes wiederverwenden, theoretisch. Bei deinem oben gezeigten Code würde das aber nicht klappen, weil dann alles direkt ausgeführt wird. Deshalb steckt man den Code in eine Funktion namens "main", die nur dann ausgeführt wird, wenn der Code direkt gestartet wird (also bei dir derzeit immer) und nicht zB beim Importen. Das wirkt auf den ersten Blick vielleicht unüblich, macht Code wiederverwenden aber leichter.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Antworten