Xte Schere, Sttein Papier Variante

Code-Stücke können hier veröffentlicht werden.
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

So, hier wäre denn nun mein Allererstes.
Interessant war für mich dabei die Zufallsfunktion und die Zeit.
Da ich die 'Konventionen' noch nicht verinnerlicht haben kann, hoffe ich besonders hier auf konstruktive Kritik/ Tips.
Danke für Aufmerksamkeit!

Code: Alles auswählen

# Python Version: 3.2
# Simulation des Schere-Stein-Papier Spiels
# Bei 100000 Runden Vorgabe braucht mein
# PC fast zwei Sekunden! Lahme Kiste.
import random
from  tkinter import *

f1= Tk()
f1.geometry('140x160')
f1.config(bg='white')
f1.title('w3h1ssp')

def spiel():
    aplay=0    #Player A  Punkte
    bplay=0    #Player B  Punkte
    playp=0    # Patt    
    playz=0    # Rundenzähler
    zw = 0
    while zw < 100000:   # Runden-Vorgabe
        a = random.randint(1,3) 
        b = random.randint(1,3)
        # 1= Stein, 2= Papier, 3=Schere 
        if a == b:
            playp = playp + 1
        else:
            if a > b:
                z = a - b
                if z == 1:
                    aplay =aplay + 1
                else:
                    bplay = bplay + 1
            else:
                z = b - a
                if z == 1:
                    bplay = bplay + 1
                else:
                    aplay = aplay +1
        playz = playz +1                       
        zw = zw+1

    print('Player A: ', aplay)
    print('Player B: ', bplay)
    print('Patt', playp)
    print('Runden', playz)
    atex.delete(0, END)
    atex.insert(INSERT, aplay)
    btex.delete(0, END)
    btex.insert(INSERT, bplay)
    ptex.delete(0, END)
    ptex.insert(INSERT, playp)
    ztex.delete(0, END)
    ztex.insert(INSERT, playz)
    
atex=Entry() 
atex.place(relx=0.05, rely=0.05, relwidth=0.90)
atex.config( font =("Arial", 14))
atex.insert(INSERT, 'Player A')
btex=Entry()
btex.place(relx=0.05, rely=0.20, relwidth=0.90)
btex.config(font =("Arial", 14))
btex.insert(INSERT, 'Player B')
ptex=Entry()
ptex.place(relx=0.05, rely=0.40, relwidth=0.90)
ptex.config(font =("Arial", 12 ))
ptex.insert(INSERT, 'Patt')
ztex=Entry()
ztex.place(relx=0.05, rely=0.55, relwidth=0.90)
ztex.config(font =("Arial", 12))
ztex.insert(INSERT, 'Runden')

strt=Button(command=spiel, text='Start')
strt.config(bg='white', fg='red', font =('Arial', 14))
strt.place(relx=0.05, rely=0.72, relwidth=0.90)

f1 = mainloop()
BlackJack

@wwwheimer: Sternchen-Importe sollte man vermeiden. Die sind in einer interaktiven Shell zum Ausprobieren ganz nützlich und wenn man explizit die Inhalte von Namensräumen zusammen führen möchte, zum Beispiel in einem Package die Untermodule zu einer offiziellen API zusammen führen will. `tkinter` wird oft mittels ``as`` an den Namen `tk` gebunden weil das GUI-Toolkit Tk heisst.

Womit wir bei Namensgebung wären: Abkürzungen nur, wenn sie allgemein bekannt sind. Oder zumindest in der Domäne für die das Programm ist. Wenn man Namen hat, und in einem Kommentar dahinter schreiben muss, was der bedeutet, ist in den meisten Fällen der Name schlecht gewählt. `playp` zum Beispiel sollte `patt_zähler` oder `draw_counter` heissen, dann braucht man im Quelltext, egal wo er benutzt wird, nicht suchen wo denn der Kommentar steht, der den Namen erklärt. Das gilt in `spiel()` für alle Namen, bis auf vielleicht `a` und `b`.

