278 lines
12 KiB
Python
278 lines
12 KiB
Python
# 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 |