Skip to content

Commit

Permalink
Merge pull request #48 from vagechirkov/round-environment
Browse files Browse the repository at this point in the history
Round environment
  • Loading branch information
mezdahun authored Nov 14, 2022
2 parents 8f4c00a + 09f67da commit 0c687c2
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 98 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/docker-image-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Docker Image CI

on:
push:
branches:
- develop

jobs:

build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Docker Login
env:
DOCKER_USER: ${{secrets.DOCKER_USER}}
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
run: |
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
- name: Build the Docker image
run: docker build . --file Dockerfile --tag ${{secrets.DOCKER_USER}}/scioip34abm:latest
- name: Push Docker image to DockerHub
run: docker push ${{secrets.DOCKER_USER}}/scioip34abm:latest
19 changes: 5 additions & 14 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: Docker Image CI

on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
branches:
- develop

jobs:

Expand All @@ -13,14 +12,6 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Docker Login
env:
DOCKER_USER: ${{secrets.DOCKER_USER}}
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
run: |
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
- name: Build the Docker image
run: docker build . --file Dockerfile --tag ${{secrets.DOCKER_USER}}/scioip34abm:latest
- name: Push Docker image to DockerHub
run: docker push ${{secrets.DOCKER_USER}}/scioip34abm:latest
- uses: actions/checkout@v2
- name: Build the Docker image
run: docker build . --file Dockerfile
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Python package

on: [push]
on: [push, pull_request]

jobs:
build:
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,7 @@ cython_debug/
.idea

# ignore all copied env files
*_copy.env
*_copy.env

# ignore videos
*.mp4
69 changes: 57 additions & 12 deletions abm/projects/cooperative_signaling/cs_agent/cs_agent.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import numpy as np

from abm.agent import supcalc
from abm.projects.cooperative_signaling.cs_agent.cs_supcalc import \
reflection_from_circular_wall, random_walk, F_reloc_LR, phototaxis
from abm.agent.agent import Agent
from abm.contrib import colors
from abm.projects.cooperative_signaling.cs_agent.cs_supcalc import random_walk, \
F_reloc_LR, phototaxis


class CSAgent(Agent):
Expand Down Expand Up @@ -57,11 +57,11 @@ def update(self, agents):
else:
if self.meter > 0:
theta, taxis_dir = phototaxis(
self.meter,
self.prev_meter,
self.theta_prev,
self.taxis_dir,
self.phototaxis_theta_step)
self.meter,
self.prev_meter,
self.theta_prev,
self.taxis_dir,
self.phototaxis_theta_step)
self.taxis_dir = taxis_dir
vel = (2 - self.velocity)
self.agent_type = "mars_miner"
Expand All @@ -84,12 +84,15 @@ def update(self, agents):
self.velocity += vel
# self.prove_velocity() # possibly bounding velocity of agent

# updating agent's position
self.position[0] += self.velocity * np.cos(self.orientation)
self.position[1] -= self.velocity * np.sin(self.orientation)
# new agent's position
new_pos = (
self.position[0] + self.velocity * np.cos(self.orientation),
self.position[1] - self.velocity * np.sin(self.orientation)
)

# boundary conditions if applicable
self.reflect_from_walls()
# update the agent's position with constraints (reflection from the
# walls) or with the new position
self.position = list(self.reflect_from_walls(new_pos))
else:
# self.agent_type = "signalling"
print(self.meter)
Expand Down Expand Up @@ -286,3 +289,45 @@ def prove_velocity(self, velocity_limit=1):
if np.abs(self.velocity) > velocity_limit:
# stopping agent if too fast during exploration
self.velocity = 1

def reflect_from_walls(self, new_pos=()):
"""
Reflecting agent from the circle arena border.
"""
# x coordinate - x of the center point of the circle
x = new_pos[0] + self.radius
c_x = (self.WIDTH / 2 + self.window_pad)
dx = x - c_x
# y coordinate - y of the center point of the circle
y = new_pos[1] + self.radius
c_y = (self.HEIGHT / 2 + self.window_pad)
dy = y - c_y
# radius of the environment
e_r = self.HEIGHT / 2

# return if the agent has not reached the boarder
if np.linalg.norm([dx, dy]) + self.radius < e_r:
return new_pos

# reflect the agent from the boarder
self.orientation = reflection_from_circular_wall(
dx, dy, self.orientation)

# make orientation between 0 and 2pi
self.prove_orientation()

