From 3c7ed066e01d689b32718e3a6430a437c6cbbb20 Mon Sep 17 00:00:00 2001 From: Philip Paquette Date: Mon, 16 Sep 2019 21:38:58 -0400 Subject: [PATCH] Merged commits up to '8f0b8e99' - Added DEBIAN_FRONTEND=noninteractive to deploy-web and deploy-webdip - Preventing installation of h5py 2.10.0 - Standardized set initialization from set([]) to set() - WebDip - Only returning stuck_in_local_optimum if power_name is stuck and doesn't have the most SC - WebDip - Preventing 2 retreats to the same location --- .../containers/deploy-web/Dockerfile | 1 + .../containers/deploy-webdip/Dockerfile | 1 + diplomacy_research/models/state_space.py | 4 +- .../scripts/launch_bot_webdip.py | 61 ++++++++++++++----- requirements.txt | 2 +- setup.py | 2 +- 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/diplomacy_research/containers/deploy-web/Dockerfile b/diplomacy_research/containers/deploy-web/Dockerfile index e1c325f..456fc03 100644 --- a/diplomacy_research/containers/deploy-web/Dockerfile +++ b/diplomacy_research/containers/deploy-web/Dockerfile @@ -13,6 +13,7 @@ ENV LANG=en_CA.UTF-8 ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp ENV PYTHONUNBUFFERED=1 ENV WORKING_DIR=/work_dir +ENV DEBIAN_FRONTEND=noninteractive ARG GITHUB_TOKEN ARG REPO diff --git a/diplomacy_research/containers/deploy-webdip/Dockerfile b/diplomacy_research/containers/deploy-webdip/Dockerfile index f60f174..7cbfb37 100644 --- a/diplomacy_research/containers/deploy-webdip/Dockerfile +++ b/diplomacy_research/containers/deploy-webdip/Dockerfile @@ -13,6 +13,7 @@ ENV LANG=en_CA.UTF-8 ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp ENV PYTHONUNBUFFERED=1 ENV WORKING_DIR=/work_dir +ENV DEBIAN_FRONTEND=noninteractive ARG GITHUB_TOKEN ARG REPO diff --git a/diplomacy_research/models/state_space.py b/diplomacy_research/models/state_space.py index e2ee96e..b65aead 100644 --- a/diplomacy_research/models/state_space.py +++ b/diplomacy_research/models/state_space.py @@ -510,8 +510,8 @@ def get_orderable_locs_for_powers(state_proto, powers, shuffled=False): # Detecting orderable locations for each top victor # Not storing coasts for orderable locations - all_orderable_locs = set([]) - orderable_locs = {power_name: set([]) for power_name in powers} + all_orderable_locs = set() + orderable_locs = {power_name: set() for power_name in powers} for power_name in powers: # Adding build locations diff --git a/diplomacy_research/scripts/launch_bot_webdip.py b/diplomacy_research/scripts/launch_bot_webdip.py index 4bf9ee3..06feb17 100644 --- a/diplomacy_research/scripts/launch_bot_webdip.py +++ b/diplomacy_research/scripts/launch_bot_webdip.py @@ -128,20 +128,22 @@ def submit_orders(self, api, game_id, country_id): return # Querying the model for object - player = self.get_player(game) + player = self.get_player(game, power_name) if player is None: LOGGER.error('Unable to retrieve the player for map %s.', game.map_name) return orders = yield player.get_orders(game, power_name) + orders = self.adjust_orders(orders, game, power_name) # Submitting orders success = yield api.set_orders(game, power_name, orders, wait=False) if not success: self.add_error(game_id, country_id) - def get_player(self, game): + def get_player(self, game, power_name): """ Returns the player to query the orders :param game: A game instance + :param power_name: The name of the power we are playing :type game: diplomacy.Game :return: A player object to query the orders """ @@ -150,7 +152,7 @@ def get_player(self, game): if game.map_name == 'standard': if game.get_current_phase() in ('S1901M', 'F1901M'): # To get a diverse set of openings return self.players['beam_0.50'] - if self.game_stuck_in_local_optimum(game): # In case the bot gets stuck + if self.game_stuck_in_local_optimum(game, power_name): # In case the bot gets stuck return self.players['beam_0.50'] return self.players['greedy'] # Greedy by default @@ -166,14 +168,15 @@ def get_player(self, game): return None @staticmethod - def game_stuck_in_local_optimum(game): + def game_stuck_in_local_optimum(game, power_name): """ Determines if the bots are stuck in a local optimum, to avoid endless loops :param game: A game instance + :param power_name: The name of the power we are playing :type game: diplomacy.Game :return: A boolean that indicates if a local optimum has been detected. """ - # 1) A local optimum can only be detected on the standard map - if game.map_name != 'standard': + # 1) A local optimum can only be detected on the standard map for a valid power + if game.map_name != 'standard' or power_name not in game.powers: return False # 2) A local optimum can only happen if a power has 13 or more supply centers @@ -184,6 +187,10 @@ def game_stuck_in_local_optimum(game): if game.get_current_phase() == 'COMPLETED': return False + # 4) A local optimum can not happen if the power has the most supply centers + if len(game.powers[power_name].centers) == max([len(power.centers) for power in game.powers.values()]): + return False + # Building a list of units at the start of the last phase of each year for each power # e.g. W1901A, W1902A, S1903M if the current phase is S1903M # We can use this to detect a power that has not been able to move its units in the last 'x' years @@ -199,11 +206,11 @@ def game_stuck_in_local_optimum(game): units[current_year] = {power_name: set(game.get_units(power_name)) for power_name in game.powers} # Checking if powers have not moved unit in the last 3 years - nb_powers_stuck = 0 - for power_name in game.powers: - units_yr_0 = units.get(current_year, {}).get(power_name, set([])) - units_yr_1 = units.get(current_year - 1, {}).get(power_name, set([])) - units_yr_2 = units.get(current_year - 2, {}).get(power_name, set([])) + powers_stuck = set() + for pow_name in game.powers: + units_yr_0 = units.get(current_year, {}).get(pow_name, set()) + units_yr_1 = units.get(current_year - 1, {}).get(pow_name, set()) + units_yr_2 = units.get(current_year - 2, {}).get(pow_name, set()) # Power can only be stuck if it still has units on the board if not units_yr_0: @@ -211,10 +218,35 @@ def game_stuck_in_local_optimum(game): # Power is stuck if (yr_0 == yr_1 and yr_1 == yr_2) if units_yr_0 == units_yr_1 == units_yr_2: - nb_powers_stuck += 1 + powers_stuck.add(pow_name) - # 4) A local optimum can only happen if 2 or more powers are stuck (same units in the last 3 years) - return bool(nb_powers_stuck >= 2) + # 5) A local optimum can only happen if 2 or more powers are stuck (same units in the last 3 years) + return bool(len(powers_stuck) >= 2 and power_name in powers_stuck) + + @staticmethod + def adjust_orders(orders, game, power_name): + """ Performs manual order adjustments to remove edge case scenarios + :param orders: The list of orders to submit + :param game: A game instance + :param power_name: The name of the power we are playing + :type game: diplomacy.Game + :return: The adjusted list of orders + """ + del game, power_name # Unused args + adjusted_orders = [] + retreat_locs = set() + + # 1) Only allow one retreat to a given location, convert the others to disband + for order in orders: + if ' R ' in order: + unit, dest = order.split(' R ') + if dest in retreat_locs: + order = '%s D' % unit + retreat_locs.add(dest) + adjusted_orders.append(order) + + # Returning adjusted orders + return adjusted_orders def add_error(self, game_id, country_id): """ Marks a request as a failure, to throttle if too many failures are detected in a period of time @@ -258,6 +290,7 @@ def main(): io_loop.run_sync(bot.run) except KeyboardInterrupt: LOGGER.error('Bot interrupted.') + break except Exception as exc: # pylint: disable=broad-except print('--------------------------------------------------------------------------------') LOGGER.error(exc) diff --git a/requirements.txt b/requirements.txt index 1615221..84f0761 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ diplomacy==1.1.0 grpcio==1.15.0 grpcio-tools==1.15.0 gym>=0.9.6 -h5py>=2.8.0 +h5py>=2.8.0,<2.10.0 html5lib hiredis numpy>=1.15,<1.16 diff --git a/setup.py b/setup.py index 3bdfe78..b14faa3 100644 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def get_module_exts(): 'grpcio==1.15.0', 'grpcio-tools==1.15.0', 'gym>=0.9.6', - 'h5py>=2.8.0', + 'h5py>=2.8.0,<2.10.0', 'html5lib', 'hiredis', 'numpy>=1.15,<1.16',