Coverage for zombie_nomnom/engine/models.py: 100%

64 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2024-12-05 01:14 +0000

1import uuid 

2 

3from pydantic import BaseModel, Field 

4 

5from zombie_nomnom.models.bag import DieBag 

6from zombie_nomnom.models.dice import Die, Face, DieFace 

7 

8 

9def uuid_str() -> str: 

10 """Creates a stringified uuid that we can use. 

11 

12 **Returns** 

13 - `str`: stringified uuid 

14 """ 

15 return str(uuid.uuid4()) 

16 

17 

18def is_scoring_face(face: Face | DieFace) -> bool: 

19 """Checks if a face is scoring. 

20 

21 A scoring face is a face that will score points for the player. 

22 This is useful for determining if a player should continue rolling or not. 

23 

24 **Arguments** 

25 - `face: Face | DieFace`: The face to check. 

26 

27 **Returns** 

28 - `bool`: If the face is a scoring face. 

29 """ 

30 return ( 

31 isinstance(face, Face) 

32 and face == Face.BRAIN 

33 or isinstance(face, DieFace) 

34 and face.score 

35 ) 

36 

37 

38def is_damaging_face(face: Face | DieFace) -> bool: 

39 """Checks if a face is damaging. 

40 

41 A damaging face is a face that will limit the amount of dice the player can roll. 

42 This is useful for determining if a player should stop rolling or not. 

43 

44 **Arguments** 

45 - `face: Face | DieFace`: The face to check. 

46 

47 **Returns** 

48 - `bool`: If the face is a damaging face. 

49 """ 

50 return ( 

51 isinstance(face, Face) 

52 and face == Face.SHOTGUN 

53 or isinstance(face, DieFace) 

54 and face.damage 

55 ) 

56 

57 

58def is_blank_face(face: Face | DieFace) -> bool: 

59 """Checks if a face is blank. 

60 

61 A blank face is a face that doesn't score any points nor does it damage the player. 

62 This is useful for determining if a die should be rolled again. 

63 

64 **Arguments** 

65 - `face: Face | DieFace`: The face to check. 

66 

67 **Returns** 

68 - `bool`: True if the face is blank. 

69 """ 

70 return ( 

71 isinstance(face, Face) 

72 and face == Face.FOOT 

73 or isinstance(face, DieFace) 

74 and not face.score 

75 and not face.damage 

76 ) 

77 

78 

79class Player(BaseModel): 

80 """Player in the game. This manages the total points 

81 they have and the dice they pulled from the bag. Has 

82 methods to manage the hand and then calculate points. 

83 Also has ways to be able to tell if players turn should finish. 

84 """ 

85 

86 id: str = Field(default_factory=uuid_str) 

87 """id field used to identify the player more unqiuely""" 

88 name: str 

89 """name of the player that we display on the screen""" 

90 total_brains: int = 0 

91 """total points they currently have in the game""" 

92 hand: list[Die] = [] 

93 """the dice the player is currently holding""" 

94 

95 @property 

96 def rerolls(self) -> list[Die]: 

97 """A view of the hand that shows all the dice that are re-rollable. 

98 

99 **Returns**: 

100 - `list[zombie_nomnom.Die]`: re-rollable dice 

101 """ 

102 return [die for die in self.hand if is_blank_face(die.current_face)] 

103 

104 @property 

105 def brains(self) -> list[Die]: 

106 """A view of the hand that shows all the dice that scores points. 

107 

108 **Returns** 

109 - `list[zombie_nomnom.Die]`: scoreable dice 

110 """ 

111 return [die for die in self.hand if is_scoring_face(die.current_face)] 

112 

113 @property 

114 def shots(self) -> list[Die]: 

115 """A view of the hand that shows the shots you have taken. 

116 

117 **Returns** 

118 - `list[zombie_nomnom.Die]`: damaging dice 

119 """ 

120 return [die for die in self.hand if is_damaging_face(die.current_face)] 

121 

122 def is_player_dead(self) -> bool: 

123 """Calculates whether or not the player should be dead based on the shots in their hand. 

124 

125 **Returns** 

126 - bool: True when player is considered dead. 

127 """ 

128 total = 0 

129 for shot in self.shots: 

130 face = shot.current_face 

131 if isinstance(face, DieFace): 

132 total += face.damage 

133 else: 

134 total += 1 

135 # if you have 3 shots you are dead XP 

136 return total >= 3 

137 

138 def add_dice(self, *dice: Die) -> "Player": 

139 """Creates a new player adding the dice the caller gives plus any dice in their hand currently. 

140 

141 **Returns** 

142 - `Player`: New player instance with updated hand. 

143 """ 

144 return Player( 

145 id=self.id, 

146 name=self.name, 

147 hand=[*self.hand, *dice], 

148 total_brains=self.total_brains, 

149 ) 

150 

151 def clear_hand(self) -> "Player": 

152 """Creates a new player with no dice in their hand. 

153 

154 **Returns** 

155 - `Player`: New player instance with empty hand. 

156 """ 

157 return Player( 

158 id=self.id, 

159 name=self.name, 

160 total_brains=self.total_brains, 

161 ) 

162 

163 def reset(self) -> "Player": 

164 """Creates a new player instance with all the game related fields set back to their default values. 

165 

166 **Returns** 

167 - `Player`: New player instance with all values reset. 

168 """ 

169 return Player( 

170 id=self.id, 

171 name=self.name, 

172 total_brains=0, 

173 hand=[], 

174 ) 

175 

176 def calculate_score(self) -> "Player": 

177 """Creates a new player that has added the score of the current hand and leaves an empty hand. 

178 

179 **Returns** 

180 - `Player`: New player instance with score adjusted for scoring dice in hand and hand reset back to empty list. 

181 """ 

182 additional_score = 0 

183 for brain_xo in self.brains: 

184 face = brain_xo.current_face 

185 if isinstance(face, DieFace): 

186 additional_score += face.score 

187 else: 

188 additional_score += 1 

189 return Player( 

190 id=self.id, 

191 name=self.name, 

192 total_brains=additional_score + self.total_brains, 

193 ) 

194 

195 

196class RoundState(BaseModel): 

197 """ 

198 Object representing the state of a round in the game. Keeps track of the bag, player, 

199 and whether or not the round has ended. 

200 """ 

201 

202 bag: DieBag 

203 """Bag that is currently being played in the round""" 

204 player: Player 

205 """Player that is currently playing""" 

206 ended: bool = False 

207 """Records whether or not the current round is over""" 

208 

209 

210class DieRecipe(BaseModel): 

211 """The recipe for a dice to be used in the bag. Will have the array of faces 

212 that we will use for our die and how many instances to add to our bag. 

213 """ 

214 

215 faces: list[Face] 

216 """The faces that will make up the dice.""" 

217 amount: int 

218 """The amount of dice that will be added to the bag."""