# AI Class # by zax # This is the main file you will edit. The run_ai function's job # is to issue two types of commands (see command.py): # - BuildUnitCommand: asks the game engine to build a specific type # of unit at a specific city if the faction has enough cash # available. # - MoveUnitCommand: asks the game engine to move a specific unit # in a specific direction. The engine will only move a unit # if the destination cell is free. If it is occupied by a friendly # unit, nothing happens. If it is occupied by another faction, # combat ensues. from command import * import random import unit class AI: # init: # Currently, the initializer adds nothing to the object. # You are welcome to modify to have a place to keep # information that persists across calls to run_ai(). # # NOTE: AI objects are passed into the Faction initializer # when a faction is created (see the gen_factions() function # in the main.py file). If you'd like to subclass the AI class # to differentiate between faction behaviors, you are welcome # to do so. def __init__(self): pass # run_ai # Parameters: # - faction_id: this is the faction_id of this AI object. # Use it to access infomation in the other parameters. # - factions: dictionary of factions by faction_id. # - cities: dictionary of cities stored by faction_id. # For example: cities[faction_id] would return all the # the city objects owned by this faction. # - units: dictionary of all units by faction_id. # Similar to the cities dictionary, units[faction_id] # would return all unit objects belonging to the faction. # - gmap: the game map object. You can use this (if you wish) # to get information about the map and terrain. # # Return: # This function should return a list of commands to be processed # by the game engine this turn. # # NOTE: You should replace the following code with your # own. The code currently gives the factions totally random # behavior. Totally random behavior, while interesting, # is not an acceptable solution. # # NOTE 2: Every ai has access to ALL game objects. This means # you can (and should) access the unit and city locations # of the other faction. I STRONGLY advise against manually # changing unit or city locations from the AI file. Doing so # circumvents checks made by the game engine and is likely to # have bad side effects. If you want something more actions # than those provided by the two commands, I suggest taking # the time to create additional Command subclasses and properly # implement them in the engine (main.py). def run_ai(self, faction_id, factions, cities, units, gmap): # A list to hold our commands. This gets returned by # the function. cmds = [] # Overview: randomly select a city we own and randomly # select a unit type (utype). Create a BuildUnitCommand # This is done every turn knowing most will fail because # the faction does not have enough money to build them. my_cities = cities[faction_id] city_indexes = list(range(len(my_cities))) random.shuffle(city_indexes) for ci in city_indexes: cmd = BuildUnitCommand(faction_id, my_cities[ci].ID, random.choice(faction_id[:1])) cmds.append(cmd) # Overview: issue a move to every unit giving a random # direction. Directions can be found in the vec2.py file. # They are single char strings: 'N', 'E', 'W', 'S'. my_units = units[faction_id] for u in my_units: rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) # return all the command objects. return cmds # Needs Seperate AI's for each faction # AI for Zombies def run_ai_zombies(self, faction_id, factions, cities, units, gmap): cmds = [] # Generate Zombies (Typically Done Only At Start) # Verify there are remaining zombie cities if (len(cities['Zombies']) != 0): cmd = BuildUnitCommand(faction_id, cities['Zombies'][0].ID, "Z") cmds.append(cmd) # Random moves (zombies) my_units = units[faction_id] for u in my_units: rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) return cmds # AI for Survivors def run_ai_survivors(self, faction_id, factions, cities, units, gmap): cmds = [] # Attempts to create new survivors via reproduction my_cities = cities[faction_id] city_indexes = list(range(len(my_cities))) random.shuffle(city_indexes) for ci in city_indexes: cmd = BuildUnitCommand(faction_id, my_cities[ci].ID, "S") cmds.append(cmd) # Rare case # If cured city is lost, and there are adequate survivors to make a new cured city # one survivor city at random can produce a cured unit if (len(cities['Cured']) == 0) and (len(units[faction_id]) >= 50): my_cities = cities[faction_id] city_indexes = list(range(len(my_cities))) random.shuffle(city_indexes) cmd = BuildUnitCommand("Cured", my_cities[city_indexes].ID, "C") cmds.append(cmd) # Survivors will also attempt to defend cured cities totalCities = cities[faction_id] + cities['Cured'] # Loop through all of the survivors for u in units[faction_id]: # First checks to see if the survivor was given another tasking previously (i.e defend x city) # If so, verify city had not fallen and move towards it # If within 3 tiles of city, pick another target if u.currentTarget in totalCities: # 10% chance of random movement if random.random() < 0.1: rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) continue # Target is a city, so move towards it target = u.currentTarget #Given current position as (x,y) and target as (x,y), calculate next move to get closer to city vectorDirection = (target.pos.x - u.pos.x, target.pos.y - u.pos.y) # X is larger, move that way first if abs(vectorDirection[0]) > abs(vectorDirection[1]): if vectorDirection[0] > 0: cmd = MoveUnitCommand(faction_id, u.ID, 'E') else: cmd = MoveUnitCommand(faction_id, u.ID, 'W') else: if vectorDirection[1] > 0: cmd = MoveUnitCommand(faction_id, u.ID, 'S') else: cmd = MoveUnitCommand(faction_id, u.ID, 'N') cmds.append(cmd) # Too close to city, so pick another target if abs(u.pos.distance_man(target.pos)) <= 4: if len(totalCities) != 0: u.currentTarget = random.choice(totalCities) # 2 Actions to prevent clumping cmds.append(MoveUnitCommand(faction_id, u.ID, random.choice(list(vec2.MOVES.keys())))) cmds.append(MoveUnitCommand(faction_id, u.ID, random.choice(list(vec2.MOVES.keys())))) else: u.currentTarget = None else: # City has fallen, so clear target # Establish new target (if available) and move randomly if len(totalCities) != 0: u.currentTarget = random.choice(totalCities) else: u.cuurrentTarget = None rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) return cmds # AI for Cured def run_ai_cured(self, faction_id, factions, cities, units, gmap): cmds = [] # Attempts to create new cured my_cities = cities[faction_id] city_indexes = list(range(len(my_cities))) random.shuffle(city_indexes) for ci in city_indexes: cmd = BuildUnitCommand(faction_id, my_cities[ci].ID, "C") cmds.append(cmd) allCities = cities[faction_id] + cities['Zombies'] + cities['Survivors'] # Loop through all of the cured for u in units[faction_id]: # Once there are cured in the world, they will attempt to "infect" any cities on the map, one at a time # Check if we are currently targeting a city target = u.currentTarget if target in allCities: # 10% chance of random movement if random.random() < 0.1: rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) continue #Given current position as (x,y) and target as (x,y), calculate next move to get closer to city vectorDirection = (target.pos.x - u.pos.x, target.pos.y - u.pos.y) # X is larger, move that way first if abs(vectorDirection[0]) > abs(vectorDirection[1]): if vectorDirection[0] > 0: cmd = MoveUnitCommand(faction_id, u.ID, 'E') else: cmd = MoveUnitCommand(faction_id, u.ID, 'W') else: if vectorDirection[1] > 0: cmd = MoveUnitCommand(faction_id, u.ID, 'S') else: cmd = MoveUnitCommand(faction_id, u.ID, 'N') cmds.append(cmd) if (target not in allCities) or (abs(u.pos.distance_man(target.pos)) <= 4): # Pick first city that is not already Cured and target it if len(allCities) != 0: for city in allCities: if city.faction_id != "Cured": u.currentTarget = city break else: u.currentTarget = None cmds.append(MoveUnitCommand(faction_id, u.ID, random.choice(list(vec2.MOVES.keys())))) cmds.append(MoveUnitCommand(faction_id, u.ID, random.choice(list(vec2.MOVES.keys())))) # No cities left to infect, so target remaining uncured units if u.currentTarget == None or u.currentTarget.faction_id == "Cured": for faction in factions: if faction != faction_id: for unit in units[faction]: u.currentTarget = unit break else: u.currentTarget = None rand_dir = random.choice(list(vec2.MOVES.keys())) cmd = MoveUnitCommand(faction_id, u.ID, rand_dir) cmds.append(cmd) return cmds