def test_message(self): capture_output = io.StringIO() saved_stdout = sys.stdout try: sys.stdout = capture_output EventController() EventController() actual_message = capture_output.getvalue().strip() self.assertEqual(actual_message, "EventController is a singleton and has already been instantiated. " "Use EventController.get_instance() to get instance of the class." ) finally: sys.stdout = saved_stdout
def __init__(self): super().__init__() self.turn = 0 # Singletons first self.event_controller = EventController() self.fun_stat_controller = FunStatController() self.accumulative_controller = AccumulativeController() self.city_generator_controller = CityGeneratorController() self.decree_controller = DecreeController() self.destruction_controller = DestructionController() self.disaster_controller = DisasterController() self.effort_controller = EffortController() self.game_over = False
def __init__(self): super().__init__() self.event_controller = EventController.get_instance()
def __init__(self): super().__init__() self.event_controller = EventController() self.contract_list = list()
class ActionController(Controller): def __init__(self): super().__init__() self.event_controller = EventController() self.contract_list = list() def handle_actions(self, player): player_action = player.action._chosen_action # Without a contract truck has no node to move to, ensure a contract is always active if player.truck.active_contract is not None or player_action == ActionType.select_contract: # Call the appropriate method for this action if (player_action == ActionType.select_contract): # Checks if contract_list is empty. If so, we have a problem if (len(self.contract_list) == 0): raise ValueError("Contract list cannot be empty") # Selects the contract given in the player.action.action_parameter self.select_contract(player) player.time -= 1 return ActionType.select_contract elif (player_action == ActionType.select_route): # Moves the player to the node given in the action_parameter #self.move(player, player_action.action.action_parameter) move = self.move(player) return [ActionType.select_route, move[0], move[1], move[2]] if (player_action == ActionType.buy_gas): self.buy_gas(player) player.time -= GameStats.gas_pumping_time_penalty return ActionType.buy_gas elif (player_action == ActionType.repair): self.heal(player) player.time -= GameStats.repair_pumping_time_penalty return ActionType.repair elif (player_action == ActionType.upgrade): self.upgrade_level(player, player.action.action_parameter) player.time -= GameStats.upgrade_time_penalty return ActionType.upgrade elif (player_action == ActionType.change_tires): self.upgrade_tires(player, player.action.action_parameter) player.time -= GameStats.upgrade_time_penalty return ActionType.change_tires elif (player_action == ActionType.set_speed): if isinstance(player.action.action_parameter, int): #This is an ActionType because the user client cannot directly influence truck values. player.truck.set_current_speed(player.action.action_parameter) player.time -= 1 return ActionType.set_speed else: raise ValueError("Invalid speed parameter") else: self.print("Action aborted: no active contract!") player.time -= 1 return ActionType.none # Action Methods --------------------------------------------------------- def move(self, player): param = player.action.action_parameter if type(param) == int: road = player.truck.active_contract.game_map.current_node.roads[ param] elif type(param) == Road: road = param else: raise ValueError( "Attribute passed to move action was not an index or a road!") self.current_location = player.truck.active_contract.game_map.current_node time_taken = 0 fuel_efficiency = GameStats.getMPG( player.truck.speed) * GameStats.costs_and_effectiveness[ ObjectType.tires]['fuel_efficiency'][player.truck.tires] for route in self.current_location.roads: if route == road: # May need to be redone player.truck.active_contract.game_map.get_next_node() event = self.event_controller.event_chance( road, player, player.truck) time_taken = (road.length / player.truck.get_current_speed()) gas_used = (road.length / fuel_efficiency) / ( player.truck.body.max_gas * 100) player.truck.body.current_gas -= gas_used player.time -= time_taken return [road.road_type, event[0], event[1]] # Retrieve by index and store in Player, then clear the list def select_contract(self, player): if isinstance(player.action.action_parameter, int) and player.action.action_parameter >= -1: if len(self.contract_list) > int(player.action.action_parameter): player.truck.active_contract = self.contract_list[int( player.action.action_parameter)] self.contract_list.clear() else: raise Exception("Invalid contract selection index!") elif isinstance( player.action.action_parameter, Contract ) and player.action.action_parameter in self.contract_list: player.truck.active_contract = player.action.action_parameter self.contract_list.clear() else: raise Exception( "Invalid contract selection parameter! Must be valid index or contract!" ) def buy_gas(self, player): # Gas price is tied to node gasPrice = player.truck.active_contract.game_map.current_node.gas_price if (player.truck.money > 0): # Calculate what percent empty is the gas tank player.truck.body.update() percentGone = (1 - (round(player.truck.body.current_gas, 2) / (player.truck.body.max_gas))) # Calculate the percentage the player could potentially buy maxPercent = round((player.truck.money / gasPrice) / 100, 2) if (percentGone < maxPercent): # If they can afford to fill up all the way, fill em up player.truck.money -= (percentGone * 100) * gasPrice player.truck.body.current_gas = player.truck.body.max_gas else: # Otherwise, give them the max percentage they can buy player.truck.money = 0 player.truck.body.current_gas += (maxPercent * player.truck.body.max_gas) def heal(self, player): healPrice = player.truck.active_contract.game_map.current_node.repair_price if (player.truck.money > 0): # Calculate what percent repair is missing percentRemain = 1 - (round(player.truck.health, 2) / GameStats.truck_starting_health) # Calculate what percent repair they can afford maxPercent = round((player.truck.money / healPrice) / 100, 2) if (percentRemain < maxPercent): # If they can afford it, repair the truck all the way player.truck.money -= (percentRemain * 100) * healPrice player.truck.health = GameStats.truck_starting_health else: # Otherwise, do the maximum repairs player.truck.money = 0 player.truck.health += maxPercent * 100 def upgrade_body(self, player, objEnum, typ): if objEnum is ObjectType.tank: # If the player doesn't currently have a tank and they have enough money for the base tank, give them a tank! if (not isinstance(player.truck.body, Tank) ) and GameStats.costs_and_effectiveness[ ObjectType.tank]['cost'][0] <= player.truck.money: player.truck.body = Tank() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.tank]['cost'][0] else: # otherwise, upgrade their current tank tnk = player.truck.body nxtLev = tnk.level + 1 if tnk.level is not TankLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType.tank]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.tank]['cost'][nxtLev] player.truck.body.level = nxtLev else: self.print("Not enough money or at max level for gas tank") if objEnum is ObjectType.headlights: if (not isinstance(player.truck.body, HeadLights) ) and GameStats.costs_and_effectiveness[ ObjectType.headlights]['cost'][0] <= player.truck.money: player.truck.body = HeadLights() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.headlights]['cost'][0] else: # otherwise, upgrade their current headlights lgt = player.truck.body nxtLev = lgt.level + 1 if lgt.level is not HeadlightLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType. headlights]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.headlights]['cost'][nxtLev] player.truck.body.level = nxtLev else: self.print( "Not enough money or at max level for headlights") if objEnum is ObjectType.sentryGun: if (not isinstance(player.truck.body, SentryGun) ) and GameStats.costs_and_effectiveness[ ObjectType.sentryGun]['cost'][0] <= player.truck.money: player.truck.body = SentryGun() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.sentryGun]['cost'][0] else: # otherwise, upgrade their current sentry gun gn = player.truck.body nxtLev = gn.level + 1 if gn.level is not SentryGunLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType. sentryGun]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.sentryGun]['cost'][nxtLev] player.truck.body.level = nxtLev else: self.print( "Not enough money or at max level for sentry gun") def upgrade_addons(self, player, objEnum, typ): if objEnum is ObjectType.policeScanner: # If the player doesn't currently have a scanner and they have enough money for the base scanner, give them a scanner! if (not isinstance(player.truck.addons, PoliceScanner) ) and GameStats.costs_and_effectiveness[ ObjectType.policeScanner]['cost'][0] <= player.truck.money: player.truck.addons = PoliceScanner() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.policeScanner]['cost'][0] else: # otherwise, upgrade their current scanner scn = player.truck.addons nxtLev = scn.level + 1 if scn.level is not ScannerLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType. policeScanner]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.policeScanner]['cost'][nxtLev] player.truck.addons.level = nxtLev else: self.print( "Not enough money or at max level for police scanner") if objEnum is ObjectType.rabbitFoot: # If the player doesn't currently have a scanner and they have enough money for the base rabbit foot, give them a foot! if (not isinstance(player.truck.addons, RabbitFoot) ) and GameStats.costs_and_effectiveness[ ObjectType.rabbitFoot]['cost'][0] <= player.truck.money: player.truck.addons = RabbitFoot() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.rabbitFoot]['cost'][0] else: # otherwise, upgrade their current scanner ft = player.truck.addons nxtLev = ft.level + 1 if ft.level is not RabbitFootLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType. rabbitFoot]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.rabbitFoot]['cost'][nxtLev] player.truck.addons.level = nxtLev else: self.print( "Not enough money or at max level for rabbit foot") if objEnum is ObjectType.GPS: # If the player doesn't currently have a scanner and they have enough money for the base scanner, give them a scanner! if (not isinstance(player.truck.addons, GPS) ) and GameStats.costs_and_effectiveness[ ObjectType.GPS]['cost'][0] <= player.truck.money: player.truck.addons = GPS() player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.GPS]['cost'][0] else: # otherwise, upgrade their current scanner gp = player.truck.addons nxtLev = gp.level + 1 if gp.level is not GPSLevel.level_three and GameStats.costs_and_effectiveness[ ObjectType.GPS]['cost'][nxtLev] <= player.truck.money: player.truck.money -= GameStats.costs_and_effectiveness[ ObjectType.GPS]['cost'][nxtLev] player.truck.addons.level = nxtLev else: self.print("Not enough money or at max level for GPS") def upgrade_tires(self, player, typ): tireLev = player.truck.tires if typ in TireType.__dict__.values( ) and typ is not tireLev and GameStats.tire_switch_cost <= player.truck.money: player.truck.tires = typ player.truck.money -= GameStats.tire_switch_cost else: self.print( "Either type is not in the enumeration, tiretype is already set to the type requested, or not enough money" ) def upgrade_level(self, player, objEnum, typ=1): """ Handles upgrading various object for the truck. """ try: if not isinstance(player, Player): self.print("The player argument is not a Player object.") return # If the objects enum is an addon type, pass off to addon upgrade method elif objEnum in GameStats.addonObjects: self.upgrade_addons(player, objEnum, typ) player.truck.addons.update() # If the objects enum is a body type, pass off to body upgrade method elif objEnum in GameStats.body_objects: self.upgrade_body(player, objEnum, typ) player.truck.body.update() # The upgrade logic for tires is much simpler, but I have decided to modularize it for the sake of consistancy elif objEnum is ObjectType.tires: self.upgrade_tires(player, objEnum, typ) else: self.print( "The object argument is not a valid upgradable object.") return except Exception as e: self.print(e)
def test_get(self): test_ec = EventController() test_sc = SingletonController() self.assertEqual(test_ec, EventController.get_instance()) self.assertEqual(test_sc, SingletonController.get_instance())
def test_generate(self): EventController() SingletonController()
class MasterController(Controller): def __init__(self): super().__init__() self.turn = 0 # Singletons first self.event_controller = EventController() self.fun_stat_controller = FunStatController() self.accumulative_controller = AccumulativeController() self.city_generator_controller = CityGeneratorController() self.decree_controller = DecreeController() self.destruction_controller = DestructionController() self.disaster_controller = DisasterController() self.effort_controller = EffortController() self.game_over = False # Receives all clients for the purpose of giving them the objects they will control def give_clients_objects(self, client): client.city = City() client.team_name = client.code.team_name() client.city.city_name = client.code.city_name() city_type = client.code.city_type() self.city_generator_controller.handle_actions(client, city_type) # Generator function. Given a key:value pair where the key is the identifier for the current world and the value is # the state of the world, returns the key that will give the appropriate world information def game_loop_logic(self, start=1): self.turn = start # Basic loop from 1 to max turns while True: # Wait until the next call to give the number yield self.turn # Increment the turn counter by 1 self.turn += 1 # If the next turn number is above the max, the iterator ends if self.turn > config.MAX_TURNS: break # Receives world data from the generated game log and is responsible for interpreting it def interpret_current_turn_data(self, client, world, turn): # Turn disaster occurrence into a real disaster for disaster in world['disasters']: d = disaster['disaster'] l = disaster['level'] dis = None if d is DisasterType.earthquake: dis = Earthquake(l) elif d is DisasterType.fire: dis = Fire(l) elif d is DisasterType.blizzard: dis = Blizzard(l) elif d is DisasterType.monster: dis = Monster(l) elif d is DisasterType.tornado: dis = Tornado(l) elif d is DisasterType.ufo: dis = Ufo(l) if dis is None: raise TypeError( f'Attempt to create disaster failed because given type: {disaster}, does not exist.' ) client.disasters.append(dis) self.fun_stat_controller.total_disasters += 1 self.event_controller.add_event({ "event_type": EventType.disaster_spawned, "turn": turn, "disaster": dis.to_json() }) # Run decrees immediately after disasters are generated (before player has a chance to set a new one) self.decree_controller.execute_decree(client) # read the sensor results from the game map, converting strings to ints and/or floats world['sensors'] = { int(key): {int(key2): float(val2) for key2, val2 in val.items()} for key, val in world['sensors'].items() } # give client their corresponding sensor odds for sensor in client.city.sensors.values(): sensor.sensor_results = world['sensors'][sensor.sensor_type][ sensor.level] # Accumulations should occur before the player takes their turn self.accumulative_controller.update(client) # Receive a specific client and send them what they get per turn. Also obfuscates necessary objects. def client_turn_arguments(self, client, world, turn): actions = Action() client.action = actions obfuscated_city = deepcopy(client.city) obfuscated_city.obfuscate() for building in obfuscated_city.buildings.values(): building.obfuscate() for sensor in obfuscated_city.sensors.values(): sensor.obfuscate() obfuscated_disasters = deepcopy(client.disasters) for disaster in obfuscated_disasters: disaster.obfuscate() args = ( self.turn, actions, obfuscated_city, obfuscated_disasters, ) return args # Perform the main logic that happens per turn def turn_logic(self, client, world, turn): self.event_controller.update(turn) self.effort_controller.handle_actions(client) self.decree_controller.update_decree(client.action.get_decree()) self.destruction_controller.handle_actions(client) self.disaster_controller.handle_actions(client) # Fun stat controller interjection self.fun_stat_controller.total_population_ever += client.city.population self.fun_stat_controller.total_structure_ever += client.city.structure if client.city.structure <= 0: self.print("Game is ending because city has been destroyed.") self.game_over = True if client.city.population <= 0: self.print("Game is ending because population has died.") self.game_over = True # Return serialized version of game def create_turn_log(self, client, world, turn): data = dict() # Retrieve all events on this turn data['events'] = [ event for event in self.event_controller.get_events() if event.get('turn') == turn ] data['rates'] = world['rates'] data['player'] = client.to_json() return data # Gather necessary data together in results file def return_final_results(self, client, world, turn): # data is the json information what will be written to the results file data = { "Team": client. team_name, # TODO: Replace with an engine-safe ID of each team "Score": turn, "Events": self.event_controller.get_events(), "Error": client.error, "Statistics": self.fun_stat_controller.export() } return data # Return if the game should be over def game_over_check(self): return self.game_over