commit 2e304247f6b6dd28399d3459e2c5ca1f29f45732 Author: Nicholas Pease Date: Thu Feb 20 13:46:55 2025 -1000 Upload files to "/" diff --git a/JuliaMono-Bold.ttf b/JuliaMono-Bold.ttf new file mode 100644 index 0000000..277ec41 Binary files /dev/null and b/JuliaMono-Bold.ttf differ diff --git a/gobjs.py b/gobjs.py new file mode 100644 index 0000000..ef082dd --- /dev/null +++ b/gobjs.py @@ -0,0 +1,235 @@ +import math +import random +import pygame + + +class GObj: + def __init__(self, x, y, radius, speed, turn_rate, heading, sight_distance, + color="white", fill=0): + self.x = x + self.y = y + self.radius = radius + self.speed = speed + self.turn_rate = turn_rate + self.heading = heading + self.sight_distance = sight_distance + self.color = color + self.fill = fill + def pos(self): + return (self.x, self.y) + def move(self, dt, direction=1.0): + self.x += direction*self.speed*dt*math.cos(self.heading) + self.y += direction*self.speed*dt*math.sin(self.heading) + def turn(self, dt, direction): + self.heading += direction*self.turn_rate*dt + # def can_see(self, gameobj): + # ox = gameobj.x + # oy = gameobj.y + + # x1 = self.x + # y1 = self.y + # x2 = x1+math.cos(self.heading)*self.sight_distance + # y2 = y1+math.sin(self.heading)*self.sight_distance + # D = x1*y2 - x2*y1 + # I = gameobj.radius**2 * self.sight_distance**2 - D**2 + # return I >= 0 + def check_collision(self, gameobj): + distance = math.sqrt( (gameobj.x-self.x)**2 + (gameobj.y-self.y)**2 ) + if distance < self.radius+gameobj.radius: + return True + else: + return False + def onscreen(self, rect): + if (self.x+self.radius >= rect[0] and + self.x-self.radius <= rect[2] and + self.y+self.radius >= rect[1] and + self.y-self.radius <= rect[3]): + return True + else: + return False + def draw(self, screen): + pygame.draw.circle( + screen, + self.color, + (self.x, self.y), + self.radius, + self.fill + ) + +class Player(GObj): + def __init__(self, x, y, radius=10, speed=100, turn_rate=5.0, + heading=0, sight_distance=0, color="darkorchid", + fill=0): + GObj.__init__(self, x, y, radius, speed, turn_rate, + heading, sight_distance, color, + fill) + def draw(self, screen): + GObj.draw(self, screen) + pygame.draw.line( + screen, + "black", + (self.x, self.y), + ( + self.x+math.cos(self.heading)*self.radius, + 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, + heading=0.0, sight_distance=0.0, + color="red", fill=3): + GObj.__init__(self, x, y, radius, speed, turn_rate, + heading, sight_distance, color, + fill) + self.touched = False + def touch(self): + self.color = "green" + self.touched = True + def is_touched(self): + return self.touched + +class Enemy(GObj): + def __init__(self, x, y, radius, speed, turn_rate, + heading, sight_distance, + color, fill, goals): + GObj.__init__(self, x, y, radius, speed, turn_rate, + heading, sight_distance, color, + fill) + self.goals = goals + self.sight_cone_color_clear = 'white' + self.sight_cone_color_obj = 'fuchsia' + self.sight_cone_color = self.sight_cone_color_clear + + x0 = self.x + y0 = self.y + x1 = self.x+math.cos(self.heading-math.pi/6)*(self.radius+self.sight_distance) + y1 = self.y+math.sin(self.heading-math.pi/6)*(self.radius+self.sight_distance) + x2 = self.x+math.cos(self.heading+math.pi/6)*(self.radius+self.sight_distance) + y2 = self.y+math.sin(self.heading+math.pi/6)*(self.radius+self.sight_distance) + + self.sight_cone = [ + (x0,y0), + (x1,y1), + (x2,y2) + ] + + def draw(self, screen): + xy0 = self.sight_cone[0] + xy1 = self.sight_cone[1] + xy2 = self.sight_cone[2] + + pygame.draw.line( + screen, + self.sight_cone_color, + (xy0[0], xy0[1]), + (xy1[0], xy1[1]), + 1) + pygame.draw.line( + screen, + self.sight_cone_color, + (xy1[0],xy1[1]), + (xy2[0],xy2[1]), + 1) + pygame.draw.line( + screen, + self.sight_cone_color, + (xy2[0],xy2[1]), + (xy0[0],xy0[1]), + 1) + + GObj.draw(self, screen) + + def update(self, gameobj): + ox = gameobj.x + oy = gameobj.y + + x0 = self.x + y0 = self.y + x1 = self.x+math.cos(self.heading-math.pi/6)*(self.radius+self.sight_distance) + y1 = self.y+math.sin(self.heading-math.pi/6)*(self.radius+self.sight_distance) + x2 = self.x+math.cos(self.heading+math.pi/6)*(self.radius+self.sight_distance) + y2 = self.y+math.sin(self.heading+math.pi/6)*(self.radius+self.sight_distance) + + self.sight_cone[0] = (x0, y0) + self.sight_cone[1] = (x1, y1) + self.sight_cone[2] = (x2, y2) + + + for i in range(len(self.sight_cone)): + x1 = self.sight_cone[i][0] + y1 = self.sight_cone[i][1] + x2 = self.sight_cone[(i+1)%len(self.sight_cone)][0] + y2 = self.sight_cone[(i+1)%len(self.sight_cone)][1] + if (x2-x1)*(oy-y1) - (y2-y1)*(ox-x1) <= 0: + self.sight_cone_color = self.sight_cone_color_clear + return (False,None) + self.sight_cone_color = self.sight_cone_color_obj + dx = ox-self.x + dy = oy-self.y + dist = math.sqrt(dx*dx + dy*dy) + dx /= dist + dy /= dist + return (True, (dx, dy), dist) + + # Base class AI routine + def ai(self, percept, goals, comms): + + return (0.0, 0.0, None) + + +class EnemyYellow(Enemy): + def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0, + heading=0.0, sight_distance=120, + color="yellow", fill=0, goals=[]): + 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) + + +class EnemyBlue(Enemy): + def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0, + heading=0.0, sight_distance=120, + color="dodgerblue", 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, ("I see you!",2000) ) + return (0.0, 0.0, None) + +class EnemyRed(Enemy): + def __init__(self, x, y, radius=10, speed=90, turn_rate=3.0, + heading=0.0, sight_distance=120, + 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) diff --git a/hw1_main.py b/hw1_main.py new file mode 100644 index 0000000..dfb12ec --- /dev/null +++ b/hw1_main.py @@ -0,0 +1,163 @@ +import pygame +import pygame.freetype +import math +import random +import gobjs + +class Display: + def __init__(self, screen, clock): + self.screen = screen + self.clock = clock + self.run = True + self.delta = 0 + self.font = None + + def draw_gobj(self, gobj): + pygame.draw.circle( + self.screen, + gobj.color, + gobj.pos(), + gobj.radius) + def draw_text(self, msg, x, y, color): + surface, rect = self.font.render(msg, color) + self.screen.blit(surface, (x-rect.w//2, y-rect.h)) + + +def init_display(sw, sh): + pygame.init() + screen = pygame.display.set_mode((sw,sh)) + clock = pygame.time.Clock() + display = Display(screen, clock) + display.font = pygame.freetype.Font('JuliaMono-Bold.ttf', 18) + return display + +def handle_input(keys, dt, player, win_w, win_h): + pos = player.pos() + r = player.radius + + if keys[pygame.K_a]: + player.turn(dt, -1) + elif keys[pygame.K_d]: + player.turn(dt, 1) + if keys[pygame.K_w]: + player.move(dt) + elif keys[pygame.K_s]: + player.move(dt, -1.0) + +def gather_perecpts(enemy, player): + return enemy.can_see(player) + +def game_loop(display): + + win_w, win_h = pygame.display.get_window_size() + d_rect = (0, 0, win_w, win_h) + + # NOTE: You are welcome to modify some aspects of the following + # The player and enemy classes do take additional parameters + # which are currently set to defaults. You are welcome to + # override them to get a different scenario. + + # Possible Player start locations (x, y, heading) + start_locs = [ + (50,50, math.pi/4), + (win_w//2, 50, math.pi/2), + (win_w-50, 50, 3*math.pi/4), + (50, win_h//2, 0), + (win_w-50, win_h//2, math.pi), + (50, win_h-50, -math.pi/4), + (win_w//2, win_h-50, -math.pi/2), + (win_w-50, win_h-50, -3*math.pi/4) + ] + # Randomly choose one. + player_start = random.choice(start_locs) + player = gobjs.Player(player_start[0], player_start[1], heading=player_start[2]) + + # Goals. The number passed into the Goal contructor are the x,y coords. + goals = [ + gobjs.Goal(200, 200), + gobjs.Goal(win_w-200, 200), + gobjs.Goal(200, win_h-200), + gobjs.Goal(win_w-200, win_h-200), + gobjs.Goal(win_w//2, win_h//2) + ] + + # The enemies. + enemy1 = gobjs.EnemyYellow(win_w//2+50, win_h//2, heading=0) + enemy2 = gobjs.EnemyBlue(win_w//2-50, win_h//2, heading=math.pi) + enemy3 = gobjs.EnemyRed(win_w//2, win_h//2-50, heading=-math.pi/2) + enemies = [enemy1, enemy2, enemy3] + comms = { + "R": None, + "B": None, + "Y": None + } + + tick = 0 + goal_count = 0 + winner = "Draw" + msgs = [] + + + # You probably don't need to modify anything in the main loop + while display.run: + dt = display.clock.tick(60) / 1000 + + # Check event queue for quit + for event in pygame.event.get(): + if event.type == pygame.QUIT: + display.run = False + + display.screen.fill("black") + + next_msgs = [] + for msg in msgs: + if pygame.time.get_ticks() - msg[2] < msg[0][1]: + display.draw_text(msg[0][0], msg[1].x, msg[1].y-(msg[1].radius+3), msg[1].color) + next_msgs.append(msg) + msgs = next_msgs + + for g in goals: + if not g.is_touched(): + if g.check_collision(player): + g.touch() + goal_count += 1 + if goal_count == len(goals): + display.run = False + winner = "Player" + g.draw(display.screen) + + # Call Enemy AI and draw + for e in enemies: + mt = e.ai(e.update(player), goals, comms) + e.turn(dt, mt[0]) + e.move(dt, mt[1]) + if mt[2] is not None: + msgs.append( (mt[2], e, pygame.time.get_ticks()) ) + if e.check_collision(player): + display.run = False + winner = "AI" + if not e.onscreen(d_rect): + display.run = False + winner = "Player" + e.draw(display.screen) + + # Move and draw the player + keys = pygame.key.get_pressed() + handle_input(keys, dt, player, win_w, win_h) + player.draw(display.screen) + + if not player.onscreen(d_rect): + winner = "AI" + display.run=False + + pygame.display.flip() + + print(f"The winner is the {winner}.") + + +def main(): + display = init_display(800, 800) + game_loop(display) + +if __name__ == "__main__": + main()