Layout per `place()` sollte man vermeiden. Relative Angaben sind zwar ganz geringfügig besser als Absolute, aber das macht die GUI trotzdem unflexibel. Auch hier kann es Leute geben die Probleme bekommen, weil sie eine Bildschirmauflösung haben, wo die Schrift nicht mehr in die Eingabefelder passt, oder anders herum, so klein sein kann, dass es komisch aussieht. Davon abgesehen ist es äusserst umständlich so ein Layout zu ändern um zum Beispiel ein weiteres Element einzufügen. Hier wäre ein vertikales `pack()`-Layout einfacher und flexibler. Oder ein `grid()`, bei dem man noch die Bedeutung vor den Zahlen ausgeben kann. Ohne das muss man sich ja merken was dort ganz am Anfang mal drin stand, oder auf die Textausgabe schauen.

Wenn man weiss wie oft eine Schleife durchlaufen wird, sollte man eine ``for``-Schleife verwenden. ``while``-Schleifen sind eher für Abbruchbedingungen bei denen man nicht vorhersagen kann, bei welchem Schleifendurchlauf die greifen werden. Zudem enthalten `zw` und `playz` immer den gleichen Wert. Man braucht also nicht wirklich beide.

Zum erhöhen der Zähler könntest Du ``+=`` verwenden.

Du gibst bei keinem Widget das Eltern-Widget an. Ich weiss gar nicht ob es nur Zufall ist, dass es zu funktionieren scheint, aber das sollte man immer machen. Spätestens wenn es mehr als ein Containerwidget in der GUI gibt, muss man sagen wo die Widgets hinein gehören.

Ganz am Ende wird an `f1` (auch ein sehr schlechter Name!) der Rückgabewert der Tk-Hauptschleife gebunden und damit das Hauptfenster ersetzt, was am Anfang mal an den Namen gebunden wird. Warum? Die Funktion gibt auch gar nichts brauchbares zurück.

Auf Modulebene sollten nach Möglichkeit nur Konstanten, Funktionen und Klassen definiert werden. Der „Hauptcode” sollte in einer Funktion verschwinden und über dieses Idiom gestartet werden:

Code: Alles auswählen

if __name__ == '__main__':
    hauptfunktion()  # Name ist oft `main()`.
Dann lässt sich das Modul importieren ohne dass der Code los läuft. Zum Beispiel um den Inhalt vom Modul in anderen Modulen zu verwenden, oder einzelne Komponenten zu testen oder in einer Python-Shell interaktiv aus zu probieren.

Dann müsste in diesem Fall auch `spiel()` als lokale Funktion in der Hauptfunktion verschwinden, oder die GUI-Elemente müssten als Argumente übergeben werden. Vielleicht sollte man die eigentlich Simulation auch von der Ausgabe in der GUI trennen, so dass `spiel()` nur die Zahlen ermittelt, und der Aufrufer dann dafür zuständig ist sie auszugeben. Wo auch immer dass dann gemacht werden soll.

Mit Leerzeichen könntest Du etwas konsistenter umgehen. Übliche Konvention sind Leerzeichen links und rechts von Operatoren und dem ``=`` bei Zuweisungen. Ausnahme: Zuweisungen in Argumentlisten — da wird auf die Leerzeichen verzichtet. Ausserdem ein Leerzeichen nach Kommas.

Edit: Eine Alternative zu den ``if``\s um den Gewinner zu ermitteln:

Code: Alles auswählen

def simulate_match(rounds):
    """Plays given number of rounds and returns the results as a sequence
    of the form ``[points_player_1, points_player_2, draws]``.
    """
    result = [0, 0, 0]
    for _ in xrange(rounds):
        a, b = randrange(3), randrange(3)
        result[2 if a == b else int(a == (b + 1) % 3)] += 1
    assert sum(result) == rounds
    return result
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

BlackJack, bin noch bei den Basics!

Die Sternchen-Importe: ein Fehler, weil ich es noch nicht besser weiß. Habe es probiert und bin bis 'from tkinter import Tk, Button, Entry' gekommen. Dann fehlte aber immer noch das Modul mit der insert(INSERT,)-Funktion, und mir fehlte die Lust einem Problem nachzusteigen das sich mit der Erfahrung von selbst erledigt. Natürlich ist es richtig nur das zu laden was auch benötigt wird. Nur hilft dieser Hinweis kaum weiter, wenn nicht aufgezeigt wird wie es im konkreten Fall richtig wäre.

Dass die Funktion so an die Entrys gebunden ist und von Widgets weiß, von denen sie nichts wissen sollte, das ist schlecht und Deine Kritik berechtigt.

