Python :: Aufgabe #300
2 Lösungen
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ß
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ß
Lösungen:
Python-Code
from random import randint class Yahtzee: def __init__(self): self.roll=[] self.roll_result={} self.block = {} def start(self, iteration): for index in range(iteration): self.roll_dice() print(f'\nWurf: {self.roll}') self.evaluate_roll() self.show_result() def roll_dice(self): self.roll=[] self.roll_result={1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0} for index in range(5): self.roll.append(randint(1, 6)) for element in self.roll: self.roll_result[element] += 1 def evaluate_roll(self): self.block = { 'Einser': 0, 'Zweier': 0, 'Dreier': 0, 'Vierer': 0, 'Fünfer': 0, 'Sechser': 0, 'Dreierpasch': 0, 'Viererpasch': 0, 'Full House': 0, 'Kleine Straße': 0, 'Große Straße': 0, 'Yahtzee': 0, 'Chance': 0 } if self.__check_dice_rolled(): self.block['Einser'] = self.roll_result[1] * 1 self.block['Zweier'] = self.roll_result[2] * 2 self.block['Dreier'] = self.roll_result[3] * 3 self.block['Vierer'] = self.roll_result[4] * 4 self.block['Fünfer'] = self.roll_result[5] * 5 self.block['Sechser'] = self.roll_result[6] * 6 if max(self.roll_result.values()) >= 3: self.block['Dreierpasch'] = sum(self.roll) if max(self.roll_result.values()) >= 4: self.block['Viererpasch'] = sum(self.roll) if 3 in self.roll_result.values() and 2 in self.roll_result.values(): self.block['Full House'] = 25 check_list = sorted(set(self.roll)) if check_list == [1, 2, 3, 4] or check_list == [2, 3, 4, 5] or check_list == [3, 4, 5, 6]: self.block['Kleine Straße'] = 30 if sorted(self.roll) in [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]: self.block['Große Straße'] = 40 self.block['Kleine Straße'] = 30 if max(self.roll_result.values()) == 5: self.block['Yahtzee'] = 50 self.block['Chance'] = sum(self.roll) else: print('Sie müssen die Würfel erst werfen, bevor Sie das Ergebnis auswerten können.') def show_result(self): if self.__check_result_evaluated(): for element in self.block: if self.block[element] != 0: print(f'{element:15.15}{str(self.block[element]):>2.2} Pkt.') else: print('Sie müssen erst würfeln bzw. den Wurf auswerten, bevor Sie die Ergebnisse angezeigt bekommen.') def __check_dice_rolled(self): if not self.roll: return False else: return True def __check_result_evaluated(self): if not self.block: return False else: return True if __name__ == "__main__": y = Yahtzee() y.start(5)
Simple Game loop
The Kniffel class
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
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])}')