# relocate the agent back inside the circle
new_pos = (
self.position[0] + self.velocity * np.cos(self.orientation),
self.position[1] - self.velocity * np.sin(self.orientation)
)
# check if the agent is still outside the circle
diff = [new_pos[0] - c_x, new_pos[1] - c_y]
if np.linalg.norm(diff) + self.radius >= e_r:
# if yes, relocate it again
dist = np.linalg.norm(diff) + self.radius - e_r
new_pos = (
self.position[0] + dist * np.cos(self.orientation),
self.position[1] - dist * np.sin(self.orientation)
)
return new_pos
37 changes: 37 additions & 0 deletions abm/projects/cooperative_signaling/cs_agent/cs_supcalc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import pygame

from abm.contrib import movement_params

Expand Down Expand Up @@ -38,6 +39,42 @@ def F_reloc_LR(vel_now, V_now, v_desired=None, theta_max=None):
return (v_desired - vel_now), theta



def reflection_from_circular_wall(dx, dy, orientation):
"""
Calculating the reflection of the agent from the circle arena border.
SEE: https://stackoverflow.com/questions/54543170/angle-reflexion-for-bouncing-ball-in-a-circle
:param dx: x coordinate of the agent minus center of the circle
:param dy: y coordinate of the agent minus center of the circle
:param orientation: orientation of the agent
:return: new orientation of the agent
"""
# normal vector of the circle
c_norm = (pygame.math.Vector2(dx, dy)).normalize()
# incident vector: the current direction vector of the bouncing agent
vec_i = pygame.math.Vector2(np.cos(orientation), np.sin(orientation))
# orientation inside the circle
i_orientation = np.pi + np.arctan2(vec_i[1], vec_i[0])

# reflection vector: outgoing direction vector of the bouncing agent
vec_r = vec_i - 2 * c_norm.dot(vec_i) * c_norm
# np.degrees(self.orientation)
new_orientation = np.pi + np.arctan2(vec_r[1], vec_r[0])

# make sure that the new orientation points inside the circle and not too
# flat to the border
if np.abs(new_orientation - i_orientation) > np.pi / 4:
new_orientation = i_orientation
# make sure that the change of the orientation is not too big; this prevents
# the agent from "jumping" over the border and other wierd behavior when the
# agent changes its orientation too ofter
elif np.abs(new_orientation - orientation) > np.pi / 4:
new_orientation = i_orientation

return new_orientation


def phototaxis(meter, prev_meter, prev_theta, taxis_dir,
phototaxis_theta_step):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np
import pytest
from unittest import mock

from abm.projects.cooperative_signaling.cs_agent import cs_supcalc

Expand All @@ -9,11 +10,44 @@ def test_random_walk():
# set random seed
np.random.seed(42)

dvel, dtheta = cs_supcalc.random_walk()

# passing values to override abm.movementparams values read from .env file
dvel, dtheta = cs_supcalc.random_walk(desired_vel=1, exp_theta_min=-0.3, exp_theta_max=0.3)
assert dvel == 1.0
assert dtheta == -0.0752759286915825

# test case: not passing min theta, read from module fixed var
with mock.patch('abm.contrib.movement_params.exp_theta_min', -0.5):
dvel, dtheta = cs_supcalc.random_walk(desired_vel=1, exp_theta_min=None, exp_theta_max=0.3)
assert dvel == 1.0
assert dtheta == 0.26057144512793295

# test case: not passing velocity, read from module fixed var
with mock.patch('abm.contrib.movement_params.exp_vel_max', 3):
dvel, dtheta = cs_supcalc.random_walk(desired_vel=None, exp_theta_min=-0.3, exp_theta_max=0.3)
assert dvel == 3
assert dtheta == 0.13919636508684308


def test_reflection_from_circular_wall():
"""Test reflection_from_circular_wall()"""

new_orientation = cs_supcalc.reflection_from_circular_wall(
0, 1, np.pi / 2)
assert new_orientation == np.pi * 3 / 2

new_orientation = cs_supcalc.reflection_from_circular_wall(
1, 0, np.pi)
assert new_orientation == np.pi * 2

# test very flat reflection angle
orient = np.pi + np.pi / 6
vec_i = [np.cos(orient), np.sin(orient)]
# orientation inside the circle
i_orientation = np.pi + np.arctan2(vec_i[1], vec_i[0])
new_orientation = cs_supcalc.reflection_from_circular_wall(
0, 1, orient)
assert new_orientation == i_orientation