Was ich aber nicht verstehe, dass ist das mit der mainloop() Ich durchschaue das System dahinter einfach noch nicht und mache es wie 'man' es mir in vielen Beispielen gezeigt hat. Es funktioniert ja auch (irgendwie).;)

Auf die anderen, von Dir angesprochenen Punkte, komme ich später vielleicht noch mal. Jedenfalls erstmal vielen Dank für Deine Mühe!
BlackJack

@wwwheimer: Bei dem `INSERT` hast Du wahrscheinlich das `INSERT` nicht importiert, also einen `NameError` an der Stelle bekommen. Der Name kommt beim Sternchen-Import ja auch aus dem `tkinter`-Modul. Namen müssen ja irgend wann einmal an einen Wert gebunden werden, bevor man sie benutzen kann. Ausser den Namen im `builtins`-Modul (vor Python 3 hiess das `__builtins__`), die überall einfach so zur Verfügung stehen, müssen alle anderen Namen in einem Modul vorher irgendwie definiert werden. Wenn sie in anderen Modulen definiert sind, muss man sie importieren oder das Modul importieren und sie als Attribute des importierten Moduls ansprechen. Siehe auch das Tutorial: http://docs.python.org/py3k/tutorial/modules.html

Ergänzend dazu gibt es eben noch die Möglichkeit ein Modul zu importieren und an einen anderen Namen zu binden: ``import tkinter as tk``.

Was genau verstehst Du denn nicht an der `mainloop()`? Das ist die Hauptschleife der GUI. Da wird die GUI-Anzeige aktualisiert und wenn der Benutzer etwas macht, werden die Rückruffunktionen aufgerufen, die man für bestimmte Ereignisse hinterlegt hat. Zum Beispiel für das betätigen einer Schaltfläche.
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

Doch, ich hatte INSERT richtig importiert, aber das war noch nicht alles. Habe ein wenig herumprobiert und noch END dazugeholt. Jetzt funktioniert es und die Importliste sieht so aus: from tkinter import Tk, Entry, Button, INSERT, END

Das mit der mainloop kann ich jetzt nicht so aus dem Stand artikulieren. Ich denke aber, dass auch hier das Verständnis mit der Übung kommt.

Bin aber froh in Sachen Import etwas weiter gekommen zu sein.
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

Jetzt ist das hoffentlich eine richtige Funktion :?:
Was mir jetzt nicht einleuchtet ist, dass ich die Funktion erst am Ende aufrufen kann. Versuche ich sie schon ganz oben aufzurufen, dann kennt der Interpreter ihren Namen nicht. :K

Code: Alles auswählen

# Schere-Stein-Papier-Simulation  (sspsim) 2. Variante
import random

##erg = sspsim(99)
##print(erg)

def sspsim(runden):
    aplay = 0
    bplay = 0
    playp= 0
    zw = 0
    while zw < runden:
        a = random.randint(1,3) 
        b = random.randint(1,3)
        # 1= Stein, 2= Papier, 3=Schere 
        if a == b:
            playp = playp + 1
        else:
            if a > b:
                z = a - b
                if z == 1:
                    aplay =aplay + 1
                else:
                    bplay = bplay + 1
            else:
                z = b - a
                if z == 1:
                    bplay = bplay + 1
                else:
                    aplay = aplay +1                   
        zw = zw + 1
    return(aplay, bplay, playp)
    
erg = sspsim(99)
print(erg)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.
wwwheimer hat geschrieben:Was mir jetzt nicht einleuchtet ist, dass ich die Funktion erst am Ende aufrufen kann. Versuche ich sie schon ganz oben aufzurufen, dann kennt der Interpreter ihren Namen nicht. :K
Das liegt, ganz grob ausgedrückt, daran, dass erst mit dem `def` der Name erzeugt wird. Nun noch ein paar Worte zu deinem Code:

