From 0699c0f47422a9a106d3c4cc4e4f638b07c96e60 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 5 Nov 2021 11:12:02 +0100 Subject: [PATCH] Add game structure --- .gitignore | 1 + orochi/bot.py | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/.gitignore b/.gitignore index 1cdb6f1..007b90e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__/ venv/ config.yml +game.save *.log diff --git a/orochi/bot.py b/orochi/bot.py index 715f8fd..61dfa2d 100644 --- a/orochi/bot.py +++ b/orochi/bot.py @@ -1,3 +1,8 @@ +from dataclasses import dataclass, field +from datetime import datetime +from enum import Enum +import pickle + import disnake from disnake import CategoryChannel, PermissionOverwrite, TextChannel from disnake.ext import commands @@ -6,10 +11,134 @@ import logging from orochi.config import Config bot = commands.Bot(command_prefix='!') +GAME: "Game" + + +class Room(Enum): + A = 'A' + B = 'B' + C = 'C' + + +class Vote(Enum): + ALLY = 'A' + BETRAY = 'B' + + +@dataclass(frozen=True) +class Player: + name: str + private_channel_id: int = field(hash=False) + + @property + def round_votes(self): + for r in GAME.rounds: + for room in r.rooms: + for vote in room.votes: + if self in vote.players: + yield vote + + @property + def score(self): + s = 3 + + for vote in self.round_votes: + room = vote.room + other_vote = room.vote1 if room.vote1 is not vote else room.vote2 + match vote.vote, other_vote.vote: + case Vote.ALLY, Vote.ALLY: + s += 2 + case Vote.ALLY, Vote.BETRAY: + s -= 2 + case Vote.BETRAY, Vote.ALLY: + s += 3 + case Vote.BETRAY, Vote.BETRAY: + pass + + return s + + +@dataclass +class RoundVote: + player1: Player + player2: Player | None + vote: Vote + timestamp: datetime + + @property + def players(self): + return self.player1, self.player2 + + @property + def room(self): + for r in GAME.rounds: + for room in r.rooms: + if self in room.votes: + return room + + +@dataclass +class RoundRoom: + room: Room + vote1: RoundVote + vote2: RoundVote + + @property + def votes(self): + return self.vote1, self.vote2 + + @property + def round(self): + for r in GAME.rounds: + if self in r.rooms: + return r + + +@dataclass +class Round: + round: int + room_a: RoundRoom + room_b: RoundRoom + room_c: RoundRoom + + @property + def rooms(self): + return self.room_a, self.room_b, self.room_c + + +@dataclass +class Game: + rounds: list[Round] = field(default_factory=list) + players: dict[str, Player] = field(default_factory=dict) + + def register_player(self, name: str, vote_channel_id: int) -> Player: + player = Player(name, vote_channel_id) + self.players[name] = player + return player + + def save(self, filename: str) -> None: + """ + Uses pickle to save the current state of the game. + """ + with open(filename, 'wb') as f: + pickle.dump(self, f) + + @classmethod + def load(cls, filename: str) -> "Game | None": + """ + Reload the game from a saved file. + """ + try: + with open(filename, 'rb') as f: + return pickle.load(f) + except FileNotFoundError: + return None @bot.event async def on_ready(): + global GAME + config: Config = bot.config logger = bot.logger @@ -85,6 +214,19 @@ async def on_ready(): role, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) + GAME = Game.load('game.save') + if not GAME: + GAME = Game() + for player in config.PLAYERS: + GAME.register_player(player, config.vote_channels[player.lower()]) + GAME.save('game.save') + + # Update private channel id if necessary + for player in list(GAME.players.values()): + if player.private_channel_id != config.vote_channels[player.name.lower()]: + GAME.register_player(player.name, config.vote_channels[player.name.lower()]) + GAME.save('game.save') + if not config.telepathy_channel: channel: TextChannel = await secret_category.create_text_channel("bigbrain") config.telepathy_channel = channel.id