def reader(self):
     try:
         while self.connected and self._reader_alive:
             if not self._should_read():
                 continue
             # read all that is there or wait for one byte
             self.reader_buffer_string += self.serial_instance\
                 .read(self.serial_instance.in_waiting or 1)\
                 .decode('UTF-8')
             if '\r\n' in self.reader_buffer_string:
                 lines = self.reader_buffer_string.split('\r\n')
                 self.reader_last_received = lines[-2]
                 self.reader_buffer_string = lines[-1]
                 self.reader_last_read_time = utc_now()
                 if self.reader_last_received[:1] == ID_TYPE_EM4200:
                     for reader_listener in self.reader_listeners:
                         reader_listener.handle_card_read(
                             EM4200Card(self.reader_last_received[1:]))
                 else:
                     for reader_listener in self.reader_listeners:
                         reader_listener.handle_data(
                             self.reader_last_received)
     except serial.SerialException:
         self.connected = False
         # TODO: Handle exception (reconnect?) instead of re-raise
         raise
    def _check_pending_card_registration(self, card):
        pending_registration = self.firebase.database()\
            .child('pending_registrations')\
            .child(card.get_uid()) \
            .get().val()

        if pending_registration is None:
            return False

        registration_datetime = datetime.fromtimestamp(
            int(pending_registration['timestamp'] /
                1000)).replace(tzinfo=pytz.utc)

        if (utc_now() - registration_datetime
            ).total_seconds() > GAME_CARD_REGISTRATION_TIMEOUT:
            # Send a notification to listeners
            for game_listener in self.game_listeners:
                game_listener.on_pending_card_registration_timeout(
                    pending_registration, card)
            return False

        self.register_new_game_card(card, pending_registration['user_id'])
        self.firebase.database() \
            .child('pending_registrations') \
            .child(card.get_uid()) \
            .remove()
        return True
    def __init__(self, serial_port, baudrate=9600, read_delay=1):
        self.serial_port = serial_port
        self.baudrate = baudrate
        self.read_delay = read_delay
        self.connected = False
        self._reader_alive = None
        self.receiver_thread = None
        self.reader_listeners = []
        self.reader_buffer_string = ''
        self.reader_last_received = None
        self.reader_last_read_time = utc_now()

        try:
            self.serial_instance = serial.serial_for_url(
                self.serial_port.path,
                self.baudrate,
                do_not_open=True
            )

            if not hasattr(self.serial_instance, 'cancel_read'):
                # enable timeout for alive flag polling if cancel_read is not available
                self.serial_instance.timeout = 1
        except serial.SerialException as exception:
            raise ReaderCouldNotConnect(
                self.serial_port,
                exception
            )
 def should_reset(self):
     if len(self.players) == 0 or self.has_all_needed_players():
         return False
     for player in self.players:
         if (utc_now() - self.get_player_join_time(player)
             ).total_seconds() > GAME_PLAYER_REGISTRATION_TIMEOUT:
             return True
     return False
    def __init__(self, hid_ports, read_delay=1):
        self.hid_ports = hid_ports
        self.reader_devices = []
        self.selector = selectors.DefaultSelector()
        self.read_delay = read_delay
        self.connected = False
        self._reader_alive = None
        self.receiver_thread = None
        self.reader_listeners = []
        self.reader_buffer_strings = []
        self.reader_last_received = []
        self.reader_last_read_time = []

        for i in range(len(hid_ports)):
            self.reader_buffer_strings.append('')
            self.reader_last_received.append(None)
            self.reader_last_read_time.append(utc_now())
 def _should_read(self, reader_index):
     return (utc_now() - self.reader_last_read_time[reader_index]
             ).total_seconds() > self.read_delay
 def _should_read(self):
     return (utc_now() - self.reader_last_read_time).total_seconds() > self.read_delay
Exemple #8
0
 def should_sync_slack_information(self):
     return (utc_now() - self.get_slack_last_sync()
             ).total_seconds() > SLACK_SYNC_INTERVAL
