Python :: Aufgabe #300 :: Lösung #2

2 Lösungen Lösungen öffentlich
#300

Kniffel (Yahtzee, Pasch)

Fortgeschrittener - Python von JKooP - 06.11.2020 um 15:24 Uhr
Schreibe eine Klasse/Modul mit der/dem es möglich ist das Spiel Kniffel in abgespeckter Form abzubilden.
Zur Vereinfachung soll statt 3 nur 1 Mal gewürfelt werden.
Als Ergebnis sollen alle möglichen Gewinnstufen eines Wurfs sowohl des oberen als auch des unteren Blocks mit der erreichten Punktzahl ausgegeben werden.

Beispielwurf: 2-2-2-4-4

Oberer Block:
Zweier: 2+2+2 = 6 (nur Summe der 2er zählen)
Vierer: 4+4 = 8 (nur Summe der 4er zählen)

Unterer Block:
Dreierpasch: 2+2+2 und 4+6 = 16 (Summe aller Augen)
Full House: Dreierpasch + Zweierpasch -> Sonderwertung = 25
Chance: 2+2+2+4+6 = 16 (Summe aller Augen)

Als Erweiterung kann auch das dreimalige Würfeln implementiert werden.
Da die Interaktion mit der Konsole nicht allzu bedienerfreundlich ist, sollte
man vielleicht auf eine grafischen Benutzeroberfläche ausweichen.

Viel Spaß
#2
vote_ok
von felixTheC (1200 Punkte) - 07.02.2021 um 19:53 Uhr
Simple Game loop

Quellcode ausblenden Python-Code
import sys

from kniffel_game.kniffel import Kniffel
from kniffel_game.kniffel import NoTurnsLeft


def display_options():
    print('''
    Press [r] for rolling the dices
    Press [1-6] to move a dice into the bucket
        (example: 1, 3) to move dice 1 and 3 into your bucket
    Press [q] to quit
    ''')
    selected_option = input('>>: ').strip().lower()
    if selected_option == 'q':
        print('Good bye.\nSee you next time.')
        sys.exit(0)
    if ',' in selected_option:
        return [int(obj.strip()) for obj in selected_option.split(',')]
    return selected_option


def game_loop():
    print('Welcome to Kniffel')
    while True:
        kniffel = Kniffel()
        while True:
            option = display_options()
            if option == 'n':
                continue
            if option == 'r':
                try:
                    kniffel.roll_dices()
                except NoTurnsLeft:
                    print('Sorry no more turns left')
                    break
                kniffel.display_dices()
                kniffel.display_num_outcomes()
                kniffel.display_down_outcomes()
                print('-' * 30)
                print(f'The best upper block option is {kniffel.best_num_outcome()}')
                print('-' * 30)
                print(f'The best down block option is {kniffel.best_down()}')
                print('-' * 30)
            if isinstance(option, list):
                kniffel.save_dices(tuple(option))
            if isinstance(option, str) and option.isnumeric():
                kniffel.save_dices((int(option), ))
        print('Good Bye.')
        break


if __name__ == '__main__':
    game_loop()




The Kniffel class
Quellcode ausblenden Python-Code
from collections import Counter
import random
from typing import List
from typing import Tuple


class NoTurnsLeft(BaseException):
    pass


class Dice:

    def __init__(self):
        self.eyes = 1

    def __call__(self, *args, **kwargs):
        self.eyes = random.randint(1, 6)
        return self

    def __eq__(self, other):
        if isinstance(other, Dice):
            return self.eyes == other.eyes
        else:
            return self.eyes == other

    def __hash__(self):
        return self.eyes

    def __repr__(self):
        return str(self.eyes)


