Files
2025-04-18 23:04:16 +00:00

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