- Du solltest dir die ganzen abgekürzten Namen sofort wieder abgewöhnen. Man kann deren Bedeutung nur erraten und auch du wirst die bedeutung in zwei Wochen nicht mehr kennen.
- Deine while-Schleife sollte eine for-Schleife sein.
- Nummerierungen bei 1 beginnend sind eher ungewöhnlich, Standard ist 0
- Warum schreibst du nur als Kommentar hin, welche Werte Stein, Schere und Papier haben, Konstanten wären noch hilfreicher
- Die Bedingungen innerhalb des ersten else-Blocks kannst du nach außen ziehen und solltest sie nicht unter das `else` packen. Du suchst `elif`.
- Im ersten `else`-Block hast du zweimal im Prinzip das selbe geschrieben. Das kannst du in einer Funktion zusammenfassen.
- Beim `return` solltest du die Klammern weglassen
- Um dein Modul wiederverwendbar zu machen, solltest du den Code auf Modulebene in eine main-Funktion packen und so aufrufen.

Code: Alles auswählen

def main():
    ergebnis = sspsim(99)
    print(ergebnis)

if __name__ == "__main__":
    main()
Damit kannst du dein Modul auch in einem anderen importieren.
Das Leben ist wie ein Tennisball.
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

EyDu hat geschrieben: - Deine while-Schleife sollte eine for-Schleife sein.
- Nummerierungen bei 1 beginnend sind eher ungewöhnlich, Standard ist 0
- Warum schreibst du nur als Kommentar hin, welche Werte Stein, Schere und Papier haben,
Konstanten wären noch hilfreicher
Hi EyDu,
die For... hatte auch schon BlackJack angemahnt und steht auf der Liste.

Was Du mit Nummerieung meinst das kann ich jetzt nur vermuten. Random gibt jeweils 1,2, oder 3 aus. Damit die anschliessende Berechnung nachvollzogen werden kann, ist der Hinweis auf die Zuordnung in der Kommentarzeile wichtig. Was ich da mit Konstanten anfangen könnte, das müßtest Du mir aber schon erklären.

Dank und Gruß!
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

Die Funktion ist hiermit komplett. Sie gibt nun auch noch für jeden der beiden Player an, wer was gezogen hat, 1, 2, oder 3. bei einer Runde, bzw. die Summen bei mehreren Runden.

Code: Alles auswählen

# Schere-Stein-Papier-Simulation  (sspsim)
# 2. Variante mit for-Schleife und erweterter Ausgabe 
import random

def sspsim(runden):
    aplay = 0; ap1 = 0; ap2 = 0; ap3 = 0
    bplay = 0; bp1 = 0; bp2 = 0; bp3 = 0
    patt = 0    
    x = 0
    for x in range(runden):
        a = random.randint(1,3)
        if a == 1:
            ap1 = ap1 + 1
        if a == 2:
            ap2 = ap2 + 1
        if a == 3:
           ap3 = ap3 + 1        
        b = random.randint(1,3)
        if b ==1:
            bp1 = bp1 + 1
        if b == 2:
            bp2 = bp2 + 1
        if b == 3:
            bp3 = bp3 + 1
        # 1 = Stein, 2 = Papier, 3 = Schere 
        if a == b:
            patt = patt + 1
        else:
            if a > b:
                z = a - b
                if z == 1:
                    aplay = aplay + 1
                else:
                    bplay = bplay + 1
            else:
                z = b - a
                if z == 1:
                    bplay = bplay + 1
                else:
                    aplay = aplay +1                   
        x = x + 1
    return(aplay, ap1, ap2, ap3, bplay, bp1, bp2, bp3, patt)
    
ergebnis = sspsim(99)
print(ergebnis)

    
    

Weil das Spiel vielleicht wenig bekannt ist:
Die Spielregel geht so: Schere schneidet Papier, Papier wickelt Stein ein und Stein macht Schere putt.
'Tsching Tschang Tschong' hieß das Start-Kommando in meiner Kinderzeit, wenn ich mich richtig erinnere.

Bei der Suche nach einem reizvollen Übungsthema bin ich hier diesem Spiel begegnet. Dann habe ich mir überlegt, dass interessant wäre zu schauen, ob sich nicht im Spiel des Zufalls doch irgendwelche Muster ergeben, die ausgenutzt werden könnten. Ich glaube das zwar nicht, aber auf Glauben allein will ich mich nicht verlassen. Die Funktion liefert auswertbare Ergebnisse zum Weiterüben, zB. mit einer weiteren Funktion.
mal sehen wohin das führt.

