Coverage for zombie_nomnom_api/game.py: 100%

76 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-07 04:25 +0000

1from enum import Enum 

2from typing import Protocol, runtime_checkable 

3import uuid 

4 

5from pydantic_settings import BaseSettings 

6from pymongo.collection import Collection 

7from pymongo import MongoClient 

8 

9from zombie_nomnom import ZombieDieGame 

10from zombie_nomnom.engine.serialization import format_to_json_dict, parse_game_json_dict 

11 

12 

13class Game: 

14 game: ZombieDieGame 

15 id: str 

16 

17 def __init__(self, *, game: ZombieDieGame, id: str = None) -> None: 

18 self.game = game 

19 self.id = id or str(uuid.uuid4()) 

20 

21 def __eq__(self, value: object) -> bool: 

22 return isinstance(value, Game) and self.id == value.id 

23 

24 @classmethod 

25 def from_dict(cls, game_data: dict) -> "Game": 

26 id = game_data.pop("id", None) 

27 game = parse_game_json_dict(game_data["game"]) 

28 return cls(game=game, id=id) 

29 

30 def to_dict(self) -> dict: 

31 return { 

32 "id": self.id, 

33 "game": format_to_json_dict(self.game), 

34 } 

35 

36 

37@runtime_checkable 

38class GameMakerInterface(Protocol): 

39 """ 

40 Defines the methods that are used by game makers to create and load games currently. 

41 """ 

42 

43 def make_game(self, players: list[str]) -> Game: ... 

44 

45 def __getitem__(self, key: str) -> Game: ... 

46 

47 def __iter__(self): ... 

48 

49 

50class InMemoryGameMaker: 

51 def __init__(self) -> None: 

52 self.session = {} 

53 

54 def make_game(self, players: list[str]) -> Game: 

55 game = Game(game=ZombieDieGame(players)) 

56 self.session[game.id] = game 

57 return game 

58 

59 def __getitem__(self, key: str) -> Game: 

60 return self.session.get(key, None) 

61 

62 def __iter__(self): 

63 return iter(self.session.values()) 

64 

65 

66class MongoGameMaker: 

67 def __init__(self, mongo_client: MongoClient, game_collection_name) -> None: 

68 self.mongo_client = mongo_client 

69 self.game_collection: Collection = mongo_client.get_database().get_collection( 

70 game_collection_name 

71 ) 

72 

73 def make_game(self, players: list[str]) -> Game: 

74 game = Game(game=ZombieDieGame(players)) 

75 self.game_collection.insert_one(game.to_dict()) 

76 return game 

77 

78 def load_game(self, game_id: str) -> Game | None: 

79 game_data = self.game_collection.find_one({"id": game_id}) 

80 if game_data is None: 

81 return None 

82 

83 game = Game.from_dict(game_data) 

84 return game 

85 

86 def get_all_games(self) -> list[Game]: 

87 games_data = self.game_collection.find() 

88 games = [Game.from_dict(game_data) for game_data in games_data] 

89 return games 

90 

91 def __getitem__(self, key: str) -> Game: 

92 return self.load_game(key) 

93 

94 def __iter__(self): 

95 return iter(self.get_all_games()) 

96 

97 

98class MongoGameMakerConfig(BaseSettings): 

99 """Environment Config settings for MongoGameMakers to allow us to not have to 

100 need mongo connection details unless we are loading the mongo engine. 

101 """ 

102 

103 mongo_connection: str = "mongodb://localhost:27017/zombie_nomnom" 

104 """ 

105 Full mongo connection string uri ex. mongodb://localhost:27017/zombie_nomnom 

106 """ 

107 game_collection_name: str = "games" 

108 """ 

109 Name of the collection where we store game data. 

110 """ 

111 

112 

113class GameMakerType(str, Enum): 

114 """ 

115 Enum for the supported types of game makers we have implemented. 

116 """ 

117 

118 memory = "memory" 

119 mongo = "mongo" 

120 

121 

122def create_maker(kind: GameMakerType) -> GameMakerInterface: 

123 """Translates the game maker type to the implementation of the GameMakerInterface 

124 

125 **Parameters** 

126 - kind (GameMakerType): The kind of game maker we want. 

127 

128 **Raises** 

129 - ValueError: Given a GameMakerType that we do not have mapped. 

130 

131 **Returns** 

132 - GameMakerInterface: GameMaker implementation 

133 """ 

134 if kind == GameMakerType.mongo: 

135 config = MongoGameMakerConfig() 

136 return MongoGameMaker( 

137 game_collection_name=config.game_collection_name, 

138 mongo_client=MongoClient(config.mongo_connection), 

139 ) 

140 elif kind == GameMakerType.memory: 

141 return InMemoryGameMaker() 

142 raise ValueError(f"Invalid game maker type: {kind}")