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

37 statements  

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

1from abc import ABC, abstractmethod 

2import sys 

3 

4from pydantic import validate_call 

5from .models import RoundState 

6 

7 

8class Command(ABC): 

9 """ 

10 Used to modify round state. Cannot be used to reset game. 

11 """ 

12 

13 @abstractmethod 

14 def execute(self, round: RoundState) -> RoundState: # pragma: no cover 

15 """ 

16 Method to generate a new RoundState that represents modifications on the command. 

17 

18 **Parameters** 

19 - round(`RoundState`): the round we are on. 

20 

21 **Returns** `RoundState` 

22 

23 New instance of round with modified state. 

24 """ 

25 

26 

27class DrawDice(Command): 

28 """ 

29 The core command that represents handling a draw action in the game. 

30 This will attempt to draw dice in your hand to fill in any dice that 

31 are not re-rollable then roll the dice for the turn. Then it will 

32 check to make sure you are still alive and if so keep dice in your hand 

33 and return a new round object. 

34 

35 **Parameters** 

36 - amount_drawn (`int`): Dice this action will attempt to draw. 

37 """ 

38 

39 amount_drawn: int 

40 """Amount of dice that this action will attempt to draw.""" 

41 

42 def __init__(self, amount_drawn: int = 3) -> None: 

43 if amount_drawn <= 0: 

44 raise ValueError("Cannot draw a no or a negative amount of dice.") 

45 self.amount_drawn = amount_drawn 

46 

47 @validate_call 

48 def execute(self, round: RoundState) -> RoundState: 

49 """ 

50 Executes a dice draw on a round that is active. 

51 

52 If round is already over will return given round context. 

53 

54 **Parameters** 

55 - round(`RoundState`): the round we are on. 

56 

57 **Returns** `RoundState` 

58 

59 New instance of a round with player adding dice to hand. 

60 """ 

61 if round.ended: 

62 return round 

63 player = round.player 

64 dice_to_roll = player.rerolls 

65 total_dice = len(dice_to_roll) 

66 try: 

67 bag = ( 

68 round.bag.clear_drawn_dice() 

69 if total_dice == self.amount_drawn 

70 else round.bag.draw_dice(amount=self.amount_drawn - total_dice) 

71 ) 

72 except ValueError as exc: 

73 return self.execute( 

74 round=RoundState( 

75 bag=round.bag.add_dice(player.brains), 

76 player=player, 

77 ended=round.ended, 

78 ) 

79 ) 

80 dice_to_roll.extend(bag.drawn_dice) 

81 player = player.add_dice(*bag.drawn_dice) 

82 

83 for die in dice_to_roll: 

84 die.roll() 

85 

86 ended = player.is_player_dead() 

87 if ended: 

88 player = player.clear_hand() 

89 

90 return RoundState( 

91 bag=bag, 

92 player=player, 

93 ended=ended, 

94 ) 

95 

96 

97class Score(Command): 

98 """ 

99 Command to score the hand of a player and add the brains they have as points to their total. 

100 """ 

101 

102 def execute(self, round: RoundState) -> RoundState: 

103 """ 

104 Scores the hand of the current player by rolling up all the scoring faces and adding it to their hand. 

105 

106 **Parameters** 

107 - round (`RoundState`): The round we are currently in. 

108 

109 **Returns** `RoundState` 

110 

111 Roundstate that is now ended with the player with hand cleared and new score added to them. 

112 """ 

113 if round.ended: 

114 return round 

115 player = round.player.calculate_score() 

116 return RoundState( 

117 bag=round.bag, 

118 player=player, 

119 ended=True, 

120 )