Skip to content

Commit

Permalink
feat: add vision system to beings in the world, and create an initial…
Browse files Browse the repository at this point in the history
… state they could learn from
  • Loading branch information
aurbano committed May 21, 2022
1 parent f020ed5 commit 008992c
Show file tree
Hide file tree
Showing 3 changed files with 777 additions and 21 deletions.
67 changes: 63 additions & 4 deletions src/being.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import numpy as np
from math import cos, sin
from math import cos, sin, degrees, atan2, hypot
import random

# Hyper-parameters
Expand All @@ -10,7 +10,8 @@
WATER_TO_ENERGY = 0.001

# Rotation vectors for movement
theta = np.deg2rad(45)
rotation_angle = 45
theta = np.deg2rad(rotation_angle)
rot_right = np.array([[cos(theta), -sin(theta)], [sin(theta), cos(theta)]])
rot_left = np.array([[cos(-theta), -sin(-theta)], [sin(-theta), cos(-theta)]])

Expand All @@ -32,14 +33,31 @@ def __init__(self, sprite_index):
self.water = 1.
self.energy = 1.

# State
self.angle = 0.
self.direction = [1, 0] # vector from (0,0) (the being) to the direction its facing
self.speed = 0.
self.action_space = ['NOOP', 'TURN_LEFT', 'TURN_RIGHT', 'MOVE', 'STOP', 'EAT', 'DRINK']

# Vision
self.vision_angle = 45 # degrees of vision, it can see forward, and vision_angle/2 to each side
self.vision_pixels = 5 # resolution of vision, size of the 2-d array it can "see"
self.vision_distance = 10 # distance it can see objects at

# Don't change these
self.vision_chunk_size = self.vision_angle // self.vision_pixels
self.sprite_index = sprite_index

def choose_action(self):
def choose_action(self, vision):
"""
Choose an action based on the current state and the vision
:param vision:
:return:
"""
state = [self.happiness, self.hunger, self.thirst, self.energy, *vision]

# TODO: Use the state to learn somehow

action = random.choice(self.action_space)

if action == 'TURN_LEFT' or action == 'TURN_RIGHT':
Expand All @@ -49,7 +67,7 @@ def choose_action(self):

return action

def step(self):
def step(self, location, being_locations):
self.energy = max(0, self.energy - ENERGY_LOSS_GENERAL)

if self.energy < 1:
Expand All @@ -64,5 +82,46 @@ def step(self):
if self.speed > 0:
self.energy = max(0, self.energy - ENERGY_LOSS_ACTIONS)

vision = self.vision(location, being_locations)

return self.choose_action(vision)

def vision(self, location, locations):
"""
Calculate the vision array
:param locations:
:return:
"""
direction_angle = degrees(atan2(self.direction[1], self.direction[0]))

min_angle = direction_angle - self.vision_angle / 2
max_angle = direction_angle + self.vision_angle / 2

min_angle %= 360
max_angle %= 360

vision = [0] * self.vision_pixels

# calculate beings in its field of view
for coords in locations:
if coords == location:
# skip itself
continue

y = coords[1] - location[1]
x = coords[0] - location[0]

angle = degrees(atan2(y, x))
angle %= 360

if min_angle <= angle <= max_angle:
dist = hypot(x, y)
if dist <= self.vision_distance:
# visible
vision_chunk = int((angle - min_angle) // self.vision_chunk_size)
vision[vision_chunk] += 1

return vision

def is_alive(self):
return self.energy > 0
Loading

0 comments on commit 008992c

Please sign in to comment.