<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def a2s_players(server_addr, timeout=2, challenge=0):
 """Get list of players and their info :param server_addr: (ip, port) for the server :type server_addr: tuple :param timeout: (optional) timeout in seconds :type timeout: float :param challenge: (optional) challenge number :type challenge: int :raises: :class:`RuntimeError`, :class:`socket.timeout` :returns: a list of players :rtype: :class:`list` """

    ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    ss.connect(server_addr)
    ss.settimeout(timeout)

    # request challenge number
    header = None

    if challenge in (-1, 0):
        ss.send(_pack('<lci', -1, b'U', challenge))
        try:
            data = ss.recv(512)
            _, header, challenge = _unpack_from('<lcl', data)
        except:
            ss.close()
            raise

        if header not in b'AD':  # work around for CSGO sending only max players
            raise RuntimeError("Unexpected challenge response - %s" % repr(header))

    # request player info
    if header == b'D':  # work around for CSGO sending only max players
        data = StructReader(data)
    else:
        ss.send(_pack('<lci', -1, b'U', challenge))

        try:
            data = StructReader(_handle_a2s_response(ss))
        finally:
            ss.close()

    header, num_players = data.unpack('<4xcB')

    if header != b'D':
        raise RuntimeError("Invalid reponse header - %s" % repr(header))

    players = []

    while len(players) < num_players:
        player = dict()
        player['index'] = data.unpack('<B')[0]
        player['name'] = data.read_cstring()
        player['score'], player['duration'] = data.unpack('<lf')
        players.append(player)

    if data.rlen() / 8 == num_players:  # assume the ship server
        for player in players:
            player['deaths'], player['money'] = data.unpack('<ll')

    return players