diff --git a/ple/games/flappybird/__init__.py b/ple/games/flappybird/__init__.py index e24cfbd..b2b5f50 100644 --- a/ple/games/flappybird/__init__.py +++ b/ple/games/flappybird/__init__.py @@ -113,6 +113,7 @@ def __init__(self, self.image.set_colorkey((0, 0, 0)) self.init(gap_start, gap_size, offset, color) + assert gap_size >=30, "Gap size not big enough!" #check if the bird can't fly through the pipes. Update to adjust after testing if needed def init(self, gap_start, gap_size, offset, color): self.image.fill((0, 0, 0)) diff --git a/ple/games/pixelcopter.py b/ple/games/pixelcopter.py index 773bb11..5d83b2a 100644 --- a/ple/games/pixelcopter.py +++ b/ple/games/pixelcopter.py @@ -135,6 +135,9 @@ class Pixelcopter(PyGameWrapper): """ def __init__(self, width=48, height=48): + assert 00: + y_pos+=int(self.height * 0.10) + y_pos+=int(self.height * 0.50) + self.block_group.add( Block( (x_pos, y_pos), diff --git a/ple/games/pong.py b/ple/games/pong.py index 40b357b..4177129 100644 --- a/ple/games/pong.py +++ b/ple/games/pong.py @@ -1,7 +1,7 @@ import math import sys -import pygame +import pygame, pygame.sprite from pygame.constants import K_w, K_s from ple.games.utils.vec2d import vec2d from ple.games.utils import percent_round_int @@ -42,18 +42,6 @@ def __init__(self, radius, speed, rng, self.rect = self.image.get_rect() self.rect.center = pos_init - def line_intersection(self, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y): - - s1_x = p1_x - p0_x - s1_y = p1_y - p0_y - s2_x = p3_x - p2_x - s2_y = p3_y - p2_y - - s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y) - t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y) - - return (s >= 0 and s <= 1 and t >= 0 and t <= 1) - def update(self, agentPlayer, cpuPlayer, dt): self.pos.x += self.vel.x * dt @@ -61,21 +49,21 @@ def update(self, agentPlayer, cpuPlayer, dt): is_pad_hit = False - if self.pos.x <= agentPlayer.pos.x + agentPlayer.rect_width: - if self.line_intersection(self.pos_before.x, self.pos_before.y, self.pos.x, self.pos.y, agentPlayer.pos.x + agentPlayer.rect_width / 2, agentPlayer.pos.y - agentPlayer.rect_height / 2, agentPlayer.pos.x + agentPlayer.rect_width / 2, agentPlayer.pos.y + agentPlayer.rect_height / 2): - self.pos.x = max(0, self.pos.x) - self.vel.x = -1 * (self.vel.x + self.speed * 0.05) - self.vel.y += agentPlayer.vel.y * 2.0 - self.pos.x += self.radius - is_pad_hit = True - - if self.pos.x >= cpuPlayer.pos.x - cpuPlayer.rect_width: - if self.line_intersection(self.pos_before.x, self.pos_before.y, self.pos.x, self.pos.y, cpuPlayer.pos.x - cpuPlayer.rect_width / 2, cpuPlayer.pos.y - cpuPlayer.rect_height / 2, cpuPlayer.pos.x - cpuPlayer.rect_width / 2, cpuPlayer.pos.y + cpuPlayer.rect_height / 2): - self.pos.x = min(self.SCREEN_WIDTH, self.pos.x) - self.vel.x = -1 * (self.vel.x + self.speed * 0.05) - self.vel.y += cpuPlayer.vel.y * 0.006 - self.pos.x -= self.radius - is_pad_hit = True + #Using pygame collision detection, we can pass in the objects directly, first the player + if pygame.sprite.collide_rect(self, agentPlayer): + self.pos.x = max(0, self.pos.x) + self.vel.x = -1 * (self.vel.x + self.speed * 0.05) + self.vel.y += agentPlayer.vel.y * 2.0 + self.pos.x += self.radius + is_pad_hit = True + + #then we test the cpu, much cleaner! + if pygame.sprite.collide_rect(self, cpuPlayer): + self.pos.x = min(self.SCREEN_WIDTH, self.pos.x) + self.vel.x = -1 * (self.vel.x + self.speed * 0.05) + self.vel.y += cpuPlayer.vel.y * 0.006 + self.pos.x -= self.radius + is_pad_hit = True # Little randomness in order not to stuck in a static loop if is_pad_hit: @@ -196,6 +184,13 @@ class Pong(PyGameWrapper): def __init__(self, width=64, height=48, cpu_speed_ratio=0.6, players_speed_ratio = 0.4, ball_speed_ratio=0.75, MAX_SCORE=11): + assert width > 0, "Error: width must be greater than 0" + assert height > 0, "Error: height must be greater than 0" + assert cpu_speed_ratio > 0, "Error: cpu_speed_ratio must be greater than 0" + assert players_speed_ratio > 0, "Error: player_speed_ratio must be greater than 0" + assert ball_speed_ratio > 0, "Error: ball_speed_ration must be greater than 0" + assert MAX_SCORE > 0, "Error: MAX_SCORE must be greater than 0" + actions = { "up": K_w, "down": K_s diff --git a/ple/games/puckworld.py b/ple/games/puckworld.py index 621e203..7d5a66a 100644 --- a/ple/games/puckworld.py +++ b/ple/games/puckworld.py @@ -75,6 +75,9 @@ def __init__(self, width=64, height=64): + assert 0 5, "map_size must be gte 5" + for x in init_pos: + assert (x >= 0) and (x < map_size), "Error: init_pos must be on the map (0 to map_size)" + assert resolution > 0, "Error: resolution must be greater than 0" + assert move_speed > 0, "Error: move_speed must be greater than 0" + assert move_speed < 100, "Error: move_speed must be less than 100" + assert turn_speed > 0, "Error: turn_speed must be greater than 0" + assert turn_speed < 100, "Error: turn_speed must be less than 100" + assert (map_size > 5) and (map_size <= 100), "Error: map_size must be >= 5 and <= 100" + assert (height > 0) and (width > 0) and (height == width), "Error: height and width must be equal and greater than 0" # do not change init_dir = (1.0, 0.0) diff --git a/ple/ple.py b/ple/ple.py index 6f2e2b7..5bc793f 100644 --- a/ple/ple.py +++ b/ple/ple.py @@ -1,7 +1,7 @@ import numpy as np from PIL import Image # pillow import sys - +from time import sleep import pygame from .games.base.pygamewrapper import PyGameWrapper @@ -373,6 +373,8 @@ def act(self, action): Returns the reward that the agent has accumlated while performing the action. """ + if self.display_screen: + sleep(1/float(self.fps)) return sum(self._oneStepAct(action) for i in range(self.frame_skip)) def _draw_frame(self): diff --git a/requirements-dev.txt b/requirements-dev.txt index 6a7c32f..ed17187 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ mock==1.0.1 numpydoc Sphinx==1.2.3 -sphinx_rtd_theme +sphinx_rtd_theme \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..231dd17 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pygame \ No newline at end of file diff --git a/setup.py b/setup.py index 5209e97..81c19a9 100644 --- a/setup.py +++ b/setup.py @@ -11,9 +11,9 @@ ] setup( - name='ple', - version='0.0.1', - description='PyGame Learning Environment', + name='ple', + version='0.0.1', + description='PyGame Learning Environment', classifiers=[ "Intended Audience :: Developers", "Intended Audience :: Science/Research", @@ -21,13 +21,13 @@ "Programming Language :: Python :: 2.7", "Topic :: Scientific/Engineering :: Artificial Intelligence", ], - url='https://github.com/ntasfi/PyGame-Learning-Environment', - author='Norman Tasfi', - author_email='first letter of first name plus last at googles email service.', - keywords='', - license="MIT", - packages=find_packages(), - include_package_data=False, - zip_safe=False, - install_requires=install_requires + url='https://github.com/ntasfi/PyGame-Learning-Environment', + author='Norman Tasfi', + author_email='first letter of first name plus last at googles email service.', + keywords='', + license="MIT", + packages=find_packages(), + include_package_data=False, + zip_safe=False, + install_requires=install_requires ) diff --git a/test.py b/test.py new file mode 100644 index 0000000..461c626 --- /dev/null +++ b/test.py @@ -0,0 +1,34 @@ +from ple import PLE +from ple.games.pong import Pong +import pygame +import time +import sys + +game = Pong(width=300, height=200) + +p = PLE(game, fps=30, display_screen=True, force_fps=True) +p.init() + +print(p.getActionSet()) + +nb_frames = 1000 +action = None + +for f in range(nb_frames): + if p.game_over(): + p.reset_game() + obs = p.getScreenRGB() + events = pygame.event.get() + for event in events: + if event.type == pygame.QUIT: + sys.exit() + elif event.type == pygame.KEYDOWN: + if event.key: + action = event.key + print(action) + elif event.type == pygame.KEYUP: + action = None + p.act(action) + time.sleep(.05) + +#This is a change \ No newline at end of file diff --git a/tests/test_pong.py b/tests/test_pong.py new file mode 100644 index 0000000..3e095c1 --- /dev/null +++ b/tests/test_pong.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +from ple import PLE +from ple.games.pong import Pong, Ball, Player +import pygame, time, sys, pytest + +#This test passes if a positive difference in movement speed is detected +def test_movement_up(): + game=Pong() + p=PLE(game, display_screen=True, fps=20, force_fps=1) + p.init() + time.sleep(.5) + oldState=p.getGameState() + p.act(game.actions["up"]) + newState=p.getGameState() + assert oldState["player_velocity"] > newState["player_velocity"] + +#This tests passes is a negative difference in movement speed is detected +def test_movement_down(): + game=Pong() + p=PLE(game, display_screen=True, fps=20, force_fps=1) + p.init() + time.sleep(.5) + oldState=p.getGameState() + p.act(game.actions["down"]) + newState=p.getGameState() + assert oldState["player_velocity"] < newState["player_velocity"] + +#This test passes if an exception is thrown when a negative cpu speed is specified +def test_negative_cpu_speed(): + with pytest.raises(Exception): + game=Pong(cpu_speed_ratio=-1) + +#This test passes if an exception is thrown when a negative player speed is specified +def test_negative_player_speed(): + with pytest.raises(Exception): + game=Pong(players_speed_ratio=-1) + +#This test passes if an exception is thrown when a negative ball speed is specified +def test_negative_ball_speed(): + with pytest.raises(Exception): + game=Pong(ball_speed_ratio=-1) + +#This test passes if an exception is thrown when a negative game size is specified +def test_invalid_game_size(): + with pytest.raises(Exception): + game=Pong(width=-200, height=-200) + +#This test passes if an exception is thrown when a negative max score is specified +def test_invalid_max_score(): + with pytest.raises(Exception): + game=Pong(MAX_SCORE=-1) \ No newline at end of file diff --git a/tests/test_raycastmaze.py b/tests/test_raycastmaze.py new file mode 100644 index 0000000..1dc2f79 --- /dev/null +++ b/tests/test_raycastmaze.py @@ -0,0 +1,39 @@ +#!/usr/bin/python +from ple import PLE +from ple.games.raycastmaze import RaycastMaze +import pygame, time, sys, pytest + +#This test passes if an exception is thrown when a negative initial position is specified +def test_oob_init_pos(): + with pytest.raises(Exception): + game=RaycastMaze(init_pos=(-1,-1)) + +#This test passes if an exception is thrown when the map size is below the minimum for the algorithm (5) +def test_below_min_map_size(): + with pytest.raises(Exception): + game=RaycastMaze(map_size=3) + +#This test passes if an exception is thrown when a large map size is specified +def test_beyond_max_map_size(): + with pytest.raises(Exception): + game=RaycastMaze(map_size=400) + +#This test passes if an exception is thrown when a negative move speed is specified +def test_negative_move_speed(): + with pytest.raises(Exception): + game=RaycastMaze(move_speed=-1) + +#This test passes if an exception is thrown when a negative turn speed is specified +def test_negative_turn_speed(): + with pytest.raises(Exception): + game=RaycastMaze(turn_speed=-1) + +#This test passes if an exception is thrown when a non-usefully large turn speed is specified +def test_beyond_max_turn_speed(): + with pytest.raises(Exception): + game=RaycastMaze(turn_speed=400) + +#This test passes if an exception is thrown when a negative window size is specified +def test_negative_game_size(): + with pytest.raises(Exception): + game=RaycastMaze(width=-100,height=-100)