From 0125f5ea8630f11224ef62f5290d09d040adba93 Mon Sep 17 00:00:00 2001 From: overtired Date: Fri, 29 Sep 2023 15:39:10 +0300 Subject: [PATCH] Initial commit. --- .gitignore | 1 + dicelib.py | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ input_assist.py | 71 ++++++++++++++++++++++ main.py | 86 ++++++++++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 .gitignore create mode 100644 dicelib.py create mode 100644 input_assist.py create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/dicelib.py b/dicelib.py new file mode 100644 index 0000000..003d5ff --- /dev/null +++ b/dicelib.py @@ -0,0 +1,157 @@ +from random import randint, choice, seed +from input_assist import ichoiced_input, iyes_or_no + + +class Die: + def __init__(self, ai): + self.ai = ai + self.die_id = "Die" + self.spec = False + + def roll(self): + return (self.die_id, choice(self.possible_values)) + + @classmethod + def multiple(cls, count): + return [cls() for _ in range(count)] + + +class Deck: + def __init__(self, *dice, **kwargs): + try: + self.dice = [d for d in dice[0]] + except TypeError: + self.dice = dice + if "ai" in kwargs: + for die in self.dice: + die.ai = kwargs["ai"] + + def roll(self, print_func=print, ai=False): + dice_list = [] + for die in self.dice: + r = die.roll() + if die.spec and (r[1] in die.spec_results): + if ai: + r = (die.die_id, die.handle_ai(r, dice_list)) + else: + print_func(dice_list) + r = (die.die_id, die.handle_input(r[1])) + dice_list.append(r) + return dice_list + + @staticmethod + def sum(roll_result): + return sum([x[1] for x in roll_result]) + + @staticmethod + def as_str(roll_result): + paren_result = ', '.join([str(x[1]) for x in roll_result]) + return f"{Deck.sum(roll_result)} ({paren_result})" + + +class QueueDeck(Deck): + def __init__(self, *dice, **kwargs): + super().__init__(*dice, **kwargs) + + def roll(self, prompt, print_func=print, exit_check=None): + q = self.dice.copy() + dice_list = [] + while die := q.pop(): + r = die.roll() + if die.spec and (r[1] in die.spec_results): + r = (die.die_id, die.handle_input(r[1])) + + dice_list.append(r) + + if len(q) < 1: + break + # else: + # print_func(dice_list) + if exit_check is not None: + if exit_check(dice_list): + break + + print_func(dice_list) + c = iyes_or_no(prompt, default_pos=True) + if c is None: + return None + if not c: + break + return dice_list + + +class AceDie(Die): + def __init__(self, ai=None): + if ai: + super().__init__(ai) + self.die_id = "AceDie" + self.possible_values = [1] + self.spec = True + self.spec_results = [1] + + def handle_input(self, r): + r = int(ichoiced_input( + "Ace!", + ("1", "6")) + ) + if r is None: + exit() + return r + + +class AdvancedDie(Die): + def __init__(self, ai=None): + if ai: + super().__init__(ai) + self.die_id = "AdvancedDie" + self.possible_values = [1, 2, 3, 4, 5, 6] + self.spec = True + self.spec_results = [6] + + def roll(self): + return (self.die_id, choice(self.possible_values)) + + def handle_input(self, r): + if r == 6: + r = int(ichoiced_input( + "Advanced die just rolled a 6! What would you like?", + ("1", "6")) + ) + if r is None: + exit() + return r + + def handle_ai(self, r, dice_list): + print(f"REACHED AI: {r}, {dice_list}") + return self.ai(r, dice_list) + + +class BasicDie(Die): + def __init__(self, ai=None): + if ai: + super().__init__(ai) + self.die_id = "BasicDie" + self.possible_values = [1, 2, 3, 4, 5, 6] + self.spec = False + self.spec_results = [] + + +class BlankDie(Die): + def __init__(self, ai=None): + if ai: + super().__init__(ai) + self.die_id = "BlankDie" + self.possible_values = [1, 2, 3, 4, 0, 0] + self.spec = True + self.spec_results = [0] + + def handle_input(self, r): + if r in self.spec_results: + r = ichoiced_input( + "Blank die rolled a blank side! What would you like?", + ("5", "6")) + if r is not None: + r = int(r) + else: + exit() + return r diff --git a/input_assist.py b/input_assist.py new file mode 100644 index 0000000..9cf12c1 --- /dev/null +++ b/input_assist.py @@ -0,0 +1,71 @@ +def err(i): + print(f"Not a choice: {i}") + + +def choiced_input(prompt, choices): + if len(choices) < 2: + exit("Achievement unlocked: How did we get here?") + prompt_add = f"{', '.join(choices[:-1])} or {choices[-1]}" + while (i := input(prompt + f" ({prompt_add}) ")) not in choices: + err(i) + return i + + +def choiced_input_ci(prompt, choices): + if len(choices) < 2: + exit("Achievement unlocked: How did we get here?") + prompt_add = f"{', '.join(choices[:-1])} or {choices[-1]}" + while (i := input(prompt + f" ({prompt_add}) ")).casefold() not in choices: + err(i) + return i + + +def yes_or_no(prompt, default_pos=False): + positive = list("+y") + ["yes"] + negative = list("-n") + ["no"] + choices = positive + negative + [""] + while (i := input(prompt)).strip().casefold() not in choices: + err(i) + if i == "": + return default_pos + return i in positive + + +def ranged_input_int(prompt, _range): + try: + while int((i := input(prompt)).strip().casefold()) not in _range: + err(i) + except ValueError: + return ranged_input_int(prompt, _range) + return i + + +def iwrapper(func, *args): + try: + return func(*args) + except (EOFError, KeyboardInterrupt): + return None + + +def ichoiced_input(prompt, choices): + return iwrapper(choiced_input, prompt, choices) + + +def ichoiced_input_ci(prompt, choices): + return iwrapper(choiced_input_ci, prompt, choices) + + +def iyes_or_no(prompt, default_pos=False): + return iwrapper(yes_or_no, prompt, default_pos) + + +def iranged_input_int(prompt, _ranged): + return iwrapper(ranged_input_int, prompt, _ranged) + + +if __name__ == "__main__": + print("TESTS AHEAD!!! :3") + print(iranged_input_int("HIIII (-10-10) ", range(-10, 11))) + exit() + print(ichoiced_input("x or y (no z)?", ["x", "y"])) + print(ichoiced_input_ci("X or Y or other case?", ["x", "y"])) diff --git a/main.py b/main.py new file mode 100644 index 0000000..23775b0 --- /dev/null +++ b/main.py @@ -0,0 +1,86 @@ +from dicelib import AceDie, AdvancedDie, BasicDie, BlankDie, Deck, QueueDeck +from input_assist import iyes_or_no, iranged_input_int +from random import choice, shuffle + +# seed(42069) # for testing + + +def rai(r, dice_list): + if r[0] == "AdvancedDie": + return choice([1, 6]) + exit("Achievement unlocked: How did we get here?") + + +def smolthink_ai(r, dice_list): + if r[0] == "AdvancedDie": + if Deck.sum(dice_list) > 7: + return 1 + else: + return 6 + + +def pfunc(dice_list): + dice = [f"{DICE_NAMES[x[0]]}: {x[1]}" for x in dice_list] + print("\nDice so far:\n " + ("\n ".join(dice) or "None")) + + +def efunc(dice_list): + return Deck.sum(dice_list) >= 13 + + +def shuffled(_list): + l1 = _list.copy() + shuffle(l1) + return l1 + + +if __name__ == "__main__": + DICE_NAMES = { + "AdvancedDie": "Advanced", + "BasicDie": "Basic", + "BlankDie": "Blank", + "AceDie": "Ace", + } + deck = QueueDeck(shuffled(BasicDie.multiple(11) + + AdvancedDie.multiple(6) + + BlankDie.multiple(3) + + AceDie.multiple(1))) + balance = 500 + + while balance > 0: + bet = iranged_input_int(f"Your bet? [1-{balance}] ", + range(1, balance+1)) + if bet is None: + exit("\nBye!~") + else: + bet = int(bet) + roll = deck.roll("Roll more? [Y/n] ", + print_func=pfunc, exit_check=efunc) + if roll is None: + exit() + + print("\nDice:") + for res in roll: + print(f"> {DICE_NAMES[res[0]]}: {res[1]}") + print("Total:", Deck.sum(roll)) + if Deck.sum(roll) > 13: + print("You've busted out!") + balance -= bet + if Deck.sum(roll) == 13: + print("You've won!") + balance += bet + if Deck.sum(roll) < 13: + print("You've backed out.") + balance -= bet // 2 + + print("-"*24) + if balance <= 0: + print("No money left :(") + print("Goodbye!") + exit() + print(f"Your beans: {balance}") + c = iyes_or_no("Continue playing? [Y/n] ", default_pos=True) + if c is None: + exit() + if not c: + break