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

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. 

4 

5For example of making your own diebag. 

6```python 

7from zombie_nomnom import DieBag, Die, Face 

8 

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) 

17 

18# every bag action creates a new bag so you can store both  

19new_bag = bag.draw_dice(1) 

20 

21# drawing dice adds the die to your drawn_dice field 

22new_bag.drawn_dice 

23 

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. 

26 

27# Clears the dice in the drawn_dice field and sets it to empty array 

28newer_bag = new_bag.clear_drawn_dice() 

29 

30newer_bag.drawn_dice # is not empty array 

31 

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 

34 

35# We can also check if the bag is empty 

36new_bag.is_empty # False since there are 3 dice in the bag 

37 

38# we can even add dice on the fly 

39bag.add_dice([Die(faces=[Face.Brain] * 6)]) 

40 

41len(bag) # will now be 5 since there are 5 dice. 

42``` 

43 

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 

47 

48# New bag containing: 6 Green Dice, 4 Yellow Dice, 3 Red Dice. 

49standard = DieBag.standard_bag() 

50 

51len(standard) # will be 13  

52``` 

53""" 

54 

55from copy import deepcopy 

56import random 

57from typing import Iterable 

58 

59from pydantic import BaseModel 

60 

61from .dice import Die, create_die, DieColor 

62 

63 

64class DieBag(BaseModel): 

65 """DieBag that contains and manages the dice in the game.""" 

66 

67 dice: list[Die] 

68 """The collection of dice in the bag""" 

69 drawn_dice: list[Die] = [] 

70 """The dice the bag last drew""" 

71 

72 @property 

73 def is_empty(self) -> bool: 

74 """The property that represents when the bag is considered empty. 

75 

76 **Returns** 

77 - bool: True when bag is empty 

78 """ 

79 return len(self) == 0 

80 

81 def clear_drawn_dice(self) -> "DieBag": 

82 """Creates a new `DieBag` where its `drawn_dice` field is empty. 

83 

84 **Returns** 

85 - `DieBag`: The new instance of DieBag without any drawn_dice. 

86 """ 

87 return DieBag( 

88 dice=self.dice, 

89 ) 

90 

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. 

94 

95 **Parameters** 

96 - dice (`Iterable[zombie_nomnom.Die]`): Dice we are adding to the new bag. 

97 

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=[]) 

106 

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. 

111 

112 **Parameters** 

113 - amount (`int`, optional): the total amount of dice we are getting from the bag. Defaults to 1. 

114 

115 **Raises** 

116 - `ValueError`: Raised when there are not enough dice to be able to draw from the bag. 

117 

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.") 

123 

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 ) 

134 

135 def __len__(self): 

136 return len(self.dice) 

137 

138 def __bool__(self): 

139 return len(self) > 0 

140 

141 @classmethod 

142 def standard_bag(cls) -> "DieBag": 

143 """Creates a bag using the standard rules format. 

144 

145 The bag will contain 6 GREEN dice, 4 YELLOW dice, and 3 RED dice. 

146 

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 )