class Kniffel:

    def __init__(self):
        self.dices: List[Dice] = [Dice() for _ in range(5)]
        self.dice_bucket: List[Dice] = []
        self.turns = 0
        self.roll_dices()
        self.funcs = vars(Kniffel)

    def roll_dices(self) -> None:
        if self.turns > 3:
            raise NoTurnsLeft('No turns left')

        [dice() for dice in self.dices]
        self.turns += 1

    def save_dices(self, dices: Tuple[int, ...]) -> None:
        tmp = {i: dice for i, dice in enumerate(self.dices, 1)}
        for dice in dices:
            if 1 <= dice <= 6:
                self.dice_bucket.append(tmp.pop(dice))
        self.dices = list(tmp.values())

    def _total_eyes(self) -> int:
        all_dices = self.dices + self.dice_bucket
        return sum(dice.eyes for dice in all_dices)

    def _sum_nums(self, num: int) -> int:
        all_dices = self.dices + self.dice_bucket
        return sum(dice.eyes for dice in all_dices if dice.eyes == num)

    def num_one(self) -> int:
        return self._sum_nums(1)

    def num_two(self) -> int:
        return self._sum_nums(2)

    def num_three(self) -> int:
        return self._sum_nums(3)

    def num_four(self) -> int:
        return self._sum_nums(4)

    def num_five(self) -> int:
        return self._sum_nums(5)

    def num_six(self) -> int:
        return self._sum_nums(6)

    def down_three_of_kind(self) -> int:
        """
        3 identical dice
        """
        counter = Counter(self.dices + self.dice_bucket)
        for key, val in counter.items():
            if val == 3:
                return self._total_eyes()
        return 0

    def down_four_of_kind(self) -> int:
        """
        4 identical dice
        """
        counter = Counter(self.dices + self.dice_bucket)
        for key, val in counter.items():
            if val == 4:
                return self._total_eyes()
        return 0

    def down_full_house(self) -> int:
        """
        3 identical and 2 identical dice
        """
        counter = Counter(self.dices + self.dice_bucket)
        is_full_house = len(counter) == 2 and (2 in counter.values() and 3 in counter.values())
        if is_full_house:
            return 25
        else:
            return 0

    def down_tiny_street(self) -> int:
        """
        [1-2-3-4], [2-3-4-5], or [3-4-5-6]
        """
        dice_set = set(self.dices + self.dice_bucket)
        possible_sets = [
            {1, 2, 3, 4},
            {2, 3, 4, 5},
            {3, 4, 5, 6},
        ]
        is_tiny_street = any(len(dice_set - possible_set) == 0 for possible_set in possible_sets)
        if is_tiny_street and len(dice_set) == 4:
            return 30
        else:
            return 0

    def down_big_street(self) -> int:
        """
        [1-2-3-4-5] or [2-3-4-5-6]
        """
        dice_set = set(self.dices + self.dice_bucket)
        possible_sets = [
            {1, 2, 3, 4, 5},
            {2, 3, 4, 5, 6},
        ]
        is_big_street = any(len(dice_set - possible_set) == 0 for possible_set in possible_sets)
        if is_big_street and len(dice_set) == 5:
            return 40
        else:
            return 0

    def down_kniffel(self) -> int:
        """
        return true if 5 of the same dice
        """
        is_kniffel = len(set(self.dices + self.dice_bucket)) == 1
        if is_kniffel:
            return 50
        else:
            return 0

    def down_chance(self) -> int:
        return self._total_eyes()

    def __display_all_outcomes(self, prefix: str) -> None:
        results = {key: val(self) for key, val in self.funcs.items() if key.startswith(prefix)}
        for key, val in results.items():
            if val > 0:
                print(f'{key.replace(prefix, "")}: {val}')

    def __best_result(self, prefix: str) -> Tuple[str, int]:
        highest_val = 0
        name = ''
        results = {key: val(self) for key, val in self.funcs.items() if key.startswith(prefix)}
        for key, val in results.items():
            if val > highest_val:
                highest_val = val
                name = key.replace(prefix, '')
        return name, highest_val

    def best_num_outcome(self) -> Tuple[str, int]:
        return self.__best_result('num_')

    def best_down(self) -> Tuple[str, int]:
        return self.__best_result('down_')

    def display_num_outcomes(self):
        self.__display_all_outcomes('num_')

    def display_down_outcomes(self):
        self.__display_all_outcomes('down_')

    def display_dices(self):
        "-".join([str(dice) for dice in self.dices])
        print(f'Current dices: {"-".join([str(dice) for dice in self.dices])}')
        print(f'Dices in bucket: {"-".join([str(dice) for dice in self.dice_bucket])}')

Kommentare:

Für diese Lösung gibt es noch keinen Kommentar

Bitte melden Sie sich an um eine Kommentar zu schreiben.
Kommentar schreiben
1988416

Du scheinst einen AdBlocker zu nutzen. Ich würde mich freuen, wenn du ihn auf dieser Seite deaktivierst und dich davon überzeugst, dass die Werbung hier nicht störend ist.