Final
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -95,3 +95,184 @@ class AI:
|
||||
|
||||
# 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
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
import cell_terrain
|
||||
|
||||
|
||||
# Modified
|
||||
# Open: -2 for Zombies
|
||||
# Shadows: +4 for Zombies
|
||||
class Cell:
|
||||
def __init__(self, terrain):
|
||||
self.terrain = terrain
|
||||
@@ -23,19 +25,19 @@ class Cell:
|
||||
match self.terrain:
|
||||
case cell_terrain.Terrain.Open:
|
||||
return "cornsilk2"
|
||||
case cell_terrain.Terrain.Forest:
|
||||
case cell_terrain.Terrain.Shadows:
|
||||
return "cornsilk3"
|
||||
|
||||
def get_attack_mod(self):
|
||||
def get_attack_mod(self, unitType):
|
||||
match self.terrain:
|
||||
case cell_terrain.Terrain.Open:
|
||||
return 2
|
||||
case cell_terrain.Terrain.Forest:
|
||||
return 0
|
||||
return -2 if unitType == "Z" else 0
|
||||
case cell_terrain.Terrain.Shadows:
|
||||
return 4 if unitType == "Z" else 0
|
||||
|
||||
def get_defense_mod(self):
|
||||
def get_defense_mod(self, unitType):
|
||||
match self.terrain:
|
||||
case cell_terrain.Terrain.Open:
|
||||
return 0
|
||||
case cell_terrain.Terrain.Forest:
|
||||
return 2
|
||||
return -2 if unitType == "Z" else 0
|
||||
case cell_terrain.Terrain.Shadows:
|
||||
return +4 if unitType == "Z" else 0
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ import enum
|
||||
|
||||
class Terrain(enum.Enum):
|
||||
Open = 0
|
||||
Forest = 1
|
||||
Shadows = 1
|
||||
|
||||
+7
-1
@@ -25,4 +25,10 @@ class Faction:
|
||||
|
||||
# ################################################################
|
||||
def run_ai(self, factions, cities, units, gmap):
|
||||
return self.ai.run_ai(self.ID, factions, cities, units, gmap)
|
||||
match self.ID:
|
||||
case "Zombies":
|
||||
return self.ai.run_ai_zombies(self.ID, factions, cities, units, gmap)
|
||||
case "Survivors":
|
||||
return self.ai.run_ai_survivors(self.ID, factions, cities, units, gmap)
|
||||
case "Cured":
|
||||
return self.ai.run_ai_cured(self.ID, factions, cities, units, gmap)
|
||||
|
||||
@@ -99,6 +99,7 @@ class Display:
|
||||
|
||||
def init_display(sw, sh):
|
||||
pygame.init()
|
||||
pygame.display.set_caption('Infection')
|
||||
screen = pygame.display.set_mode((sw, sh))
|
||||
clock = pygame.time.Clock()
|
||||
display = Display(screen, clock)
|
||||
@@ -124,7 +125,7 @@ def gen_factions(gmap):
|
||||
# Allows one time generation of X zombies
|
||||
# x = random.randint(4, 8) * 100
|
||||
factions['Zombies'] = faction.Faction(
|
||||
'Zombies', random.randint(8, 16) * 100,
|
||||
'Zombies', random.randint(3, 16) * 100,
|
||||
ai.AI(), 'darkgreen')
|
||||
|
||||
# Starts with normal amount of money
|
||||
@@ -380,8 +381,8 @@ def RunCombat(attacker, defender, cmd, factions, unit_dict, cities, gmap):
|
||||
def_roll = defender.roll(attacker.utype)
|
||||
|
||||
# Add terrain modifiers.
|
||||
att_roll += att_cell.get_attack_mod()
|
||||
def_roll += def_cell.get_defense_mod()
|
||||
att_roll += att_cell.get_attack_mod(attacker.faction_id)
|
||||
def_roll += def_cell.get_defense_mod(defender.faction_id)
|
||||
|
||||
# Damage health.
|
||||
defender.health -= att_roll
|
||||
@@ -491,11 +492,20 @@ class UnitDict:
|
||||
|
||||
|
||||
|
||||
def CheckForGameOver(cities):
|
||||
def CheckForGameOver(cities, unit_dict):
|
||||
faction_ids_with_cities = []
|
||||
for c in cities:
|
||||
if c.faction_id not in faction_ids_with_cities:
|
||||
faction_ids_with_cities.append(c.faction_id)
|
||||
|
||||
# Verify all units are of same type
|
||||
for unit in unit_dict.allUnits:
|
||||
if unit.faction_id not in faction_ids_with_cities:
|
||||
faction_ids_with_cities.append(unit.faction_id)
|
||||
|
||||
if len(faction_ids_with_cities) == 2 and "Survivors" in faction_ids_with_cities and "Cured" in faction_ids_with_cities:
|
||||
return True, "Cured"
|
||||
|
||||
return len(faction_ids_with_cities) == 1, faction_ids_with_cities[0]
|
||||
|
||||
|
||||
@@ -517,6 +527,8 @@ def GameLoop(display):
|
||||
# - The map_cell_size given in the Display class above.
|
||||
gmap = gen_game_map(40, 30)
|
||||
|
||||
maxY = None
|
||||
|
||||
factions = gen_factions(gmap)
|
||||
# City gen is basically locked
|
||||
cities = gen_cities(gmap)
|
||||
@@ -547,8 +559,10 @@ def GameLoop(display):
|
||||
speed = speed * 2
|
||||
|
||||
|
||||
display.screen.fill("white")
|
||||
display.screen.fill("cornsilk3")
|
||||
|
||||
game_over = [False, None]
|
||||
|
||||
if ticks >= speed:
|
||||
ticks = 0
|
||||
cities_by_faction = {}
|
||||
@@ -562,11 +576,7 @@ def GameLoop(display):
|
||||
RunAllCommands(commands, factions, unit_dict, cities, gmap)
|
||||
turn += 1
|
||||
|
||||
game_over = CheckForGameOver(cities)
|
||||
if game_over[0]:
|
||||
print(f"Winning faction: {game_over[1]}")
|
||||
display.run = False
|
||||
|
||||
game_over = CheckForGameOver(cities, unit_dict)
|
||||
|
||||
display.draw_map(gmap)
|
||||
display.draw_cities(cities, factions)
|
||||
@@ -575,26 +585,51 @@ def GameLoop(display):
|
||||
# ###########################################3
|
||||
# RIGHT_SIDE UI
|
||||
display.draw_text(f"TURN {turn}", 805, 5, "black")
|
||||
display.draw_text(f"{'Fctn':<5} {'C':>2} {'U':>3} {'M':>4}",
|
||||
805, 25, "black")
|
||||
y = 45
|
||||
for fid, f in factions.items():
|
||||
num_cities = 0
|
||||
for c in cities:
|
||||
if c.faction_id == fid:
|
||||
num_cities += 1
|
||||
|
||||
display.draw_text(f"{fid:<5} {num_cities:>2} {len(unit_dict.unitsByFaction()[fid]):>3} {f.money:>4}",
|
||||
805, y, "black")
|
||||
y += 20
|
||||
|
||||
numZombies = len(unit_dict.unitsByFaction()["Zombies"])
|
||||
numSurvivors = len(unit_dict.unitsByFaction()["Survivors"])
|
||||
numCured = len(unit_dict.unitsByFaction()["Cured"])
|
||||
totalUnits = len(unit_dict.allUnits)
|
||||
|
||||
infectionRate = numZombies / totalUnits * 100 if totalUnits > 0 else 0
|
||||
cureRate = numCured / totalUnits * 100 if totalUnits > 0 else 0
|
||||
|
||||
display.draw_text("Infection Rate", 805, 45, "black")
|
||||
display.draw_text(f"{infectionRate:.2f}%", 805, 65, "black")
|
||||
|
||||
display.draw_text("Cure Progress", 805, 85, "black")
|
||||
display.draw_text(f"{cureRate:.2f}%", 805, 105, "black")
|
||||
|
||||
y = 145
|
||||
|
||||
display.draw_text(f"Cities", 805, y, "black")
|
||||
y += 20
|
||||
for city in cities:
|
||||
display.draw_text(f"{city.ID}", 805, y, factions[city.faction_id].color)
|
||||
y += 20
|
||||
|
||||
maxY = y
|
||||
|
||||
if game_over[0]:
|
||||
display.draw_text(f"{game_over[1]} won", 805, maxY + 20, "black")
|
||||
display.draw_text("Press any key to quit", 805, maxY + 40, "black")
|
||||
pygame.display.flip()
|
||||
key = None
|
||||
while key == None:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
display.run = False
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
key = event.key
|
||||
print(f"Winning faction: {game_over[1]}")
|
||||
display.run = False
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
|
||||
def main():
|
||||
random.seed(None)
|
||||
display = init_display(1000, 600)
|
||||
display = init_display(1100, 600)
|
||||
GameLoop(display)
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import random
|
||||
# algorithms here. If you want something fancier, you'd need
|
||||
# to add them below and call them in game_map.py.
|
||||
CELL_TERRAIN_PROBABILITY = {
|
||||
cell_terrain.Terrain.Forest: 1,
|
||||
cell_terrain.Terrain.Shadows: 1,
|
||||
cell_terrain.Terrain.Open: 4
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import math
|
||||
# C = CURED (EXTREMELY SLOW)
|
||||
UNIT_COSTS = {
|
||||
"Z": 100,
|
||||
"S": 500,
|
||||
"C": 2500
|
||||
"S": 250,
|
||||
"C": 2000
|
||||
}
|
||||
|
||||
# UNIT_HEALATH is a constant dictionary that holds the starting health
|
||||
@@ -28,7 +28,7 @@ UNIT_COSTS = {
|
||||
# C = CURED (HIGH HEALTH)
|
||||
|
||||
UNIT_HEALTH = {
|
||||
"Z": 5,
|
||||
"Z": 6,
|
||||
"S": 7,
|
||||
"C": 7
|
||||
}
|
||||
@@ -75,6 +75,9 @@ class Unit:
|
||||
# sight_radius: int - how far it sees
|
||||
# NOT USED.
|
||||
self.sight_radius = sight_radius
|
||||
|
||||
# Allows for persistent target (if applicable)
|
||||
self.currentTarget = None
|
||||
|
||||
def __eq__(self, o):
|
||||
return self.ID == o.ID and self.faction_id == o.faction_id
|
||||
@@ -91,11 +94,4 @@ class Unit:
|
||||
# rock-paper-scissors have max damage of 20. All other
|
||||
# combinations are 10. Feel free to modify if you want.
|
||||
def roll(self, op_utype):
|
||||
if op_utype == 'R' and self.utype == 'P':
|
||||
return random.randint(0, 20)
|
||||
elif op_utype == 'P' and self.utype == 'S':
|
||||
return random.randint(0, 20)
|
||||
elif op_utype == 'S' and self.utype == 'R':
|
||||
return random.randint(0, 20)
|
||||
else:
|
||||
return random.randint(0, 10)
|
||||
|
||||
Reference in New Issue
Block a user