def player_finished(self, player: Player) -> GameUpdate: """ Finaliza la partida para un jugador en concreto. """ if player.has_finished(): raise GameLogicException("El jugador ya ha terminado") self._players_finished += 1 player.position = self._players_finished logger.info(f"{player.name} has finished at position {player.position}") # Vaciamos la mano del jugador y devolvemos las cartas a la baraja player.empty_hand(return_to=self.deck) # Vaciamos el cuerpo del jugador player.empty_body(return_to=self.deck) # Generamos un GameUpdate donde: # 1. Avisamos a todos los jugadores de que el jugador ha acabado. update = GameUpdate(self) update.repeat({"leaderboard": self._leaderboard()}) # 2. Mostramos las pilas vacías empty_piles = GameUpdate(self) empty_piles.repeat({"bodies": {player.name: player.body.piles}}) update.merge_with(empty_piles) # 3. Le enviamos al jugador la mano vacía empty_hand = GameUpdate(self) empty_hand.add(player.name, {"hand": player.hand}) update.merge_with(empty_hand) return update
def piles_update(self, game: "Game") -> GameUpdate: """ Genera un diccionario indicando cambios a la pila del target. """ update = GameUpdate(game) update.repeat({"bodies": {self.target.name: self.target.body.piles}}) return update
def bodies_update(self) -> GameUpdate: update = GameUpdate(self) data = {"bodies": {}} for player in self.players: data["bodies"][player.name] = player.body.piles update.repeat(data) return update
def pause_update(self) -> GameUpdate: update = GameUpdate(self) data = { "paused": self.is_paused(), "paused_by": self._paused_by, } update.repeat(data) return update
def finish_update(self) -> GameUpdate: update = GameUpdate(self) data = {"finished": self.is_finished()} if self.is_finished(): data["leaderboard"] = self._leaderboard() data["playtime_mins"] = self._playtime_mins() update.repeat(data) return update
def time_update(self) -> GameUpdate: update = GameUpdate(self) # No se envía si no hay timer o si no ha empezado if self._turn_timer is None: return update remaining = self._turn_timer.remaining_secs() if remaining is None: return update update.repeat({"remaining_turn_secs": remaining}) return update
def players_update(self) -> GameUpdate: update = GameUpdate(self) players = [] for player in self.players: data = {"name": player.name} if player.is_ai: data["picture"] = BOT_PICTURE_ID data["is_ai"] = True players.append(data) update.repeat({"players": players}) return update
def apply(self, action: "PlayCard", game: "Game") -> GameUpdate: self.get_action_data(action, game) # Comprobamos que la pila tiene órgano if self.organ_pile.is_empty(): raise GameLogicException("No puedes robar órganos inexistentes") # Comprobamos que ninguno de los dos órganos está inmunizado if self.organ_pile.is_immune(): raise GameLogicException("No puedes robar órganos inmunes") # Comprobamos que el caller no tiene ya un órgano de ese color if not action.caller.body.organ_unique(self.organ_pile.organ): raise GameLogicException("Ya tienes un órgano de ese color") # Comprobamos que no se va a robar un órgano a sí mismo if action.caller == self.target: raise GameLogicException("No puedes robarte un órgano a ti mismo") # Obtenemos un espacio libre del caller self.empty_slot = None for (slot, pile) in enumerate(action.caller.body.piles): if pile.is_empty(): self.empty_slot = slot break if self.empty_slot is None: raise GameLogicException("No tienes espacio libre") logger.info("organ-thief played") # Robamos la pila del target y la guardamos en el caller empty_pile = action.caller.body.piles[self.empty_slot] action.caller.body.piles[self.empty_slot] = self.organ_pile self.target.body.piles[self.organ_pile_slot] = empty_pile update = GameUpdate(game) # Añadimos el cuerpo del caller al GameUpdate update.repeat({ "bodies": { self.target.name: self.target.body.piles, action.caller.name: action.caller.body.piles, }, }) update.msg = f"un Ladrón de Órganos sobre {self.target.name}" return update
def apply(self, action: "PlayCard", game: "Game") -> GameUpdate: self.get_action_data(action, game) # Comprobamos que las dos pilas tienen órgano if self.organ_pile1.is_empty() or self.organ_pile2.is_empty(): raise GameLogicException( "No puedes intercambiar órganos inexistentes") # Comprobamos que ninguno de los dos órganos está inmunizado if self.organ_pile1.is_immune() or self.organ_pile2.is_immune(): raise GameLogicException("No puedes intercambiar órganos inmunes") # Comprobamos que no se haga un transplante a sí mismo. if self.player1 == self.player2: raise GameLogicException( "No puedes intercambiar óganos entre el mismo jugador") # Comprobamos que ninguno de los dos jugadores tienen ya un órgano del # mismo color del órgano a añadir. NOTE: Ignoramos las pilas sobre las # que se va a reemplazar, porque no crean conflicto. if not (self.player1.body.organ_unique(self.organ_pile2.organ, ignored_piles=[self.pile_slot1]) and self.player2.body.organ_unique( self.organ_pile1.organ, ignored_piles=[self.pile_slot2])): raise GameLogicException("Ya tiene un órgano de ese color") logger.info("transplant played") update = GameUpdate(game) # Intercambiamos las pilas de ambos jugadores tmp = self.player1.body.piles[self.pile_slot1] self.player1.body.piles[self.pile_slot1] = self.player2.body.piles[ self.pile_slot2] self.player2.body.piles[self.pile_slot2] = tmp # Añadimos los dos cuerpos al GameUpdate update.repeat({ "bodies": { self.player1.name: self.player1.body.piles, self.player2.name: self.player2.body.piles, }, }) update.msg = f"un Transplante entre {self.player1.name} y {self.player2.name}" return update
def apply(self, action: "PlayCard", game: "Game") -> GameUpdate: self.get_action_data(action, game) if action.caller == self.target: raise GameLogicException( "No puedes intercambiar tu cuerpo contigo mismo") logger.info("medical-error played") update = GameUpdate(game) # Intercambiamos los cuerpos de ambos jugadores action.caller.body, self.target.body = self.target.body, action.caller.body # Añadimos los dos cuerpos al GameUpdate update.repeat({ "bodies": { self.target.name: self.target.body.piles, action.caller.name: action.caller.body.piles, }, }) update.msg = f"un Error Médico sobre {self.target.name}" return update
def current_turn_update(self) -> GameUpdate: update = GameUpdate(self) update.repeat({"current_turn": self.turn_player().name}) return update
def apply(self, action: "PlayCard", game: "Game") -> GameUpdate: logger.info("infection played") # Diccionario: color -> lista de pilas con virus de ese color virus = dict() for color in Color: virus[color] = [] # Listamos los virus que tiene en el cuerpo accediendo en orden # aleatorio a las pilas. for pile in random.sample(action.caller.body.piles, 4): if pile.is_infected(): color = pile.get_top_color() virus[color].append(pile) if all(map(lambda x: len(x) == 0, virus.values())): raise GameLogicException("No tienes virus disponibles") # Lista de pilas libres de todos los jugadores candidates = [] # Accederemos a los jugadores en orden aleatorio unfinished = game.get_unfinished_players() random.shuffle(unfinished) for player in unfinished: # Eliminamos al caller de la iteración if player == action.caller: continue # Añadimos las pilas libres a la lista de candidatas candidates.extend( list(filter(lambda p: p.is_free(), player.body.piles))) if len(candidates) == 0: raise GameLogicException( "No hay nadie que pueda recibir tus virus") # Aplicamos un orden aleatorio también a las pilas candidatas for candidate_pile in random.sample(candidates, len(candidates)): color = candidate_pile.get_top_color() # Asignamos el primer virus de ese color y lo quitamos de los # posibles. if len(virus[color]) == 0: # Si no hay virus de ese color -> comprobamos si hay virus # multicolor if len(virus[Color.All]) > 0: color = Color.All else: # No tenemos opción continue pile = virus[color].pop() # Eliminamos el virus del cuerpo del caller pile.pop_modifiers() # Lo colocamos en la pila candidata candidate_pile.add_modifier(Virus(color=color)) # Por simplificar, devolvemos el cuerpo de todos los jugadores update = GameUpdate(game) for player in game.players: body_update = GameUpdate(game) body_update.repeat({"bodies": {player.name: player.body.piles}}) update.merge_with(body_update) update.msg = "un Contagio" return update