Exemple #9
0
 def update_slack_last_sync(self):
     self.set_slack_last_sync(utc_now())
    def end_session(self, winner_player):
        end_time = utc_now()
        all_players = self.get_current_session().get_players().copy()

        # Remove the winner from the player list and return the first player (loser for now)
        self.get_current_session().remove_player(winner_player)
        loser_player = self.get_current_session().get_player(0)

        # Calculate the players new rating
        winner_elo_rating = calculate_new_rating(
            player_rating=winner_player.get_elo_rating(),
            opponent_rating=loser_player.get_elo_rating(),
            player_won=True)
        loser_elo_rating = calculate_new_rating(
            player_rating=loser_player.get_elo_rating(),
            opponent_rating=winner_player.get_elo_rating(),
            player_won=False)

        winner_trueskill_rating, loser_trueskill_rating = rate_1vs1(
            winner_player.get_trueskill_rating(),
            loser_player.get_trueskill_rating())

        # Push the current session (which has ended) to the list of sessions in Firebase
        results = self.get_db().child('sessions').push({
            'session_started':
            self.get_current_session().start_time.isoformat(),
            'session_ended':
            end_time.isoformat(),
            'session_seconds':
            (end_time - self.get_current_session().start_time).total_seconds(),
            'trueskill_quality':
            quality_1vs1(winner_player.get_trueskill_rating(),
                         loser_player.get_trueskill_rating()),
            'winner': {
                'slack_user_id': winner_player.get_slack_user_id(),
                'trueskill_rating': {
                    'mu': {
                        'before':
                        winner_player.get_trueskill_rating().mu,
                        'after':
                        winner_trueskill_rating.mu,
                        'delta':
                        winner_trueskill_rating.mu -
                        winner_player.get_trueskill_rating().mu
                    },
                    'sigma': {
                        'before':
                        winner_player.get_trueskill_rating().sigma,
                        'after':
                        winner_trueskill_rating.sigma,
                        'delta':
                        winner_trueskill_rating.sigma -
                        winner_player.get_trueskill_rating().sigma
                    }
                },
                'elo_rating': {
                    'before': winner_player.get_elo_rating(),
                    'after': winner_elo_rating,
                    'delta':
                    winner_elo_rating - winner_player.get_elo_rating()
                }
            },
            'loser': {
                'slack_user_id': loser_player.get_slack_user_id(),
                'trueskill_rating': {
                    'mu': {
                        'before':
                        loser_player.get_trueskill_rating().mu,
                        'after':
                        loser_trueskill_rating.mu,
                        'delta':
                        loser_trueskill_rating.mu -
                        loser_player.get_trueskill_rating().mu
                    },
                    'sigma': {
                        'before':
                        loser_player.get_trueskill_rating().sigma,
                        'after':
                        loser_trueskill_rating.sigma,
                        'delta':
                        loser_trueskill_rating.sigma -
                        loser_player.get_trueskill_rating().sigma
                    }
                },
                'elo_rating': {
                    'before': loser_player.get_elo_rating(),
                    'after': loser_elo_rating,
                    'delta': loser_elo_rating - loser_player.get_elo_rating()
                }
            }
        })

        # Push the current session id/pk to the list of sessions for each card
        for player in all_players:
            if player.get_card().get_uid() == winner_player.get_card().get_uid(
            ):
                is_winner = True
                new_elo_rating = winner_elo_rating
                new_trueskill_rating = winner_trueskill_rating
            else:
                is_winner = False
                new_elo_rating = loser_elo_rating
                new_trueskill_rating = loser_trueskill_rating
            self.firebase.database()\
                .child('players')\
                .child(player.get_slack_user_id())\
                .child('sessions')\
                .child(self.game_slug)\
                .child(results['name'])\
                .set({
                    'card_uid': player.get_card().get_uid(),
                    'winner': is_winner,
                    'elo_rating': {
                        'new': new_elo_rating,
                        'delta': new_elo_rating - player.get_elo_rating()
                    },
                    'trueskill_rating': {
                        'mu': {
                            'new': new_trueskill_rating.mu,
                            'delta': new_trueskill_rating.mu - player.get_trueskill_rating().mu
                        },
                        'sigma': {
                            'new': new_trueskill_rating.sigma,
                            'delta': new_trueskill_rating.sigma - player.get_trueskill_rating().sigma
                        }
                    }
                })

            existing_player_statistics = self.get_db()\
                .child('player_statistics')\
                .child(player.get_slack_user_id())\
                .get().val()

            # Set the player statistics
            self.get_db().child('player_statistics').child(
                player.get_slack_user_id()).set({
                    'elo_rating':
                    new_elo_rating,
                    'trueskill_rating': {
                        'mu': new_trueskill_rating.mu,
                        'sigma': new_trueskill_rating.sigma
                    },
                    'total_games':
                    existing_player_statistics['total_games'] + 1,
                    'games_won':
                    existing_player_statistics['games_won'] +
                    (1 if is_winner else 0),
                    'games_lost':
                    existing_player_statistics['games_lost'] +
                    (1 if not is_winner else 0),
                    'seconds_played':
                    existing_player_statistics['seconds_played'] +
                    (end_time -
                     self.get_current_session().start_time).total_seconds()
                })

        # Send a notification to listeners
        for game_listener in self.game_listeners:
            game_listener.on_end_session(
                winner_player=winner_player,
                winner_new_elo_rating=winner_elo_rating,
                winner_new_trueskill_rating=winner_trueskill_rating,
                loser_player=loser_player,
                loser_new_elo_rating=loser_elo_rating,
                loser_new_trueskill_rating=loser_trueskill_rating)

        # Create a new session placeholder (it doesn't start until we call .start())
        self.current_session = GameSession(self.min_max_card_count)

        # Reset / remove the remote session
        self.get_db().child('current_session').remove()
    def register_new_game_card(self, card, slack_user_id):
        existing_card = self.firebase.database().child('cards').child(
            card.get_uid()).get().val()

        if existing_card is None:
            # Card does not exist, insert the card
            self.firebase.database().child('cards').child(card.get_uid()).set({
                'slack_user_id':
                slack_user_id,
                'registration_date':
                utc_now().isoformat()
            })
        else:
            # Card exists in the database
            if 'slack_user_id' not in existing_card:
                # Card exists, but is not registered to a player, update the owner (player)
                self.firebase.database().child('cards').child(
                    card.get_uid()).update({'slack_user_id': slack_user_id})
            elif existing_card['slack_user_id'] == slack_user_id:
                # The card is already registered to this Slack user, ignore
                logger.debug(
                    f'Card {card} is already registered under user ID {slack_user_id}, doing nothing'
                )
                return
            else:
                # The card exists under another player
                raise CardExists(card, existing_card['slack_user_id'])

        # Does the player exists in the database?
        existing_player = self.firebase.database().child('players').child(
            slack_user_id).get().val()

        if existing_player is not None:
            # Player exists in the database, just append the card to the player
            self.firebase.database()\
                .child('players')\
                .child(slack_user_id)\
                .child('cards')\
                .child(card.get_uid())\
                .set(True)

            player = GamePlayer(
                card=card,
                slack_user_id=slack_user_id,
                slack_username=existing_player['slack_username'],
                slack_first_name=existing_player['slack_first_name'],
                slack_avatar_url=existing_player['slack_avatar_url'])
        else:
            # Player does not exist, get the slack information of the user
            slack_information = self._get_slack_information(slack_user_id)
            new_player = {
                **slack_information,
                **{
                    'slack_last_sync': utc_now().isoformat(),
                    'registration_date': utc_now().isoformat(),
                    'cards': {
                        card.get_uid(): True
                    }
                }
            }

            player = GamePlayer(
                card=card,
                slack_user_id=slack_user_id,
                slack_username=new_player['slack_username'],
                slack_first_name=new_player['slack_first_name'],
                slack_avatar_url=new_player['slack_avatar_url'],
                slack_last_sync=new_player['slack_last_sync'])

            # Add the new player to the database, with the card added in the card list
            self.firebase.database().child('players').child(slack_user_id).set(
                new_player)

            # Add the player to the player statistics of the current game_slug
            self.get_db().child('player_statistics').child(slack_user_id).set({
                'trueskill_rating': {
                    'mu': Rating().mu,
                    'sigma': Rating().sigma,
                },
                'elo_rating':
                1200,
                'total_games':
                0,
                'games_won':
                0,
                'games_lost':
                0,
                'seconds_played':
                0
            })

        # Send a notification to listeners
        for game_listener in self.game_listeners:
            game_listener.on_new_card_registration(player, card)
 def start(self):
     # TODO: beep 3 times with buzzer
     self.start_time = utc_now()
 def get_seconds_elapsed(self):
     return (utc_now() - self.start_time).total_seconds()
 def add_player(self, player):
     self.players.append(player)
     self.join_times[player.get_slack_user_id()] = utc_now()