Coverage for zombie_nomnom/models/bag.py: 100%
33 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 01:14 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-05 01:14 +0000
1"""
2Module that contains the code related to the dice bag and how we draw and manage it.
3This allows us to create custom sets per game of zombiedice by creating a method that creates `DieBag` objects.
5For example of making your own diebag.
6```python
7from zombie_nomnom import DieBag, Die, Face
9bag = DieBag(
10 dice=[
11 Die(faces=[Face.Brain] * 6), # Dice with only brains
12 Die(faces=[Face.Brain] * 6),
13 Die(faces=[Face.Brain] * 6),
14 Die(faces=[Face.Brain] * 6),
15 ]
16)
18# every bag action creates a new bag so you can store both
19new_bag = bag.draw_dice(1)
21# drawing dice adds the die to your drawn_dice field
22new_bag.drawn_dice
24# You can check the amount of the dice in your bag using len()
25len(new_bag) # 3 dice since this is the bag after you drew one.
27# Clears the dice in the drawn_dice field and sets it to empty array
28newer_bag = new_bag.clear_drawn_dice()
30newer_bag.drawn_dice # is not empty array
32# Bag also supports bool check which returns true if there are dice in the bag
33bool(new_bag) # True since there are 3 dice
35# We can also check if the bag is empty
36new_bag.is_empty # False since there are 3 dice in the bag
38# we can even add dice on the fly
39bag.add_dice([Die(faces=[Face.Brain] * 6)])
41len(bag) # will now be 5 since there are 5 dice.
42```
44If you don't want to bother making dice manually you can create our standard 13 dice bag:
45```python
46from zombie_nomnom import DieBag
48# New bag containing: 6 Green Dice, 4 Yellow Dice, 3 Red Dice.
49standard = DieBag.standard_bag()
51len(standard) # will be 13
52```
53"""
55from copy import deepcopy
56import random
57from typing import Iterable
59from pydantic import BaseModel
61from .dice import Die, create_die, DieColor
64class DieBag(BaseModel):
65 """DieBag that contains and manages the dice in the game."""
67 dice: list[Die]
68 """The collection of dice in the bag"""
69 drawn_dice: list[Die] = []
70 """The dice the bag last drew"""
72 @property
73 def is_empty(self) -> bool:
74 """The property that represents when the bag is considered empty.
76 **Returns**
77 - bool: True when bag is empty
78 """
79 return len(self) == 0
81 def clear_drawn_dice(self) -> "DieBag":
82 """Creates a new `DieBag` where its `drawn_dice` field is empty.
84 **Returns**
85 - `DieBag`: The new instance of DieBag without any drawn_dice.
86 """
87 return DieBag(
88 dice=self.dice,
89 )
91 def add_dice(self, dice: Iterable[Die]) -> "DieBag":
92 """Creates a new Diebag where you added the dice in this bag with
93 the dice from the caller.
95 **Parameters**
96 - dice (`Iterable[zombie_nomnom.Die]`): Dice we are adding to the new bag.
98 **Returns**
99 - `DieBag`: The new bag of dice with both dice.
100 """
101 new_dice = [
102 *(deepcopy(die) for die in self.dice),
103 *(deepcopy(die) for die in dice),
104 ]
105 return DieBag(dice=new_dice, drawn_dice=[])
107 def draw_dice(self, amount: int = 1) -> "DieBag":
108 """Creates a new bag where we select dice from this one randomly
109 and then set both the dice_drawn, and dice to a subset of the dice
110 minus the dice we drew.
112 **Parameters**
113 - amount (`int`, optional): the total amount of dice we are getting from the bag. Defaults to 1.
115 **Raises**
116 - `ValueError`: Raised when there are not enough dice to be able to draw from the bag.
118 **Returns**
119 `DieBag`: The new bag with the dice_drawn set to the dice we drew from the bag.
120 """
121 if amount < 0 or amount > len(self):
122 raise ValueError("The die bag does not have enough dice.")
124 total = len(self)
125 selected_dice = set()
126 while len(selected_dice) < amount:
127 selected_dice.add(random.randint(0, total - 1))
128 return DieBag(
129 dice=[
130 die for index, die in enumerate(self.dice) if index not in selected_dice
131 ],
132 drawn_dice=[self.dice[index] for index in selected_dice],
133 )
135 def __len__(self):
136 return len(self.dice)
138 def __bool__(self):
139 return len(self) > 0
141 @classmethod
142 def standard_bag(cls) -> "DieBag":
143 """Creates a bag using the standard rules format.
145 The bag will contain 6 GREEN dice, 4 YELLOW dice, and 3 RED dice.
147 **Returns**
148 - `DieBag`: the standard diebag that was created.
149 """
150 return cls(
151 dice=[
152 *(create_die(DieColor.GREEN) for _ in range(6)),
153 *(create_die(DieColor.YELLOW) for _ in range(4)),
154 *(create_die(DieColor.RED) for _ in range(3)),
155 ],
156 )