tictactoe(anfänger)

Code-Stücke können hier veröffentlicht werden.
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Grüß Euch,

ich hab hier ein tic-tac-toe spiel erstellt, funktioniert soweit auch. Für Anregungen was ich besser machen kann oder Sachen die ich vermeiden sollte, wär ich wie immer sehr erfreut. :D

Code: Alles auswählen

from random import randint

fields = [' * ', ' * ', ' * ', ' * ', ' * ', ' * ', ' * ', ' * ', ' * ']

def position(mark, position_field, turn, cpu_level):
    if fields[position_field] != ' * ' :
        if turn == 'player':
            print 'not possible'
            player(cpu_level)
        if turn == 'cpu':
            cpu(cpu_level)
    else:
        print
        fields[position_field] = mark
        print ' '.join(fields[0:3])
        print ' '.join(fields[3:6])
        print ' '.join(fields[6:9])
        print
        print '-----------'
        print
        check_winner(turn, mark, cpu_level)
        
def player(cpu_level):
    mark = ' X '
    turn = 'player'
    position_field = int(raw_input('take position: '))-1
    if position_field >= 9 or position_field < 0:
        print 'Position to high!'
        player(cpu_level)
    else:
        position(mark, position_field, turn, cpu_level)

def cpu(cpu_level):
    mark = ' O '
    turn = 'cpu'
    if cpu_level == 'l':
        cpu_random(cpu_level)
    elif cpu_level == 'h':
        chance_1 = fields[0], fields[1], fields[2], 0, 1, 2
        chance_2 = fields[3], fields[4], fields[5], 3, 4, 5
        chance_3 = fields[6], fields[7], fields[8], 6, 7, 8
        chance_4 = fields[0], fields[3], fields[6], 0, 3, 6
        chance_5 = fields[1], fields[4], fields[7], 1, 4, 7
        chance_6 = fields[2], fields[5], fields[8], 2, 5, 8
        chance_7 = fields[0], fields[4], fields[8], 0, 4, 8
        chance_8 = fields[2], fields[4], fields[6], 2, 4, 6
        chance_9 = [fields[4], fields[2], fields[0], fields[6], fields[8], 4, 2, 0, 6, 8]
        chance_10 = [fields[1], fields[3], fields[5], fields[7], 1, 3, 5, 7]
        chance_list = [chance_1, chance_2, chance_3, chance_4, chance_5, chance_6, chance_7, chance_8]
        # 1.Check for chance to win
        for chance in chance_list:
            if chance.count(' O ') == 2:
                for pos in chance:
                    if pos == ' * ':
                        new_pos = chance.index(pos)+((len(chance)+1)/2)
                        position_field = chance[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 2.Check to defend players win
        for chance in chance_list:
            if chance.count(' X ') == 2:
                for pos in chance:
                    if pos == ' * ':
                        new_pos = chance.index(pos) + ((len(chance)+1)/2)
                        position_field = chance[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 3.Defend player trick set cpu to corner and middle points or set chance to win
        if chance_9.count(' X ') == 2:
            if chance_9[0] == ' X ':
                for pos in chance_9:
                    if pos == ' * ':
                        new_pos = chance_9.index(pos) + (len(chance_9)+1)/2
                        position_field = chance_9[new_pos]
                        position(mark, position_field, turn, cpu_level)
            else:
                for pos in chance_10:
                    if pos == ' * ':
                        new_pos = chance_10.index(pos) + ((len(chance_10)+1)/2)
                        position_field = chance_10[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 4.Set at first middle point or corner point
        for pos in chance_9:
            if pos == ' * ':
                new_pos = chance_9.index(pos) + (len(chance_9)+1)/2
                position_field = chance_9[new_pos]
                position(mark, position_field, turn, cpu_level)
    
def cpu_random(cpu_level):
    mark = ' O '
    turn = 'cpu'
    position_field = randint(0, 8)
    position(mark, position_field, turn, cpu_level)           

def check_winner(turn, mark, cpu_level):
    if fields[0] == fields[1] == fields[2] == mark:
        winner(turn)
    if fields[3] == fields[4] == fields[5] == mark:
        winner(turn)
    if fields[6] == fields[7] == fields[8] == mark:
        winner(turn)
    if fields[0] == fields[3] == fields[6] == mark:
        winner(turn)
    if fields[1] == fields[4] == fields[7] == mark:
        winner(turn)
    if fields[2] == fields[5] == fields[8] == mark:
        winner(turn)
    if fields[0] == fields[4] == fields[8] == mark:
        winner(turn)
    if fields[2] == fields[4] == fields[6] == mark:
        winner(turn)
    if fields.count(' * ') == 0:
        turn = 'draw.'
        winner(turn)
    if turn == 'player':
        cpu(cpu_level)
    if turn == 'cpu':
        player(cpu_level)

def winner(turn):
    if turn == 'draw.':
        print turn
        print
    else:
        print ('The winner is %s .' % (turn))
        print
    new_game = raw_input('new game? type n for new game e for exit: ')
    if new_game == 'n':
        for pos in range(len(fields)):
            fields[pos] = ' * '
        main()
    if new_game == 'e':
        exit()

def main():
    print fields
    print 'Welcome to XXO!'
    print
    print 'CPU = O '
    print 'Player = X '
    print
    print 'Gamefield:'
    print
    print ' '.join(' 1  2  3 ')
    print ' '.join(' 4  5  6 ')
    print ' '.join(' 7  8  9 ')
    print
    beginner = raw_input('Who starts? type p for player, c for cpu?: ')
    cpu_level = str(raw_input('Set cpu Level. type l for low, h for hard. '))
    print
    if beginner == 'p':
        player(cpu_level)
    if beginner == 'c':
        cpu(cpu_level)

if __name__ == '__main__':
    main()
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Der Code ist mir jetzt etwas zu unübersichtlich, deswegen mag ich hier mal nur etwas abstrahierte Kritik geben:
  • Du vermischst Logik/Datenstrukturen und Darstellung zu sehr. Programme gliedert man üblicherweise in Datenstrukturen, "Geschäftslogik" - also Dinge mit den Daten anstellen - und Darstellung der Daten; wobei bei Objektorientierter Programmierung Datenstrukturen und Logik gerne auch zusammengewürfelt werden (vgl. Klassen und Methoden).

    Konkret könnten erste Schritte in diese Richtung so aussehen, dass du dein Programm aufteilst in eine Hauptschleife, die mit dem Benutzer interagiert und Dinge ausgibt, und Funktionen, die entsprechend der Aktionen des Nutzers Werte übergeben bekommen, die Spiellogik durchführen und ggf. Werte an die Hauptschleife zurückgeben, die diese dann dem Benutzer ausgibt.
  • Deine Funktionen sind zu lang, machen zu viele Dinge und sind zu tief verschachtelt. Zusammen mit Punkt 1) machen kleine, übersichtliche Funktionen dein Programm einfacher und übersichtlicher.
  • Du solltest dir Gedanken über die Datenstruktur machen, die zum Speichern des Feldzustands verwendet wird. Bei einer Größe von 3x3 macht eine durchgehende Nummerierung vielleicht noch keine Probleme, aber wenn dein Programm größer wird - wo würde sich z.B. Feld 183 befinden bei einer Größe von 15x15? Hier bietet sich zum Beispiel eine dreidimensionale Liste oder ein Dictionary mit (x,y)-Keys an:

    Code: Alles auswählen

    {(0, 0) : True, (0, 1) : False, (0, 2) : None, ... (3, 3) : None}
    wobei True, False und None jeweils für Spieler O, Spieler X und "leeres Feld" stehen könnten.

Und hier noch etwas konkrete Code-Kritk:

Code: Alles auswählen

        chance_1 = fields[0], fields[1], fields[2], 0, 1, 2
        chance_2 = fields[3], fields[4], fields[5], 3, 4, 5
        chance_3 = fields[6], fields[7], fields[8], 6, 7, 8
        chance_4 = fields[0], fields[3], fields[6], 0, 3, 6
        chance_5 = fields[1], fields[4], fields[7], 1, 4, 7
        chance_6 = fields[2], fields[5], fields[8], 2, 5, 8
        chance_7 = fields[0], fields[4], fields[8], 0, 4, 8
        chance_8 = fields[2], fields[4], fields[6], 2, 4, 6
        chance_9 = [fields[4], fields[2], fields[0], fields[6], fields[8], 4, 2, 0, 6, 8]
        chance_10 = [fields[1], fields[3], fields[5], fields[7], 1, 3, 5, 7]
        chance_list = [chance_1, chance_2, chance_3, chance_4, chance_5, chance_6, chance_7, chance_8]
        # 1.Check for chance to win
        for chance in chance_list:
            if chance.count(' O ') == 2:
                for pos in chance:
                    if pos == ' * ':
                        new_pos = chance.index(pos)+((len(chance)+1)/2)
                        position_field = chance[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 2.Check to defend players win
        for chance in chance_list:
            if chance.count(' X ') == 2:
                for pos in chance:
                    if pos == ' * ':
                        new_pos = chance.index(pos) + ((len(chance)+1)/2)
                        position_field = chance[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 3.Defend player trick set cpu to corner and middle points or set chance to win
        if chance_9.count(' X ') == 2:
            if chance_9[0] == ' X ':
                for pos in chance_9:
                    if pos == ' * ':
                        new_pos = chance_9.index(pos) + (len(chance_9)+1)/2
                        position_field = chance_9[new_pos]
                        position(mark, position_field, turn, cpu_level)
            else:
                for pos in chance_10:
                    if pos == ' * ':
                        new_pos = chance_10.index(pos) + ((len(chance_10)+1)/2)
                        position_field = chance_10[new_pos]
                        position(mark, position_field, turn, cpu_level)
        # 4.Set at first middle point or corner point
        for pos in chance_9:
            if pos == ' * ':
                new_pos = chance_9.index(pos) + (len(chance_9)+1)/2
                position_field = chance_9[new_pos]
                position(mark, position_field, turn, cpu_level)
Da sind viel zu viele Wiederholungen drin. Wenn du immer wieder das selbe (wenn auch in leicht abgewandelter Form, was zählt ist das "Schema") schreibst, machst du was falsch und du solltest dir überlegen, wie man das abstrakter, kürzer, also "eleganter" formulieren könnte. Da solche Wiederholungen oft immer der gleiche Code mit anderen Parametern sind, lässt sowas meist abkürzen, indem man die sich ändernden Parameter in eine Liste (oder eine andere Datenstruktur) speichert und dann mit einer Schleife drüberiteriert.

Hoffe das hilft :-)
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

marlene hat geschrieben:ich hab hier ein tic-tac-toe spiel erstellt, funktioniert soweit auch.
http://xkcd.com/832/
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Hi,

@Dauerbaustelle, die ersten zwei Punkte von dir sind glaub ich mein Hauptproblem an der ganzen Sache, an der ich noch zu arbeiten habe.
Was ist wenn die Hauptschleife mehr Parameter benötigt als Funktionen die, die Spiellogik druchführen und ergebnise zurückliefern?

Zum dritten Punkt mit den Feldern hab ich mir vorher gedanken gemacht. Es werden hier nicht mehr als 3x3 Felder gebraucht. Wenn ich den Code, wo
anders benutzen wollte hast du natürlich recht, dann funktioniert das so nicht.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

marlene hat geschrieben: @Dauerbaustelle, die ersten zwei Punkte von dir sind glaub ich mein Hauptproblem an der ganzen Sache, an der ich noch zu arbeiten habe.
Was ist wenn die Hauptschleife mehr Parameter benötigt als Funktionen die, die Spiellogik druchführen und ergebnise zurückliefern?
Na dann bekommt deine Hauptschleife halt die benötigten Parameter und gibt an die Logik-Schnipsel jeweils nur die Parameter weiter, die dort benötigt werden.
Zum dritten Punkt mit den Feldern hab ich mir vorher gedanken gemacht. Es werden hier nicht mehr als 3x3 Felder gebraucht. Wenn ich den Code, wo
anders benutzen wollte hast du natürlich recht, dann funktioniert das so nicht.
Ich wollte auch noch auf was anderes raus: Mit einer solchen Datenstruktur, also eine flachen Liste, die aber ein zweidimensionales Feld darstellt, ist es relativ schwer, verständlichen Code zu schreiben. Du hast jetzt ständig irgendwelche "magischen" Zahlen im Code, die auf bestimmte Stellen in der Liste verweisen. Sowas sollte man unbedingt vermeiden -- woher soll man als fremder Codeleser denn wissen, was diese 5 da jetzt bedeutet?
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Ich wollte auch noch auf was anderes raus: Mit einer solchen Datenstruktur, also eine flachen Liste, die aber ein zweidimensionales Feld darstellt, ist es relativ schwer, verständlichen Code zu schreiben. Du hast jetzt ständig irgendwelche "magischen" Zahlen im Code, die auf bestimmte Stellen in der Liste verweisen. Sowas sollte man unbedingt vermeiden -- woher soll man als fremder Codeleser denn wissen, was diese 5 da jetzt bedeutet?
Ok, vielen Dank ich versteh was du meinst. Ich brauch mehr Übung, um solche "Sachen" zu vermeiden und Programme richtig aufzubauen. Ich war schon froh das ich das Spiel überhaupt hinbekommen habe. Aber ich bin gewillt mir einen vernünftigen "Programmierstil" anzueignen.


Ich hab hier noch ein anderes Spiel (hat zwar nicht viel Sinn, geht um die Übung), es ist ein Würfelspiel(1 verliert). Ich hoffe das es von der Logik schon ein wenig besser ist.

Code: Alles auswählen

from random import randint
from time import sleep

#Hauptschleife
def game(players, points, turn, first_round, win, win_points):
    print
    print('Punktestand: %s ') % (zip(players, points))
    win = winner(points, players, win_points) 
    if win == True:
        print('%s hat gewonnen.') % (players[turn-1])
        print
        new_game = raw_input('Neues Spiel ja(j) nein(n): ')
        if new_game == 'j':
            main()
        else:
            exit()
    print
    if first_round == True:
        print('Wilkommen zu 1 verliert')
        print('Es wird ausgewuerfelt wer anfaengt')
        turn = beginner(players)
        print('%s feangt an.') % (players[turn])
        raw_input('Enter druecken zum wuerfeln.')
        dice(points, turn, players, win_points)
    else:
        print('%s ist dran.') % (players[turn])
        raw_input('Enter druecken zum wuerfeln.')
        dice(points, turn, players, win_points)

def dice(points, turn, players, win_points):
    chance = 0
    cache = []
    first_round = False
    while chance < 4:
        cube = play_dice()
        print('Wuerfeln....  %s') %(cube)
        if cube == 1:
            print('Sie haben 1 gewuerfelt und bekommen 0 Punkte.')
            chance = 5
            cache = []
        else:
            chance += 1
            cache.append(cube)
    win = False
    points = score(points, turn, cache)
    turn = set_turn(turn, players)
    game(players, points, turn, first_round, win, win_points) 

#Funktionen           
def beginner(players):
    turn = randint(0, (len(players)-1))
    return turn

def play_dice():
    cube = randint(1, 6)
    sleep(0.8)
    return cube
     
def score(points, turn, cache):
    for cube in cache:
        points[turn] += cube
    return points

def set_turn(turn, players):
    turn += 1
    if turn == len(players):
        turn = 0
    return turn

def winner(points, players, win_points):
    for pos in points:
        if pos >= win_points:
            win = True
            return win
    win = False
    return win 

def main():
    players = []
    points = []
    turn = 0
    first_round = True
    win = False
    rules = raw_input('Spielregeln anzeigen ja(j) nein(n): ')
    if rules == 'j':
        print('Es wird reihum gewuerfelt. Wer die hoechsten Augenzahl hat in der ersten Runde darf anfangen.\n\
Nun darf jeder Spieler viermal wuerfeln. Die Augen werden immer addiert wuerfelt er eine\n\
Einswird der gesamte Wurfungueltig und der naechste ist dran.')
    players_number = int(raw_input('Spieleranzahl: '))
    for number in range(players_number):
        player = raw_input('Name Spieler: ')
        players.append(player)
        points.append(0)
    win_points = int(raw_input('Punkte zum Gewinn angeben: '))  
    game(players, points, turn, first_round, win, win_points)

if __name__ == '__main__':
    main()
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hm. Da ist aber wieder Logik und Ausgabe gemischt.

Vielleicht klappt ja Folgendes: Lösche mal aus dem Code alles, was mit Benutzerinteraktion zu tun hat (Ausgaben, Eingaben, Warten, ...). Den resultierenden Code speicherst du in ein Modul.

Dann schreibst du dir ein zweites Modul, das mit dem Benutzer interagiert und Logik aus dem Logikmodul aufruft.

Wenn der Logik-Part deines Programms ``print`` or ``raw_input`` enthält, machst du etwas falsch (außer zu Debug-Zwecken der Entwicklung natürlich, da ist ``print`` Werkzeug Nummer 1 :-).

Im Übrigen solltest du die rekursiven Aufrufe vermeiden (main ruft game auf ruft main auf / game ruft dice auf ruft game auf) und stattdessen eine wirkliche Schleife verwenden.
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Hi Dauerbaustelle,

ich hoffe, das dies hier ein klein wenig besser ist (def game ist wohl zu unübersichtlich), möchte aber gerne deine Meinung haben wie das ganze jetzt ausschaut? :roll:

1. Teil user

Code: Alles auswählen

import logik

def game(players, points, win_points):
    turn = 0
    win = False
    first_round = True
    while win == False:
        print
        print('Punktestand: %s ') % (zip(players, points))
        print
        if first_round == True:
            print('Es wird ausgewuerfelt wer anfaengt')
            turn = logik.set_beginner(players)
            first_round = logik.set_first_round(first_round)
            print('%s feangt an.') % (players[turn])
            print
        else:
            turn = logik.set_turn(turn, players)
            print('%s ist dran.') % (players[turn])
            print
        raw_input('Enter druecken zum weurfeln.')
        cache = logik.play_dice()
        for cube in cache:
            sleep(0.8)
            print('wuerfeln....  %s') % (cube)
            if cube == 1:
                print('Sie haben 1 gewuerfelt, und bekommen 0 Punkte.')
                cache = logik.set_cache(cache)
                break
        points = logik.set_points(points, turn, cache)
        if logik.check_winner(points, players, win_points) == True:
            print('%s hat gewonnen.') % (players[turn])
            print
            win = logik.set_win(win)
    new_game = raw_input('Neues Spiel ja(j) nein(n)? :')
    if new_game == 'j':
        main()
    else:
        exit()
                  
def main():
    players = []
    points = []
    rules = raw_input('Spielregeln anzeigen ja(j) nein(n): ')
    if rules == 'j':
        print("""
        Es wird reihum gewuerfelt. Wer die hoechste Augenzahl
        in der ersten Runde hat darf anfangen.
        Nun darf jeder Spieler viermal wuerfeln.
        Die Augen werden immer addiert, wuerfelt man eine
        Eins wird der gesamte Wurf ungueltig und der naechste ist dran.
             """)
    players_number = int(raw_input('Spieleranzahl: '))
    for number in range(players_number):
        player = raw_input('Name Spieler: ')
        players.append(player)
        points.append(0)
    win_points = int(raw_input('Punkte zum Gewinn angeben: '))  
    game(players, points, win_points)
    
if __name__ == '__main__':
    main()
2. Teil Logik

Code: Alles auswählen

from random import randint

def set_beginner(players):
    turn = randint(0, (len(players)-1))
    return turn

def set_first_round(first_round):
    first_round = False
    return first_round

def set_turn(turn, players):
    turn += 1
    if turn == len(players):
        turn = 0
    return turn

def play_dice():
    chance = 0
    cache = []
    while chance < 4:
        cube = randint(1, 6)
        cache.append(cube)
        chance += 1
    return cache

def set_cache(cache):
    cache = [0]
    return cache

def set_points(points, turn, cache):
    for cube in cache:
        points[turn] += cube
    return points
          
def check_winner(points, players, win_points):
    for pos in points:
        if pos >= win_points:
            winner = True
            return winner
    winner = False
    return winner

def set_win(win):
    win = True
    return win
Merci
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Cool! Das ist schonmal so übersichtlich, dass man jetzt anfangen kann, den Code ansich zu verbessern... Hier ein paar Sachen, die mir grade auffallen:
  • Den Code zur ersten Runde würde ich aus der Hauptschleife rausnehmen (also einfach davor platzieren). Der Part wird ja eh garantiert nur ein Mal ausgeführt, daher hat er in der Schleife nix zu suchen. Damit sparst du dir auch ``first_round``.
  • Vergleiche mit ``False`` schreibt man als ``not x`` anstatt ``x == False``.
  • Vergleiche mit ``True`` macht man gar nicht, weil ``if a:`` äquivalent zu ``if a == True`` ist, man schreibt also ersteres.
  • ``set_first_round`` ergibt keinen Sinn. Da wird gar nichts gesetzt, sondern einfach immer ``False`` zurückgegeben. Im Allgemeinen würde ich Funktionen nur mit "set" bennen, falls sie wirklich einen Wert eines Objektes setzen. Deine Funktionen aber geben nur etwas zurück (ändern also nicht aktiv etwas).
  • Du musst einen Wert nicht erst an einen Namen binden, um ihn zurückzugeben. ``a = 1; return a`` ist äquivalent mit ``return 1``, und weil das letztere weniger Code braucht sollte das bevorzugt werden. (Beispiele hierfür: ``set_cache``, ``set_win``, ``check_winner`` etc).
Was mir bei genauerem Hinsehen noch auffällt: Die von dir gewählte Datenstruktur für das Speichern der Spieler und des Punktestandes ist nicht besonders gut. Was du willst ist ein "Spieler-Punktestand"-Mapping. Schau dir mal Dictionaries an.

Vielleicht könntest du den ganzen Logik-Part auch Klassen-basiert schreiben. Der Benutzerinteraktions-Part erstellt dann zu Spielbeginn ein neues Objekt der Klasse und ruft die Methoden (Funktionen von Klassen) dieser Klasse auf. Dann würde man auch den Spielstand etc. in diesem Klassenobjekt speichern und müsste ihn nicht jedes Mal übergeben.

Jede Menge Stoff zu lernen! :-)
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Dauerbaustelle hat geschrieben:Cool!
Cool is schon mal gut.

Deine Verbesserungen find ich sehr gut, auf einiges hätt ich auch selber kommen sollen. Ich hab das ganze mal überarbeitet, nur das mit dem dict. dafür habe ich noch keine gute Lösung.

zum dict:
Ich erstelle eine liste aller Schlüssel vom dict. players, um den beginner des Spiels nach zufalls index festzulegen, im weiteren verlauf benutze ich diese liste auch weiter. Dadurch habe ich eigentlich eine Variable mehr die ich eigentlich nicht bräuchte oder?

Code: Alles auswählen

from time import sleep
import logik

def game(players, win_points):
    turn = 0
    win = False
    players_list = list(players)
    print
    print('Es wird ausgewuerfelt wer anfaengt')
    turn = logik.beginner(players_list)
    print('%s feangt an.') % (players_list[turn])
    while win is False:
        print
        print('Punktestand: %s ') % (players)
        print
        print('%s ist dran.') % (players_list[turn])
        raw_input('Enter druecken zum weurfeln.')
        cache = logik.play_dice()
        for cube in cache:
            sleep(0.8)
            print('wuerfeln....  %s') % (cube)
            if cube == 1:
                print('Sie haben 1 gewuerfelt, und bekommen 0 Punkte.')
                cache = logik.cache(cache)
                break
        players = logik.points(players, players_list, turn, cache)
        if logik.check_winner(players, win_points):
            print('%s hat gewonnen.') % (players_list[turn])
            print
            print players
            print
            win = logik.win(win)
        turn = logik.turn(turn, players_list)
    new_game = raw_input('Neues Spiel ja(j) nein(n)? :')
    if new_game == 'j':
        main()
    else:
        exit()
                  
def main():
    players = {}
    rules = raw_input('Spielregeln anzeigen ja(j) nein(n): ')
    if rules == 'j':
        print("""
        Es wird reihum gewuerfelt. Wer die hoechste Augenzahl
        in der ersten Runde hat darf anfangen.
        Nun darf jeder Spieler viermal wuerfeln.
        Die Augen werden immer addiert, wuerfelt man eine
        Eins wird der gesamte Wurf ungueltig und der naechste ist dran.
             """)
    players_number = int(raw_input('Spieleranzahl: '))
    for number in range(players_number):
        player = raw_input('Name Spieler: ')
        players[player] = 0
    win_points = int(raw_input('Punkte zum Gewinn angeben: '))
    game(players, win_points)
    
if __name__ == '__main__':
    main()

Code: Alles auswählen

from random import randint

def beginner(players_list):
    turn = randint(0, (len(players_list)-1))
    return turn

def turn(turn, players_list):
    turn += 1
    if turn == len(players_list):
        turn = 0
    return turn

def play_dice():
    chance = 0
    cache = []
    while chance < 4:
        cube = randint(1, 6)
        cache.append(cube)
        chance += 1
    return cache

def cache(cache):
    return [0]

def points(players, players_list, turn, cache):
    name = players_list[turn]
    for cube in cache:
        players[name] += cube
    return players
          
def check_winner(players, win_points):
    for value in players.values():
        if value >= win_points:
            return True
    return False

def win(win):
    return True
Vielleicht könntest du den ganzen Logik-Part auch Klassen-basiert schreiben. Der Benutzerinteraktions-Part erstellt dann zu Spielbeginn ein neues Objekt der Klasse und ruft die Methoden (Funktionen von Klassen) dieser Klasse auf. Dann würde man auch den Spielstand etc. in diesem Klassenobjekt speichern und müsste ihn nicht jedes Mal übergeben.
Das klingt interessant. Daran werd ich mich mal versuchen.
BlackJack

@marlene: Die Vokabeln sind teilweise etwas "off". `cube` sollte wohl `dice` heissen, denn `cube` ist die geometrische Form, nicht das Ding mit den Augen drauf. Und warum `cache` so heisst, kann ich nicht nachvollziehen!?

Die Rekursion zwischen `game()` und `main()` solltest Du unbedingt loswerden. Das macht man in Sprachen die keine Endrekursion wegoptimieren können nicht weil Du damit bei jedem Aufruf mehr Speicher belegst, der eigentlich gar nicht mehr benötigt wird. Das macht im Fehlerfall bei Ausnahmen den Stacktrace einfach nur unnötig lang und in CPython triffst Du damit irgendwann auf das Rekursionslimit und das Programm bricht deswegen ab.

In der Logik lassen sich einige Funktionen kürzer schreiben. Statt `randint()`, könntest Du `randrange()` verwenden wenn die Endzahl nicht enthalten sein soll. Und ein Ergebnis in einer Zeile an einen Namen zu binden, nur um den in der nächsten Zeile mit einem ``return`` zu verwenden, muss nicht unbedingt sein.

Die Namen der Funktionen könnten teilweise besser sein. Die sollten beschreiben was die funktion tut, also den Namen einer Tätigkeit haben.

"beginner" heisst "Anfänger" im Sinne von "jemand der noch wenig Erfahrung hat".

Wenn man vor Eintritt in eine Schleife weiss, wie oft sie durchlaufen werden wird, dann nimmt man eine ``for``- und keine ``while``-Schleife.

Der Sinn von `cache()` und `win()` ist mir rätselhaft. Die Funktionsnamen sagen mir nichts und die Rückgabewerte scheinen wenig Sinn zu machen.

`points()` ist ein wenig umständlich. Die Daten für einen Spieler sollten nicht so über mehrere Datenstrukturen verteilt sein. Die Funktion braucht keinen Rückgabewert, weil sie die Datenstruktur ja verändert.

Komplett ungetestet:

Code: Alles auswählen

def pick_first_player(players):
    return randrange(0, len(players))


def next_player_index(index, players):
    return (index + 1) % len(players)


def roll_dices(count=4):
    return [randint(1, 6) for _ in xrange(count)]


def add_points_to_player(index2player_name,
                         player_name2points,
                         index,
                         dice_values):
    player_name2points[index2player_name[index]] += sum(dice_values)


def check_winner(players, points):
    return any(v > points for v in players.itervalues())
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

@black jack, seit deiner Antwort ist jetzt zwar schon einige Zeit vergangen, hätte aber trotzdem ein paar fragen.

1. Mit den Vokabeln hast du recht. Muss ich mir angewöhnen.

2. Die Rekursion zwischen `game()` und `main()`. Wie kann ich dann ein neues Spiel starten, ohne das Script neu zu starten?

3. 'Der Sinn von cache'. Ich wollte in der Liste "cache", die gefallenen Würfel speichern, und die Liste dann mit zeitverzögerung ausgeben. Danach die erreichte Punktzahl dem Spieler zuweisen, außer in der Liste ist ein Würfel mit der Augenzahl 1, 1 = 0 Punkte in der Runde.

4.'Der Sinn von win'. Braucht man nicht :roll:

Noch eine Frage zu deinem code:

Code: Alles auswählen

def pick_first_player(players):
    return randrange(0, len(players))
Gibt mir ja eine Zahl zurück, wie kann ich diese Zahl in einem dictionary verwenden?
z.B: Es gibt 4 Spieler, pick_first_player(players) gibt mir zahl 2 zurück, wie kann ich auf den zweiten spieler im dict zugreifen, ist das überhaupt möglich?


Merci
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

marlene hat geschrieben: 2. Die Rekursion zwischen `game()` und `main()`. Wie kann ich dann ein neues Spiel starten, ohne das Script neu zu starten?
Z.B. indem Du eine unendliche Schleife baust, aus der heraus Du einzelne Spiele ("game()" bei Dir) startest. Aus dieser Funktion kehrst Du dann einfach zurück in die Schleife und kannst dann eben erneut ein Spiel spielen:

Code: Alles auswählen

def game():
    # hier findet das eigentliche Spiel statt

def menu():
    while True:
        choice = raw_input("Noch ein Spiel (yes/no)?")
        if choice == "no":
            # Schleife wird durch Spielerentscheid verlassen
            break
        # Spieler will noch mal, also los gehts!
        game()
        # nach Beendingung geht es oben im Schleifenkopf weiter
        # Die Bedingung ist immer wahr -> solange bis Spieler sich gegen
        # Fortsetzung entscheidet wird die Schleife weiter durchlaufen
marlene hat geschrieben: Noch eine Frage zu deinem code:

Code: Alles auswählen

def pick_first_player(players):
    return randrange(0, len(players))
Gibt mir ja eine Zahl zurück, wie kann ich diese Zahl in einem dictionary verwenden?
z.B: Es gibt 4 Spieler, pick_first_player(players) gibt mir zahl 2 zurück, wie kann ich auf den zweiten spieler im dict zugreifen, ist das überhaupt möglich?
Wenn Du die Spieler in einem Dictionary speicherst, dann solltest Du eben einen zufälligen Schlüssel auswählen. Dafür kannst Du z.B. random.choice() benutzen und als Parameter die Schlüssel übergeben.

Allerdings kann ich mir noch nicht vorstellen, wie Du sinnvoll Spieler in einem dict speicherst? Vielleicht zeigst Du dieses Datenkonstrukt einmal?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Nja ich wollte, durch Anregung von Dauerbaustelle,
Was du willst ist ein "Spieler-Punktestand"-Mapping. Schau dir mal Dictionaries an
die Spieler und Punkte in einem dict. speichern. Vorher hatte ich zwei Listen Spieler und Punkte.
Bei den Listen ist es ja einfach zum nächsten eintrag zu kommen, bei den dict. hab ich da ein problem.

Wollte die Spieler und Punkte so speichern:

players = {player1:points, player2:points, usw....}
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

marlene hat geschrieben: Wollte die Spieler und Punkte so speichern:

players = {player1:points, player2:points, usw....}
An sich erst einmal eine gute Idee, aber Du zeigst hier etwas, das unschön aussieht - das kann aber auch am Beispiel liegen! Sobald Du Schlüssel durchnummerierst, machst Du etwas falsch.

Was verbrigt sich denn hinter "player1"? Zeig doch mal konkreten Code mit mehreren "Spielern" und Punkten.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Ich Schreibe die Spieler und Punkte in ein dict.
Das Beispiel war so gedacht:
players = {hans:0, uwe:0, usw....}

Code: Alles auswählen

def main():
    players = {}
    players_number = int(raw_input('Spieleranzahl: '))
    for number in range(players_number):
        player = raw_input('Name Spieler: ')
        players[player] = 0
    
if __name__ == '__main__':
    main()
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

marlene hat geschrieben:Ich Schreibe die Spieler und Punkte in ein dict.
Das Beispiel war so gedacht:

Code: Alles auswählen

players = {"hans":0, "uwe":0, usw....}
Ok, dann picke Dir eben einen zufälligen Key aus dem Dict heraus, wie ich es beschreiben habe.

Code: Alles auswählen

random.choice(players.keys())
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

OK Danke, das versteh ich,

aber wie komm ich zum nächsten Spieler(im dict.)? Nachdem der erste fertig ist.
ein dict hat ja keinen index, wie eine Liste.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

marlene hat geschrieben: aber wie komm ich zum nächsten Spieler(im dict.)? Nachdem der erste fertig ist.
ein dict hat ja keinen index, wie eine Liste.
Ok, dazu musst Du mal den Modus genauer beschreiben, sonst drehen wir uns noch stunden lang im Kreis ;-)

Wenn alle Spieler pro Runde drankommen sollen und das in zufälliger Reihenfolge, so kannst Du das mit shuffle machen:

Code: Alles auswählen

player_keys = players.keys()
random.shuffle(player_keys)
Damit hast Du einen zufällig geordneten Index, mit dem Du alle Spieler erwischst.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
marlene
User
Beiträge: 26
Registriert: Montag 13. September 2010, 12:11

Sorry, das war so gedacht das ein Spieler am Anfang zufällig ausgewählt wird, was ja jetzt auch funktioniert.
Wenn dieser Spieler fertig ist(gewürfelt hat), soll der nächste Spieler im dict dran sein usw. Wenn alle an der Reihe waren soll das ganze von vorne beginnen.

Also:
1. zufälligen Spieler wählen
2. nächsten Spieler im dict.
3. nächsten Spieler im dict.
4. usw.

Nur der erste soll zufällig gewählt werden, ab da soll das wie bei einer "echten Würfelrunde" nach der Reihe weiter gehen.
Antworten