Kniffel Python?

2 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Man könnte das Spiel durch Python steuern lassen:

Zuerst werden bspw. die Namen eingetragen. (Spieler 1,2 und 3). Dann entscheidet Python wer anfängt. Die geworfenen Würfel werden direkt nacheinander eingegeben z.B. 43265. Die Zahlen werden nach Größe sortiert: 23456. Python gibt dann eine Übersicht aus und die Möglichkeiten den Wurf einzuordnen. Der Spieler entscheidet dann worauf der Wurf geschrieben wird.

Zu verschiedenen Zeitpunkten (oder ständig sichtbar) wird der Stand als Tabelle ausgegeben. In den Zeilen die Würfe, Summen und Boni (wie auf einer Gewinnkarte) und in den spalten die Spieler.

Nach dem Spiel gibt es eine zweite Tabelle. Reduziert auf die Gesamtpunkte, Spiele und Spieler.

Python gibt immer Informationen leicht lesbar aus und gibt an, welcher Spieler als nächstes dran ist.

Ich finde, das ist ein cooles großes und unkompliziertes Projekt. Hätte ich auch Lust drauf :)

Saibotix07 
Fragesteller
 22.02.2022, 08:42

Danke :D

Bei mir ist eher das Problem dass ich nicht weiß wie Python das überprüfen soll. Wenn ein Spieler zb 12348 als Große Straße einordnet ist das ja nicht richtig. Noch schwieriger wird das überprüfen bei einem Full House (2,2,3,3,3) Das könnten ja alle möglichen Zahlen sein. Meine Idee wäre dass ich auf alle Zahlen eine Forloop laufen lasse und schaue wie oft welche Zahl vorkommt.

0
Kreasteve  22.02.2022, 19:02
@Saibotix07

Ich würde eine Funktion verwenden um die Zahlen einzulesen. Etwa so:

def input_wurf():
    while True: #solange wiederholen bis abgebrochen wird durch return/break
        wurf = input("Gib deinen Wurf ein: ")
        wurf = list(wurf) #wandelt in eine liste um
        wurf.sort() #sortiert die liste von klein nach groß
        wurf = list(map(int, wurf)) #wandelt alle ziffern in integer um
        print("So sieht die Liste aus:",wurf)

        laenge = len(wurf) #zählt die ziffern
        kleinste = wurf[0] #die erste ziffer
        groesste = wurf[-1] #die letzte ziffer

        if laenge==5 and kleinste>=1 and groesste<=6: #wenn alles passt
            return wurf
        else:
            print("Da stimmt was nicht! Bitte nochmal:")

Um zu erkennen wo man seinen Wurf einordnen kann, gibt es verschiedene Möglichkeiten.

Hier ist mein Beispiel:

def print_möglichkeiten(wurf):
    count_keys = {
        "dreier Pasch": lambda x: max(x)>=3,
        "vierer Pasch": lambda x: max(x)>=4,
        "Full House": lambda x: x.count(2)!=0 and x.count(3)!=0,
        "kleine Straße": lambda x: x.count(1)>=4,
        "große Straße": lambda x: x.count(1)==5,
        "Kniffel": lambda x: 5 in x
        }
    
    augen_vorkommnisse = [wurf.count(i+1) for i in range(6)] #zählt jede augenzahl
    
    print("\nHier sind die Möglichkeiten:")
    for ck in count_keys:
        if count_keys[ck](augen_vorkommnisse):
            print(ck)

print_möglichkeiten(input_wurf())

Diese Funktion gibt die jenigen Kategorien aus, in denen der Wurf Punkte ergibt (unabhängig vom oberen Block).

Da ich mich jetzt etwas mit dem Spiel beschäftigt habe, würde ich alle "Kategorien" (z.b. 3er, 4er, 3er-Pasch, Kniffel,...) zur Auswahl anbieten und die passenden (von print_möglichkeiten) hervorheben.

2
daCypher  23.02.2022, 07:53
@Kreasteve

Die Funktion mit den Lambdas ist echt gut. Hätte nicht gedacht, dass man das so weit kürzen kann.