Der sspsim-Funktion fehlt nur noch die Optimierung/Kompriemierug. Ich hätte nichts dagegen, wenn jemand so nett wäre eine optimierte Variante meiner doch wohl sehr rustikalen Lösung zu erzeugen. Ein Code sagt doch mehr als tausend Worte! :)
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

a, b, sspsim, aplay, ap1, ap2, ap3, bplay, bp1, bp2, bp3?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

gkuhl hat geschrieben:a, b, sspsim, aplay, ap1, ap2, ap3, bplay, bp1, bp2, bp3?
Die ap1, ap2, ap3 haben nicht nur nichtssagende Namen, sondern sehen auch so aus, als sei hier eher die Verwendung einer Liste oder eines Dictionaries geboten.
Statt

Code: Alles auswählen

import random

ap1 = 0; ap2 = 0; ap3 = 0
for x in range(runden):
    a = random.randint(1,3)
    if a == 1:
        ap1 = ap1 + 1
    if a == 2:
        ap2 = ap2 + 1
    if a == 3:
        ap3 = ap3 + 1
sähe der Code dann so aus

Code: Alles auswählen

from collections import defaultdict
import random

ap = defaultdict(int)
for x in range(runden):
    a = random.randint(1,3)
    ap[a] += 1
Den Bezeichnernamen ap sollte man natürlich noch, wie oben erwähnt, durch eine sinnvolle sprechende Bezeichnung versehen.


Persönlich würde ich das vermutlich wie folgt implementieren (wobei ap und bp vermutlich auch noch zu einer sinnvollen Datenstruktur zusammengefasst werden können)

Code: Alles auswählen

ap = defaultdict(int)
bp = defaultdict(int)
for selections in ((random.randint(1,3), random.randint(1,3)) for _ in xrange(runden)):
    ap[selections[0]] += 1
    bp[selections[1]] += 1
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

/me, Dank auch Dir!

In der folgenden Variante fände ich es gut die ersten 6 ifs in jeweils eine Zeile packen zu können. Ich glaube das geht, nur weiß ich nicht wie :K
Geändert hat sich, dass man jetzt selbst gegen den Zufall anspielen kann. Wegefallen sind Schleife, Summenbildung und Pattanzeige. Die Ausgabe ist so auf eine schöne runde Summe aus 8 Zeichen (Einsen und Nullen) geschrumpft.

Code: Alles auswählen

# 3. Variante: User ist jetzt Player a (aplay) 
# 1 = Stein, 2 = Papier, 3 = Schere 
import random
def sspsim(input_1_2_oder_3): 
    a = input_1_2_oder_3 # Eingabefehler abfangen?   
    aplay = 0; ap1 = 0; ap2 = 0; ap3 = 0
    bplay = 0; bp1 = 0; bp2 = 0; bp3 = 0
    if a == 1:  
        ap1 = 1
    if a == 2:
        ap2 = 1
    if a == 3:
       ap3 = 1        
    b = random.randint(1,3)
    if b ==1:
        bp1 = 1
    if b == 2:
        bp2 = 1
    if b == 3:
        bp3 = 1
    if a == b:
        pass
    else:
        if a > b:
            z = a - b
            if z == 1:
                aplay = 1
            else:
                bplay = 1
        else:
            z = b - a
            if z == 1:
                bplay = 1
            else:
                aplay = 1
    return(aplay, ap1, ap2, ap3, bplay, bp1, bp2, bp3)

ioput = sspsim(3) 
print(ioput)
Bedeutung der Ausgabe:
1 an 1. Stelle = User hat gewonnen
1 an 5. Stelle = Computer hat gewonnen
0 an 1. und 5. Stelle = Unentschieden
Die drei Stellen die Jeweils nach der 1. u. 5. folgen, die zeigen welche Zahl User und Computer gespielt haben.
Beispiel-Ausgabe: (0,0,1,0,1,0,0,1)
1 ist an 5. Stelle, Computer hat mit der Schere(3) gewonnen, weil User Papier(2) gezeigt hatte.

Nun brauchts noch ein bisschen Gui :P

Dank und Gruß!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

wwwheimer hat geschrieben:/me, Dank auch Dir!
Wofür? So weit ich das überblicke, hast Du die Essenz seines Postings nicht aufgenommen...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

