def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ province, coast = string.split('/') if '/' in string else [ string, None ] # Province with coast # Syntax: (STP NCS) if province and coast: str_province = self._alias_from_string.get(province, province) str_coast = self._alias_from_string.get('/' + coast, '') if not str_coast: self.error(on_error, 'Unknown coast "%s".' % '/' + coast) return self._str = str_province + str_coast self._bytes = add_parentheses( bytes(Token(from_str=str_province)) + bytes(Token(from_str=str_coast))) # Province without coast # Syntax: ADR else: str_province = self._alias_from_string.get(string, string) self._str = str_province self._bytes = bytes(Token(from_str=str_province))
def __init__(self, power_name, passcode, level, deadline, rules, **kwargs): """ Builds the response :param power_name: The name of the power being played. :param passcode: Integer. A passcode to rejoin the game. :param level: Integer. The daide syntax level of the game :param deadline: Integer. The number of seconds per turn (0 to disable) :param rules: The list of game rules. """ super(HelloResponse, self).__init__(**kwargs) power = parse_string(Power, power_name) passcode = Token(from_int=passcode) if 'NO_PRESS' in rules: level = 0 variants = add_parentheses( bytes(tokens.LVL) + bytes(Token(from_int=level))) if deadline > 0: variants += add_parentheses( bytes(tokens.MTL) + bytes(Token(from_int=deadline))) variants += add_parentheses( bytes(tokens.RTL) + bytes(Token(from_int=deadline))) variants += add_parentheses( bytes(tokens.BTL) + bytes(Token(from_int=deadline))) if 'NO_CHECK' in rules: variants += add_parentheses(bytes(tokens.AOA)) self._bytes = bytes(tokens.HLO) \ + add_parentheses(bytes(power)) \ + add_parentheses(bytes(passcode)) \ + add_parentheses(bytes(variants))
def test_tokens(): """ Test all tokens """ for token in ExpectedTokens: token_str = token.name[-3:] token_bytes = token.value.to_bytes(2, byteorder='big') token_from_str = Token(from_str=token_str) token_from_bytes = Token(from_bytes=token_bytes) assert str(token_from_str) == token_str assert str(token_from_bytes) == token_str assert bytes(token_from_str) == token_bytes assert bytes(token_from_bytes) == token_bytes
def from_bytes(self, daide_bytes, on_error='raise'): """ Builds the clause from a byte array :param daide_bytes: The bytes to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') :return: The remaining (unparsed) bytes """ str_group_bytes, remaining_bytes = break_next_group(daide_bytes) # Can't find the string if not str_group_bytes: self.error( on_error, 'Unable to find a set of parentheses to extract the string clause.' ) return daide_bytes # Extract its content nb_bytes = len(str_group_bytes) self._bytes = str_group_bytes self._str = ''.join([ str(Token(from_bytes=str_group_bytes[pos:pos + 2])) for pos in range(2, nb_bytes - 2, 2) ]) return remaining_bytes
def __init__(self, seconds, **kwargs): """ Builds the response :param seconds: Integer. The number of seconds before deadline """ super(TimeToDeadlineResponse, self).__init__(**kwargs) self._bytes = bytes(tokens.TME) + add_parentheses( bytes(Token(from_int=seconds)))
def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ self._bytes = bytes(Token(from_int=int(string))) self._int = int(string)
def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ self._bytes = add_parentheses(b''.join( [bytes(Token(from_str=char)) for char in string])) self._str = string
def str_to_bytes(daide_str): """ Converts a str into its bytes representation :param daide_str: A DAIDE string with tokens separated by spaces :return: The bytes representation of the string Note: Integers starts with a '#' character """ buffer = [] str_split = daide_str.split(' ') if daide_str else [] for word in str_split: if word == '': buffer.append(bytes(Token(from_str=' '))) elif word[0] == '#': buffer.append(bytes(Token(from_int=int(word[1:])))) else: buffer.append(bytes(Token(from_str=word))) return b''.join(buffer)
def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ str_season = self._alias_from_string.get( '%s.%s' % (string[0], string[-1]), '') str_year = string[1:-1] if not str_season or not str_year: self.error(on_error, 'Unknown season and/or year "%s".' % string) return self._str = string self._bytes = add_parentheses( bytes(Token(from_str=str_season)) + bytes(Token(from_int=int(str_year))))
def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build this clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ str_unit_type = self._alias_from_string.get(string, '') if not str_unit_type: self.error(on_error, 'Unknown unit type "%s"' % string) self._str = string self._bytes = bytes(Token(from_str=str_unit_type))
def __init__(self, phase_name, powers, daide_users, years_of_elimination, **kwargs): """ Builds the Notification """ super(SummaryNotification, self).__init__(**kwargs) powers_smrs_clause = [] # Turn turn_clause = parse_string(Turn, phase_name) for power, daide_user, year_of_elimination in zip( powers, daide_users, years_of_elimination): power_smr_clause = [] name = daide_user.client_name if daide_user else power.get_controller( ) version = daide_user.client_version if daide_user else 'v0.0.0' power_name_clause = bytes(parse_string(Power, power.name)) power_smr_clause.append(power_name_clause) # (name) name_clause = bytes(parse_string(String, name)) power_smr_clause.append(name_clause) # (version) version_clause = bytes(parse_string(String, version)) power_smr_clause.append(version_clause) number_of_centres_clause = bytes( Token(from_int=len(power.centers))) power_smr_clause.append(number_of_centres_clause) if not power.centers: year_of_elimination_clause = bytes( Token(from_int=year_of_elimination)) power_smr_clause.append(year_of_elimination_clause) power_smr_clause = add_parentheses(b''.join(power_smr_clause)) powers_smrs_clause.append(power_smr_clause) self._bytes = bytes( tokens.SMR) + bytes(turn_clause) + b''.join(powers_smrs_clause)
def from_string(self, string, on_error='raise'): """ Builds the clause from a string :param string: The string to use to build this clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') """ # Not enough bytes to get a token if not string: self.error(on_error, '`string` cannot be empty or None') return # Getting the token self._bytes = bytes(Token(from_str=string)) self._str = string
def from_bytes(daide_bytes, **kwargs): """ Builds a request from DAIDE bytes :param daide_bytes: The bytes representation of a request :return: The DaideRequest built from the bytes """ if len(daide_bytes) < 2: return None initial_bytes = daide_bytes[:2] if initial_bytes not in __REQUEST_CONSTRUCTORS__: raise ValueError('Unable to find a constructor for %s' % str(Token(from_bytes=initial_bytes))) request = __REQUEST_CONSTRUCTORS__[initial_bytes](**kwargs) # type: DaideRequest request.parse_bytes(daide_bytes) return request
def parse_bytes(self, daide_bytes): """ Builds the request from DAIDE bytes """ assert len(daide_bytes) % 2 == 0, 'Expected request to have an even number of bytes. Got %d' % len(daide_bytes) self._bytes = daide_bytes # Building str representation while daide_bytes: token = Token(from_bytes=(daide_bytes[0], daide_bytes[1])) new_str = str(token) daide_bytes = daide_bytes[2:] pad = '' if (not self._str or self._str[-1] == '(' or new_str == ')' or (is_ascii_token(token) and new_str != '(')) else ' ' self._str = self._str + pad + new_str
def bytes_to_str(daide_bytes): """ Converts a bytes into its str representation :param daide_bytes: A DAIDE bytes with tokens separated by spaces :return: The bytes representation of the string Note: Integers starts with a '#' character """ buffer = [] length = len(daide_bytes) if daide_bytes else 0 for i in range(0, length, 2): token = Token(from_bytes=(daide_bytes[i], daide_bytes[i + 1])) if is_integer_token(token): buffer.append('#' + str(token)) else: buffer.append(str(token)) return ' '.join(buffer)
def _build_adjustment_phase(self, power): """ Builds the missing orders response for a build phase """ disbands_status = len(power.units) - len(power.centers) if disbands_status < 0: available_homes = power.homes[:] # Removing centers for which it's impossible to build for unit in [unit.split() for unit in power.units]: province = unit[1] if province in available_homes: available_homes.remove(province) disbands_status = max(-len(available_homes), disbands_status) self._bytes += bytes(tokens.MIS) + add_parentheses( bytes(Token(from_int=disbands_status)))
def from_bytes(self, daide_bytes, on_error='raise'): """ Builds the clause from a byte array :param daide_bytes: The bytes to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') :return: The remaining (unparsed) bytes """ token_bytes, remaining_bytes = daide_bytes[:2], daide_bytes[2:] # Not enough bytes to get a token if not token_bytes: self.error(on_error, 'At least 2 bytes are required to build a token.') return remaining_bytes # Getting the token self._bytes = token_bytes self._str = str(Token(from_bytes=token_bytes)) return remaining_bytes
def validate_resp_notifs(self, expected_resp_notifs): """ Validate that expected response / notifications are received regardless of the order :param expected_resp_notifs: the response / notifications to receive """ while expected_resp_notifs: resp_notif_message = yield messages.DaideMessage.from_stream(self._stream) resp_notif = bytes_to_str(resp_notif_message.content) if Token(from_bytes=resp_notif_message.content[:2]) == tokens.HLO: resp_notif = resp_notif.split(' ') resp_notif[5] = expected_resp_notifs[0].split(' ')[5] resp_notif = ' '.join(resp_notif) self._is_game_joined = True LOGGER.info('[%d:%d] Received reply [%s]', self._id, self.stream.socket.fileno() + 1, str(resp_notif)) LOGGER.info('[%d:%d] Replies in buffer [%s]', self._id, self.stream.socket.fileno() + 1, ','.join(expected_resp_notifs)) assert resp_notif in expected_resp_notifs expected_resp_notifs.remove(resp_notif)
def _build_adjustment_phase(self, power): """ Builds the missing orders response for a build phase """ adjusts = [OrderSplitter(adjust) for adjust in power.adjust] build_cnt = sum(1 for adjust in adjusts if adjust.order_type == 'B') disband_cnt = sum(1 for adjust in adjusts if adjust.order_type == 'D') disbands_status = (len(power.units) + build_cnt) - (len(power.centers) + disband_cnt) if disbands_status < 0: available_homes = power.homes[:] # Removing centers for which it's impossible to build for unit in [unit.split() for unit in power.units]: province = unit[1] if province in available_homes: available_homes.remove(province) disbands_status = max(-len(available_homes), disbands_status) self._bytes += bytes(tokens.MIS) + add_parentheses( bytes(Token(from_int=disbands_status)))
def from_bytes(self, daide_bytes, on_error='raise'): """ Builds the clause from a byte array :param daide_bytes: The bytes to use to build the clause :param on_error: The action to take when an error is encountered ('raise', 'warn', 'ignore') :return: The remaining (unparsed) bytes """ if not daide_bytes: self.error(on_error, 'Expected at least 1 byte to parse a number') return daide_bytes number_bytes, remaining_bytes = daide_bytes[:2], daide_bytes[2:] number_token = Token(from_bytes=number_bytes) if not tokens.is_integer_token(number_token): self.error(on_error, 'The token is not an integer. Got %s' % number_token) return daide_bytes # Extract its content self._bytes = number_bytes self._int = int(number_token) return remaining_bytes
def parse_order_to_bytes(phase_type, order_split): """ Builds an order clause from a byte array :param phase_type: The game phase :param order_split: An instance of diplomacy.utils.subject_split.OrderSplit :return: The order clause's bytes """ buffer = [] # FRANCE WAIVE if len(order_split) == 1: words = order_split.order_type.split() buffer.append(parse_string(Power, words.pop(0))) buffer.append(parse_string(OrderType, words.pop(0))) else: buffer.append(parse_string(Unit, order_split.unit)) # FRANCE F IRI [-] MAO # FRANCE A IRI [-] MAO VIA if order_split.order_type == '-': # FRANCE A IRI - MAO VIA if order_split.via_flag: buffer.append(Token(tokens.CTO)) else: buffer.append(Token(tokens.MTO)) # FRANCE A IRO [D] elif order_split.order_type == 'D': if phase_type == 'R': buffer.append(Token(tokens.DSB)) elif phase_type == 'A': buffer.append(Token(tokens.REM)) # FRANCE A LON [H] # FRANCE A WAL [S] FRANCE F LON # FRANCE A WAL [S] FRANCE F MAO - IRI # FRANCE F NWG [C] FRANCE A NWY - EDI # FRANCE A IRO [R] MAO # FRANCE A LON [B] # FRANCE F LIV [B] else: buffer.append(parse_string(OrderType, order_split.order_type)) # FRANCE A WAL S [FRANCE F LON] # FRANCE A WAL S [FRANCE F MAO] - IRI # FRANCE F NWG C [FRANCE A NWY] - EDI if order_split.supported_unit: buffer.append(parse_string(Unit, order_split.supported_unit)) # FRANCE A WAL S FRANCE F MAO [- IRI] # FRANCE F NWG C FRANCE A NWY [- EDI] if order_split.support_order_type: # FRANCE A WAL S FRANCE F MAO - IRI if order_split.order_type == 'S': buffer.append(Token(tokens.MTO)) buffer.append( parse_string(Province, order_split.destination[:3])) else: buffer.append(Token(tokens.CTO)) buffer.append(parse_string(Province, order_split.destination)) # FRANCE F IRI - [MAO] # FRANCE A IRI - [MAO] VIA # FRANCE A IRO R [MAO] elif order_split.destination: buffer.append(parse_string(Province, order_split.destination)) # FRANCE A IRI - MAO [VIA] if order_split.via_flag: buffer.append(parse_string(OrderType, order_split.via_flag)) return b''.join([bytes(clause) for clause in buffer])