def get(self, gid, cmd): """Delegates the request to the corresponding core game operation.""" gid = int(gid) if not 0 <= gid < len(self.application.games): raise APIError(Codes.ILLEGAL_RANGE, gid) game = self.application.games[gid] cmd = int(cmd) if cmd == Commands.GET_BOARD: self.write(self._get_board(self.user, game)) elif cmd == Commands.JOIN_GAME: colour = self.get_argument("colour") game.add_player(self.user, colour) elif cmd == Commands.START_GAME: game.start_game() elif cmd == Commands.CREATE_CLUE: clue = StringClue(self.get_argument("clue")) card = game.get_card(self.get_argument("cid")) game.create_clue(self.user, clue, card) elif cmd == Commands.PLAY_CARD: card = game.get_card(self.get_argument("cid")) game.play_card(self.user, card) elif cmd == Commands.CAST_VOTE: card = game.get_card(self.get_argument("cid")) game.cast_vote(self.user, card) elif cmd == Commands.KICK_PLAYER: puid = self.get_argument("puid") game.kick_player(self.application.users.get_user_by_puid(puid)) else: raise APIError(Codes.ILLEGAL_RANGE, cmd)
def post(self): try: card_set_indices = list( map(int, self.request.arguments["card_sets"])) except ValueError as exc: raise APIError(Codes.NOT_AN_INTEGER, exc) if min(card_set_indices) < 0 or max(card_set_indices) >= len( self.application.card_sets): raise APIError(Codes.ILLEGAL_RANGE, card_set_indices) card_sets = [self.application.card_sets[i] for i in card_set_indices] password = self.get_argument("password", "") # not yet implemented if password: password = hash_obj(password) name = self.get_argument("name", "") if not name: name = "Game %d" % (len(self.application.games) + 1) max_score = self.get_argument("max_score") if not max_score: max_score = INFINITY try: max_score = int(max_score) max_players = int(self.get_argument("max_players")) max_clue_length = int(self.get_argument("max_clue_length")) cards_per_person = int(self.get_argument("cards_per_person")) except ValueError as exc: raise APIError(Codes.NOT_AN_INTEGER, exc) limits = self.application.limits if ((not limits.min_name <= len(name) <= limits.max_name) or (not limits.min_players <= max_players <= limits.max_players) or (not limits.min_score <= max_score <= limits.max_score) or (not limits.min_clue_length <= max_clue_length <= limits.max_clue_length)): raise APIError(Codes.ILLEGAL_RANGE) game = Game( self.user, card_sets, password, name, max_players, max_score, max_clue_length, limits, cards_per_person, ) self.application.games.append(game) self.application.save() self.write(str(len(self.application.games) - 1))
def post(self): try: gid = int(self.get_argument("gid")) except ValueError as exc: raise APIError(Codes.NOT_AN_INTEGER, exc) try: game = self.application.games[gid] except IndexError: raise APIError(Codes.ILLEGAL_RANGE) if self.user != game.host: raise APIError(Codes.ILLEGAL_RANGE) self.application.games[gid].hide() self.write(json.dumps("ok"))
def start_game(self): """Transitions from BEGIN to CLUE, or throws APIError.""" if self.state != States.BEGIN: raise APIError(Codes.BEGIN_BAD_STATE) if len(self.players) < self.limits.min_players: raise APIError(Codes.NOT_ENOUGH_PLAYERS) random.shuffle(self.order) for user in self.players: for _ in range(self.cards_per_person): card = self.deck.deal() if card is None: self.init_game() # rewind the dealing raise APIError(Codes.DECK_TOO_SMALL) self.players[user].deal(card) self.state = States.CLUE self.ping()
def add_player(self, user, colour): """Adds a user with a given colour to the game, or throws APIError.""" if self.state != States.BEGIN: raise APIError(Codes.BEGIN_BAD_STATE) if len(self.players) >= self.max_players: raise APIError(Codes.JOIN_FULL_ROOM) if not BunnyPalette.is_colour(colour): raise APIError(Codes.NOT_A_COLOUR, colour) if colour in list(self.colours.values()): raise APIError(Codes.COLOUR_TAKEN) if user in self.perma_banned: raise APIError(Codes.JOIN_BANNED) if not user in self.players: # idempotent self.players[user] = Player(user) self.order.append(user) self.colours[user] = colour # alow colour changing
def create_clue(self, user, clue, card): """Transitions from CLUE to PLAY, or throws APIError.""" if self.state != States.CLUE: raise APIError(Codes.CLUE_BAD_STATE) if user != self.clue_maker(): raise APIError(Codes.CLUE_NOT_TURN) if len(clue) < self.limits.min_clue_length: raise APIError(Codes.CLUE_TOO_SHORT) if len(clue) > self.max_clue_length: raise APIError(Codes.CLUE_TOO_LONG) if not self.players[user].has_card(card): raise APIError(Codes.NOT_HAVE_CARD) self.round = Round(self.players, clue, self.clue_maker()) self.round.play_card(user, card) self.players[user].deal(self.deck.deal()) self.state = States.PLAY self.ping()
def kick_player(self, user, is_permanent=False): """Kicks a user from the game, or throws APIError.""" if not user in self.players: raise APIError(Codes.KICK_UNKNOWN_USER) if len(self.players) <= self.limits.min_players and self.state != States.BEGIN: raise APIError(Codes.NOT_ENOUGH_PLAYERS) if self.state in (States.PLAY, States.VOTE): raise APIError(Codes.KICK_BAD_STATE) self.players.pop(user) turn = self.order.index(user) self.order.remove(user) # Readjust turn in case game is currently running if self.turn > turn: self.turn -= 1 self.turn %= len(self.players) if is_permanent: self.perma_banned.add(user)
def play_card(self, user, card): """Makes the given user play a card, or throws APIError.""" if self.state != States.PLAY: raise APIError(Codes.PLAY_BAD_STATE) if user == self.clue_maker(): raise APIError(Codes.PLAY_NOT_TURN) if not user in self.players: raise APIError(Codes.PLAY_UNKNOWN_USER) if self.round.has_played(user): raise APIError(Codes.PLAY_ALREADY) if not self.players[user].has_card(card): raise APIError(Codes.NOT_HAVE_CARD) self.round.play_card(user, card) self.players[user].deal(self.deck.deal()) if self.round.has_everyone_played(): # Transition from PLAY to VOTE. self.round.cast_vote(self.clue_maker(), self.round.user_to_card[self.clue_maker()]) self.state = States.VOTE self.ping()
def cast_vote(self, user, card): """Make the given user vote for a card, or throws APIError.""" if self.state != States.VOTE: raise APIError(Codes.VOTE_BAD_STATE) if user == self.clue_maker(): raise APIError(Codes.VOTE_NOT_TURN) if not user in self.players: raise APIError(Codes.VOTE_UNKNOWN_USER) if not self.round.has_card(card): raise APIError(Codes.VOTE_INVALID) if self.round.has_voted(user): raise APIError(Codes.VOTE_ALREADY) self.round.cast_vote(user, card) if self.round.has_everyone_voted(): # Transition from VOTE to CLUE or END. self._do_scoring() self.turn = (self.turn + 1) % len(self.players) self.state = States.CLUE if self.deck.is_empty(): self.state = States.END for p in self.players.values(): if p.score >= self.max_score: self.state = States.END self.ping()