@wwwheimer:
Das Problem ist, dass du noch in einem ziemlich elementaren, imperativen 'Basic/C/Pascal'-Stil denkst. Lies dich mal z.B. anhand der Original-Doku, Learning Python, A Byte of Python, etc. in Listen, Tupel und Dictionaries ein. Ein vollkommen anderer Ansatz (Achtung, Python 2):

Code: Alles auswählen

"""
Simple Rock, Paper, Scissor Simulation
"""

import random as rand

# expand with Lizard, Spock if necessary
GRAPH = {'rock' :   ('scissor',),
         'paper':   ('rock',),
         'scissor': ('paper',)}


def turn(graph=GRAPH):

  a = rand.choice(graph.keys())
  b = rand.choice(graph.keys())
  a_beats_b = b in graph[a]
  a_equal_b = a == b
  
  return a_beats_b, not (a_equal_b or a_beats_b), a_equal_b


def simulate(rounds):

  return map(sum, zip(*(turn() for _ in xrange(rounds))))


if __name__ == '__main__':

  print "A wins: %d   B wins: %d   Draws: %d " % tuple(simulate(100000))
:wink:
yipyip
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

yipyip hat geschrieben:@wwwheimer:
Das Problem ist, dass du noch in einem ziemlich elementaren, imperativen 'Basic/C/Pascal'-Stil denkst. Lies dich mal z.B. anhand der Original-Doku, Learning Python, A Byte of Python, etc. in Listen, Tupel und Dictionaries ein. Ein vollkommen anderer Ansatz (Achtung, Python 2):
Ja, yipyip, so ähnlich ist es auch.
Ich habe jetzt nicht die nötige Zeit darauf näher einzugehen.

Aber ich habe Dein Beispiel gleich ausprobiert. Auf meiner 2.7 läuft es problemlos. auf 3.2 erstmal nicht. Habe simulate(1000000) eingeben und die Sekunden abgezählt. Dein Code ist (vergleichbar meiner 2. Variante) um grob geschätze 2 Sekunden schneller!

Gefällt mir, danke!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

wwwheimer hat geschrieben:Auf meiner 2.7 läuft es problemlos. auf 3.2 erstmal nicht.
Das es unter 3.2 nicht läuft liegt hier an zwei Kleinigkeiten: Aus `print` ist eine Funktion geworden, es fehlen also hier lediglich die Klammern beim Aufruf. Und was bei 2.x noch `xrange` war, ist bei 3.x nun `range`.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Und

Code: Alles auswählen

a = rand.choice(graph.keys())
b = rand.choice(graph.keys())
müsste so

Code: Alles auswählen

a = rand.choice(list(graph.keys()))
b = rand.choice(list(graph.keys()))
aussehen.

Gruß
Whitie
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

Schön!
Ich habe mir nun als nächsten Schritt folgendes überlegt:
Spieler A ist wieder Funktion. Aber jetzt vefügt Spieler A über die Möglichkeit gespielte Sequenzen zu speichern und gespeicherte Sequenzen zu spielen.
Kleinste Sequenz besteht aus drei Zeichen, abc zB, die nächsten 6, 9 und 12 Zeichen. Ein Vergleich von Zeichen soll nicht erfolgen, sondern eine Ordnung nach Anzahl der jeweiligen Zeichen in einer Sequens soll den Typ angeben. Beispiel: abc hat die Ordnungszahl 111, weil jedes Zeichen einmal darin vorkommt, abb hat danach die Ordnungszahl 120. Ebenso so bei den längeren Sequenzen: aabbbcccc = 234. So kann zB. die Funktion erkannt haben, dass der Gegner (Zufall) schon länger kein a gespielt hat und schickt dann mehr a ins Rennen, wählt also Sequenzen mit Ordnungszahlen wie 633 oder 963.
Dann braucht es noch einen Teil, um erfolgreiche Sequenzen zu erkennen und...

Die Frage ist nun, über welche Funktionen/Methoden verfügt Python für so eine Aufgabe ? Und überhaupt, was wären hier die Fachbegriffe?
Eine Datenbank sollte nicht nötig sein, eine Textdatei reicht fürs erste.

Vielleicht noch zu der Mängelliste: die wird natürlich nach und nach abgearbeit, ist doch klar. Nur jenseits von 60 geht das nicht mehr ganz so schnell... :)

Dank und Gruß!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Und welche Ordnungszahl hat dann "aba"? ;-)