@pytest.mark.parametrize(
"meter, prev_meter, prev_theta, taxis_dir, new_theta, new_taxis_dir",
Expand Down
89 changes: 39 additions & 50 deletions abm/projects/cooperative_signaling/cs_environment/cs_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from abm.contrib import colors
from abm.environment.rescource import Rescource
from abm.projects.cooperative_signaling.cs_agent.cs_supcalc import random_walk
from abm.projects.cooperative_signaling.cs_agent.cs_supcalc import random_walk, \
reflection_from_circular_wall


class CSResource(Rescource):
Expand Down Expand Up @@ -44,59 +45,45 @@ def prove_orientation(self):
self.orientation = self.orientation - 2 * np.pi

def reflect_from_walls(self):
"""reflecting agent from environment boundaries according to a desired
x, y coordinate. If this is over any
boundaries of the environment, the agents position and orientation will
be changed such that the agent is
reflected from these boundaries."""

# Boundary conditions according to center of agent (simple)
"""
Reflecting resource from the circle arena border. Analogous to CSAgent
reflect_from_walls() method.
"""
# x coordinate - x of the center point of the circle
x = self.position[0] + self.radius
c_x = (self.WIDTH / 2 + self.window_pad)
dx = x - c_x
# y coordinate - y of the center point of the circle
y = self.position[1] + self.radius

# Reflection from left wall
if x < self.boundaries_x[0]:
self.position[0] = self.boundaries_x[0] - self.radius

if np.pi / 2 <= self.orientation < np.pi:
self.orientation -= np.pi / 2
elif np.pi <= self.orientation <= 3 * np.pi / 2:
self.orientation += np.pi / 2
self.prove_orientation() # bounding orientation into 0 and 2pi

# Reflection from right wall
if x > self.boundaries_x[1]:

self.position[0] = self.boundaries_x[1] - self.radius - 1

if 3 * np.pi / 2 <= self.orientation < 2 * np.pi:
self.orientation -= np.pi / 2
elif 0 <= self.orientation <= np.pi / 2:
self.orientation += np.pi / 2
self.prove_orientation() # bounding orientation into 0 and 2pi

# Reflection from upper wall
if y < self.boundaries_y[0]:
self.position[1] = self.boundaries_y[0] - self.radius

if np.pi / 2 <= self.orientation <= np.pi:
self.orientation += np.pi / 2
elif 0 <= self.orientation < np.pi / 2:
self.orientation -= np.pi / 2
self.prove_orientation() # bounding orientation into 0 and 2pi

# Reflection from lower wall
if y > self.boundaries_y[1]:
self.position[1] = self.boundaries_y[1] - self.radius - 1
if 3 * np.pi / 2 <= self.orientation <= 2 * np.pi:
self.orientation += np.pi / 2
elif np.pi <= self.orientation < 3 * np.pi / 2:
self.orientation -= np.pi / 2
self.prove_orientation() # bounding orientation into 0 and 2pi

c_y = (self.HEIGHT / 2 + self.window_pad)
dy = y - c_y
# radius of the environment
e_r = self.HEIGHT / 2

# return if the resource has not reached the boarder
if np.linalg.norm([dx, dy]) + self.radius < e_r:
return

# reflect the resource from the boarder
self.orientation = reflection_from_circular_wall(
dx, dy, self.orientation)

# make orientation between 0 and 2pi
self.prove_orientation()

# relocate the resource back inside the circle
relocation = self.velocity
self.position[0] += relocation * np.cos(self.orientation)
self.position[1] -= relocation * np.sin(self.orientation)
self.center = (
self.position[0] + self.radius, self.position[1] + self.radius)

# check if the resource is still outside the circle
diff = [self.center[0] - c_x, self.center[1] - c_y]
if np.linalg.norm(diff) + self.radius >= e_r:
# if yes, relocate it again at the center
self.center = (c_x, c_y)

def update(self):

# applying random movement on resource patch
Expand Down Expand Up @@ -130,6 +117,8 @@ def draw_update(self):
self.mask = pygame.mask.from_surface(self.image)
if self.is_clicked or self.show_stats:
font = pygame.font.Font(None, 18)
text = font.render(f"{self.resc_left:.2f}, Q{self.unit_per_timestep:.2f}", True, colors.BLACK)
text = font.render(
f"{self.resc_left:.2f}, Q{self.unit_per_timestep:.2f}", True,
colors.BLACK)
self.image.blit(text, (0, 0))
text_rect = text.get_rect(center=self.rect.center)
Loading

0 comments on commit 0c687c2

Please sign in to comment.