Die Lambdas für die kleine und große Straße musst du allerdings noch etwas nachbessern. Z.B. [1,2,3,4,4] wird nicht als kleine Straße erkannt und bei [1,2,3,5,6] wird kleine und große Straße angeboten, obwohl es keins von beiden ist.

1
Kreasteve  23.02.2022, 08:39
@daCypher

Oh danke fürs Testen!

count_keys = {
    "dreier Pasch": lambda x: max(x)>=3,
    "vierer Pasch": lambda x: max(x)>=4,
    "Full House": lambda x: x.count(2)!=0 and x.count(3)!=0,
    "kleine Straße": lambda x: "1111" in "".join(map(str,x)),
    "große Straße": lambda x: "11111" in "".join(map(str,x)),
    "Kniffel": lambda x: 5 in x
    }

ein String daraus zum machen ist sicherlich nicht die beste Lösung.

Ich hab gerade mal getestet den wurf aus input_wurf als String zu lassen, das wird aber schnell unverständlich.

0
daCypher  23.02.2022, 09:19
@Kreasteve

Jetzt hast du aber nur eins von beiden Problemen behoben. Die [1,2,3,4,4] wird trotzdem nicht als kleine Straße erkannt, weil die 4 halt doppelt drin ist. 😛

0
Kreasteve  23.02.2022, 09:59
@daCypher

:D nur her mit den Fehlern

def print_möglichkeiten(wurf):
    count_keys = {
        "dreier Pasch": lambda x: max(x)>=3,
        "vierer Pasch": lambda x: max(x)>=4,
        "Full House": lambda x: x.count(2)!=0 and x.count(3)!=0,
        "kleine Straße": lambda x: "1111" in "".join(["1" if i!=0 else "0" for i in x]),
        "große Straße": lambda x: "11111" in "".join(map(str,x)),
        "Kniffel": lambda x: 5 in x
        }

Interessant war auch

"kleine Straße": lambda x: "1111" in "".join(map(lambda t: str(int(bool(t))), x))

ist aber wesentlich langsamer.

1

Ich finde es meistens deutlich schwieriger, die Grafik zu bauen, als die Logik. Grade in Python sind die nötigen Abfragen sehr einfach. Hab dir mal ein kleines Programm gebastelt:

def print_all_options(dice: list[int]) -> None:
    # Zeigt die Punkte für alle Spieloptionen an
    if len(dice) != 5:
        raise ValueError("In der Liste 'dice' müssen genau fünf Werte enthalten sein.")


    if not all([(die in range(1,7)) for die in dice]):
        raise ValueError("Alle Würfel müssen einen Wert zwischen 1 und 6 haben.")


    # Obere Hälfte ("Nur Einser zählen" bis "nur Sechser zählen")
    for pips in range(1,7):
        print(f"Nur {pips}er zählen: {pips_sum(pips, dice)} Punkte")


    # Dreierpasch
    print(f"Dreierpasch: {three_of_a_kind(dice)} Punkte")


    # Viererpasch
    print(f"Viererpasch: {four_of_a_kind(dice)} Punkte")


    # Full House
    print(f"Full House: {full_house(dice)} Punkte")


    # Kleine Straße
    print(f"Kleine Straße: {small_straight(dice)} Punkte")


    # Große Straße
    print(f"Große Straße: {large_straight(dice)} Punkte")


    # Kniffel
    print(f"Kniffel: {kniffel(dice)} Punkte")


    # Chance
    print(f"Chance: {chance(dice)} Punkte")


def pips_sum(pips: int, dice: list[int]) -> int:
    # Gleiche Würfelaugen summieren
    return sum([die for die in dice if die == pips])


def three_of_a_kind(dice: list[int]) -> int:
    # Falls eine Gruppe mit mindestens drei gleichen Zahlen vorhanden ist,
    # alle Würfel zusammenzählen. Ansonsten 0 Punkte
    if any([group_size for group_size in group_sizes(dice) if group_size >= 3]):
        return sum(dice)
    return 0