Du kannst doch die Sequenzen auch in einer Liste speichern und dann vor jedem Zug analysieren. Wobei die "Analyse" und eine passende Strategie das schwierige sind ;-) Eine ganz einfache Sache wäre es, sich erst einmal die Züge in einem Counter zu merken (``collections.Counter``). Weicht ein Zug stark von der Laplace-Wahrscheinlichkeit ab, so kann man gezielt einen passenden Gegenzug wählen.

Noch eine kleine Anmerkung: "Spieler A ist eine Funktion" lässt erahnen, dass Du da eine falsche Modellierung vornimmst. Ein "Spieler" ist doch nichts ausführbares! Implementiere doch eher eine spezielle Strategiefunktion, die Du einem Spieler zuweist. Hier findest Du einen alten Beitrag von mir, in dem ich verschiedene "KI"-Funktionen implementiert habe und der eigentlichen "Spiel-Engine" nur jeweils eine passende Funktion übergebe.

Bei Dir könnte das Grundgerüst evtl. so aussehen:

Code: Alles auswählen

def choose_randomly():
    # simpler Zufallsentscheid

def choose_by_deviation():
    # Laplace Analyse und passende Gegenstrategie

def another_ki_func():
    # ...

def turn(ki_a, ki_b):
    choice_a = ki_a()
    choice_b = ki_b()
    # auswertung ...
    return result

def main():
    print turn(choose_randomly, choose_by_deviation)
Will man nun einen "Spieler" modellieren, so kann man natürlich zu einer Klasse greifen. Ohne Klasse geht das für einen simplen Fall aber auch durch eine Liste, ein Tupel oder ein Dict:

Code: Alles auswählen

players = (
    {"name": "foo", "score": 0, "choice_func": choose_by_deviation},
    {"name": "bar", "score": 0, "choice_func": choose_randomly},
)
Entsprechend könnte die ``turn``-Funktion jetzt anstelle der KI-Funktionen die Spieler entgegen nehmen. Damit ließen sich bei vielen Durchgängen auch sauber die Punkte verwalten.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
wwwheimer
User
Beiträge: 25
Registriert: Sonntag 17. Juli 2011, 16:35

Hallo Hyperion,
interessant was Du schreibst und ich habe mir das mit dem Ziegenproblem auch durchgelesen, aber es ist mir alles an dieser Stelle zu umfassend, jedenfalls für die angedachte Teillösung.
Gegeben sind Grundsequenzen aus drei Zeichen, a, b, c. Die möglichen Kombinotionen dieser drei Buchstaben von aaa - ccc, bilden eine Datenbasis aus der durch Zufall längere abc-Schlangen zusammengesetzt werden. Jede gespielte abc-Schlange wird gewertet und kommt, wenn sie überdurchschnittlich erfolgreich war auf den Stappel um bei nächster Gelegenheit wieder gespielt zu werden. Stappel braucht es wohl auch, um eine Möglichkeit zu haben von aussen solche Buchstaben-Ketten ins Spiel zu bringen. Es ist also eigentlich nicht sehr kompliziert und benötigt auch keine Wahrscheinlichkeitsfunktionen oder sonstige schwierigen mathematischen Konstrukte.
Statt einem direkten Vergleich von Zeichen bekommen die ABC-Schlangen eine Gewichtung nach der Zeichenzahl. Dazu dient die 'Odrnungszahl', die angibt wieviel mal a, b und c in der jeweiligen abc-Schlange enthalten sind.
Die 'Taktik' ist simpel, erinnert mich etwas an die Signatur eines Forummitglieds, wo es heisst: 'alles Genie ist Kopie ...' :)

Das Thema KI ist bestimmt spannend. Nur sehe ich in meinen Ansatz keine KI, sondern mehr eine einfache Mechanik, deren 'Intelligenz' sich auf eine Selektion von erfolgreichen abc-Schlangen beschränkt.

Welche 'Werkzeuge' da am besten geeignet sind, bleibt immer noch die Frage. Selbst bin ich ziemlich begeistert von der Fülle der Möglichkeiten, aber auch etwas unentschieden bei der Wahl. Bis zum kommenden Wochenende sollte das Teil aber fertig sein. Gute Tips sind natürlich willkommen.

Dank und Gruß!
Antworten