Initial
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||
{
|
||||
"name": "Python 3",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye"
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
Binary file not shown.
@@ -2,6 +2,9 @@ import math
|
||||
import random
|
||||
import pygame
|
||||
|
||||
BLUE_SAYINGS = ("Oh god my back", "I need to do more cardio", "That LASIK was amazing!")
|
||||
RED_SAYINGS = ("Gotta-gotta-gotta go", "I'm a speed demon", "I'm a blur!")
|
||||
YELLOW_SAYINGS = ("Look at these idiots","I'm average man!","...")
|
||||
|
||||
class GObj:
|
||||
def __init__(self, x, y, radius, speed, turn_rate, heading, sight_distance,
|
||||
@@ -74,7 +77,6 @@ class Player(GObj):
|
||||
self.y+math.sin(self.heading)*self.radius
|
||||
),
|
||||
2)
|
||||
|
||||
|
||||
class Goal(GObj):
|
||||
def __init__(self, x, y, radius=20, speed=0, turn_rate=0.0,
|
||||
@@ -114,6 +116,8 @@ class Enemy(GObj):
|
||||
(x1,y1),
|
||||
(x2,y2)
|
||||
]
|
||||
|
||||
self.ticks = random.randint(1,10) * 60
|
||||
|
||||
def draw(self, screen):
|
||||
xy0 = self.sight_cone[0]
|
||||
@@ -174,11 +178,54 @@ class Enemy(GObj):
|
||||
return (True, (dx, dy), dist)
|
||||
|
||||
# Base class AI routine
|
||||
def ai(self, percept, goals, comms):
|
||||
# Default patroling state shared by all guards
|
||||
# added sayings field to feed in default wandering sayings
|
||||
def ai(self, percept, goals, comms, sayings):
|
||||
if (self.color == "red"):
|
||||
current_guard = "R"
|
||||
elif (self.color == "dodgerblue"):
|
||||
current_guard = "B"
|
||||
elif (self.color == "yellow"):
|
||||
current_guard = "Y"
|
||||
|
||||
# assign self goal if not already assigned
|
||||
# add guarding attibute to comms if not already present
|
||||
if comms[current_guard] == None:
|
||||
comms[current_guard] = random.randint(0, len(goals)-2)
|
||||
# ensure goal selected is not selected by another guard and update if it is
|
||||
|
||||
# ensure guarding area is not touched, and update if it is
|
||||
if goals[comms[current_guard]].is_touched():
|
||||
while goals[comms[current_guard]].is_touched():
|
||||
comms[current_guard] = random.randint(0, len(goals)-1)
|
||||
|
||||
if "spotted" not in comms:
|
||||
comms["spotted"] = None
|
||||
elif comms["spotted"] is not None and comms["spotted"]["spotter"] == self.color:
|
||||
comms["spotted"] = None
|
||||
|
||||
# State checks
|
||||
|
||||
# If enemy is spotted, move toward enemy
|
||||
if percept[0]:
|
||||
comms["spotted"] = {"player": CalculatePlayersCurrentPosition(self, percept[2]), "spotter": self.color}
|
||||
self.ticks = 7 * 60 # reset ticks for sayings after saying something
|
||||
return (TurnTowardEnemy(self.heading, percept[1]), 1, ("Get back here!",2000))
|
||||
# if enemy is spotted by other guard, move toward enemy as reported by guard
|
||||
elif comms["spotted"] is not None:
|
||||
spottedPlayer = comms["spotted"]["player"]
|
||||
self.ticks = 7 * 60 # reset ticks for sayings after saying something
|
||||
return (TurnTowardCoords(self, Player(spottedPlayer[0], spottedPlayer[1])), 1, ("Engaging Suspect!",2000))
|
||||
# check if at guarding goal, assign new goal if there
|
||||
elif self.check_collision(goals[comms[current_guard]]):
|
||||
comms[current_guard] = random.randint(0, len(goals)-1)
|
||||
return (0.0, 0.0, None)
|
||||
# move toward guarding goal
|
||||
else:
|
||||
text = GenerateWanderingText(sayings, self)
|
||||
return (TurnTowardCoords(self, goals[comms[current_guard]]), 0.7, text)
|
||||
|
||||
return (0.0, 0.0, None)
|
||||
|
||||
|
||||
# Yellow is the a mix of the two
|
||||
class EnemyYellow(Enemy):
|
||||
def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0,
|
||||
heading=0.0, sight_distance=120,
|
||||
@@ -186,20 +233,14 @@ class EnemyYellow(Enemy):
|
||||
Enemy.__init__(self, x, y, radius, speed, turn_rate,
|
||||
heading, sight_distance, color,
|
||||
fill, goals)
|
||||
self.ticks = 0
|
||||
# This is ai routine for the type A enemy.
|
||||
def ai(self, percept, goals, comms):
|
||||
if self.ticks > 0:
|
||||
self.ticks-=1
|
||||
elif self.ticks == 0 and percept[0]:
|
||||
self.ticks = 60*4
|
||||
return (0.0, 0.0, ("Don't make me chase you!",2000) )
|
||||
return (0.0, 0.0, None)
|
||||
|
||||
def ai(self, percept, goals, comms):
|
||||
return Enemy.ai(self, percept, goals, comms, YELLOW_SAYINGS)
|
||||
|
||||
# Blue is quite slow but has a good sight distance
|
||||
class EnemyBlue(Enemy):
|
||||
def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0,
|
||||
heading=0.0, sight_distance=120,
|
||||
def __init__(self, x, y, radius=10, speed=60, turn_rate=2.0,
|
||||
heading=0.0, sight_distance=220,
|
||||
color="dodgerblue", fill=0, goals=[]):
|
||||
Enemy.__init__(self, x, y, radius, speed, turn_rate,
|
||||
heading, sight_distance, color,
|
||||
@@ -208,28 +249,58 @@ class EnemyBlue(Enemy):
|
||||
self.ticks = 0
|
||||
# This is the ai routing for the type B enemy.
|
||||
def ai(self, percept, goals, comms):
|
||||
if self.ticks > 0:
|
||||
self.ticks-=1
|
||||
elif self.ticks == 0 and percept[0]:
|
||||
self.ticks = 60*4
|
||||
return (0.0, 0.0, ("I see you!",2000) )
|
||||
return (0.0, 0.0, None)
|
||||
return Enemy.ai(self, percept, goals, comms, BLUE_SAYINGS)
|
||||
|
||||
# Red is blind and cannot see well
|
||||
# but red is fast and zippy
|
||||
class EnemyRed(Enemy):
|
||||
def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0,
|
||||
heading=0.0, sight_distance=120,
|
||||
def __init__(self, x, y, radius=10, speed=125, turn_rate=7.0,
|
||||
heading=0.0, sight_distance=50,
|
||||
color="red", fill=0, goals=[]):
|
||||
Enemy.__init__(self, x, y, radius, speed, turn_rate,
|
||||
heading, sight_distance, color,
|
||||
fill, goals)
|
||||
|
||||
self.ticks = 0
|
||||
|
||||
# This is the ai routing for the type B enemy.
|
||||
def ai(self, percept, goals, comms):
|
||||
if self.ticks > 0:
|
||||
self.ticks-=1
|
||||
elif self.ticks == 0 and percept[0]:
|
||||
self.ticks = 60*4
|
||||
return (0.0, 0.0, ("Enemy!",2000) )
|
||||
return (0.0, 0.0, None)
|
||||
return Enemy.ai(self, percept, goals, comms, RED_SAYINGS)
|
||||
|
||||
# Common Functions
|
||||
def TurnTowardEnemy(heading_angle, enemy_vector):
|
||||
# Convert heading angle to a unit vector
|
||||
heading_vector = [math.cos(heading_angle), math.sin(heading_angle)]
|
||||
# Calculate the dot product
|
||||
dot_product = heading_vector[0] * enemy_vector[0] + heading_vector[1] * enemy_vector[1]
|
||||
# Calculate the angle between the two vectors
|
||||
angle = math.acos(dot_product)
|
||||
# Calculate the cross product (in 2D, this is the determinant of the 2x2 matrix)
|
||||
cross_product = heading_vector[0] * enemy_vector[1] - heading_vector[1] * enemy_vector[0]
|
||||
return math.copysign(angle / math.pi, cross_product)
|
||||
|
||||
def TurnTowardCoords(self, target):
|
||||
dx = target.x - self.x
|
||||
dy = target.y - self.y
|
||||
dist = math.sqrt(dx*dx + dy*dy)
|
||||
dx /= dist
|
||||
dy /= dist
|
||||
|
||||
# Patrolling is slower
|
||||
return TurnTowardEnemy(self.heading, (dx, dy))
|
||||
|
||||
def CalculatePlayersCurrentPosition(self, distance):
|
||||
# calculate player coordinates using position (self.x, self.y), heading (self.heading) and distance (distance)
|
||||
return (self.x + distance * math.cos(self.heading), self.y + distance * math.sin(self.heading))
|
||||
|
||||
def GenerateWanderingText(sayings, self):
|
||||
# Function is run at 60 / second per AI
|
||||
# This function will generate a random saying from the list of sayings
|
||||
# One saying per 7 seconds of walking
|
||||
# 7 seconds is 4200 milliseconds
|
||||
# 4200 / 60 = 70
|
||||
# 70 ticks
|
||||
if self.ticks == 0:
|
||||
self.ticks = 60 * random.randint(1,10) + 60
|
||||
return (random.choice(sayings), 2000)
|
||||
else:
|
||||
self.ticks -= 1
|
||||
return None
|
||||
Reference in New Issue
Block a user