def four_of_a_kind(dice: list[int]) -> int:
    # Falls eine Gruppe mit mindestens vier gleichen Zahlen vorhanden ist,
    # alle Würfel zusammenzählen. Ansonsten 0 Punkte
    if any([group_size for group_size in group_sizes(dice) if group_size >= 4]):
        return sum(dice)
    return 0


def full_house(dice: list[int]) -> int:
    # Wenn eine Gruppe mit zwei und eine Gruppe mit drei gleichen Würfeln
    # vorhanden ist, 25 Punkte zurückgeben. Ansonsten 0 Punkte.
    if sorted(group_sizes(dice)) == [2, 3]:
        return 25
    return 0


def small_straight(dice: list[int]) -> int:
    # Wenn eine kleine Straße (definiert durch die Optionen unten)
    # in den Würfeln vorhanden ist, 30 Punkte zurückgeben. Ansonsten 0 Punkte.
    if all([pips in dice for pips in [1, 2, 3, 4]]) \
    or all([pips in dice for pips in [2, 3, 4, 5]]) \
    or all([pips in dice for pips in [3, 4, 5, 6]]):
        return 30
    return 0


def large_straight(dice: list[int]) -> int:
    # Wenn eine große Straße (definiert durch die Optionen unten)
    # in den Würfeln vorhanden ist, 40 Punkte zurückgeben. Ansonsten 0 Punkte.
    if all([pips in dice for pips in [1, 2, 3, 4, 5]]) \
    or all([pips in dice for pips in [2, 3, 4, 5, 6]]):
        return 40
    return 0


def kniffel(dice: list[int]) -> int:
    # Wenn in den Würfeln nur eine Gruppe mit 5 gleichen Würfeln vorkommt,
    # 50 Punkte zurückgeben. Ansonsten 0 Punkte
    if group_sizes(dice) == [5]:
        return 50
    return 0


def chance(dice: list[int]) -> int:
    # Einfach die Anzahl der Würfelaugen zurückgeben
    return sum(dice)


def group_sizes(dice: list[int]) -> list[int]:
    # Listet die Anzahl der gleichen Würfelzahlen auf
    return [len([die for die in dice if die == pips]) for pips in set(dice)]


if __name__ == "__main__":
    # Diverse Würfelkombinationen testen
    for dice in [[4, 2, 1, 3, 5], \
                 [2, 1, 1, 2, 3], \
                 [6, 5, 6, 5, 6], \
                 [4, 4, 4, 4, 4], \
                 [5, 3, 3, 2, 4], \
                 [2, 3, 4, 2, 2], \
                 [6, 6, 1, 6, 6]]:
        print(f"Würfel: {dice}")
        print_all_options(dice)
        print()
Saibotix07 
Fragesteller
 22.02.2022, 10:18

Was bedeutet das dice: ?

0
daCypher  22.02.2022, 14:02
@Saibotix07

dice ist das englische Wort für Würfel. Da stehen einfach die Zahlen der Würfel drin. Ist allerdings eine list[int]. Also die Zahlen der Würfel gibst du nicht mit "12345", sondern mit [1, 2, 3, 4, 5] ein.

Übrigens: "pips" sind die Würfelaugen und "die" ist die Einzahl von Würfel.

1
Saibotix07 
Fragesteller
 22.02.2022, 15:14
@daCypher

Ja ist mir schon bewusst aber warum schreibt man das in die Attribute und warum ist dann da ein -> ?

0
daCypher  22.02.2022, 15:21
@Saibotix07

Damit du der Funktion "print_all_options" die geworfenen Würfel übergeben kannst. Wenn du z.B. print_all_options([1, 2, 3, 4, 5]) schreibst, listet die Funktion alle Optionen der Kniffel Gewinnkarte auf und du siehst, dass das Beste in dem Fall eine große Straße für 40 Punkte ist.

Die Sachen, wie "(dice: list[int]) -> None" sind sogenannte type hints. Damit sagst du dem Programm nur, welche Variablentypen die Funktion erwartet und zurückgibt. Die könnte man auch weglassen und nur "def print_all_options(dice):" schreiben.

1