import base
import pygame
import numpy as np
from raycast import RayCastPlayer
from pygame.constants import K_w, K_a, K_d, K_s
[docs]class RaycastMaze(base.Game, RayCastPlayer):
"""
Parameters
----------
init_pos : tuple of int (default: (1,1))
The position the player starts on in the grid. The grid is zero indexed.
resolution : int (default: 1)
This instructs the Raycast engine on how many vertical lines to use when drawing the screen. The number is equal to the width / resolution.
move_speed : int (default: 20)
How fast the agent moves forwards or backwards.
turn_speed : int (default: 13)
The speed at which the agent turns left or right.
map_size : int (default: 10)
The size of the maze that is generated. Must be greater then 5. Can be incremented to increase difficulty by adjusting the attribute between game resets.
width : int (default: 48)
Screen width.
height : int (default: 48)
Screen height, recommended to be same dimension as width.
"""
def __init__(self,
init_pos=(1,1), resolution=1,
move_speed=20, turn_speed=13,
map_size=10, height=48, width=48):
assert map_size > 5, "map_size must be gte 5"
#do not change
init_dir=(1.0, 0.0)
init_plane=(0.0, 0.66)
block_types = {
0: {
"pass_through": True,
"color": None
},
1: {
"pass_through": False,
"color": (255, 255, 255)
},
2: {
"pass_through": False,
"color": (255, 100, 100)
}
}
actions = {
"forward": K_w,
"left": K_a,
"right": K_d,
"backward": K_s
}
base.Game.__init__(self, width, height, actions=actions)
RayCastPlayer.__init__(self, None,
init_pos, init_dir, width, height, resolution,
move_speed, turn_speed, init_plane, actions, block_types)
self.init_pos = np.array([init_pos], dtype=np.float32)
self.init_dir = np.array([init_dir], dtype=np.float32)
self.init_plane = np.array([init_plane], dtype=np.float32)
self.obj_loc = None
self.map_size = map_size
def _make_maze(self, complexity=0.75, density=0.75):
"""
ty wikipedia?
"""
dim = np.floor( self.map_size / 2 )*2+1
shape = (dim, dim)
complexity = int(complexity*(5*(shape[0]+shape[1])))
density = int(density*(shape[0]//2*shape[1]//2))
# Build actual maze
Z = np.zeros(shape, dtype=bool)
# Fill borders
Z[0,:] = Z[-1,:] = 1
Z[:,0] = Z[:,-1] = 1
# Make isles
for i in range(density):
x = self.rng.random_integers(0,shape[1]//2)*2
y = self.rng.random_integers(0,shape[0]//2)*2
Z[y,x] = 1
for j in range(complexity):
neighbours = []
if x > 1: neighbours.append( (y,x-2) )
if x < shape[1]-2: neighbours.append( (y,x+2) )
if y > 1: neighbours.append( (y-2,x) )
if y < shape[0]-2: neighbours.append( (y+2,x) )
if len(neighbours):
y_,x_ = neighbours[self.rng.random_integers(0,len(neighbours)-1)]
if Z[y_,x_] == 0:
Z[y_,x_] = 1
Z[y_+(y-y_)//2, x_+(x-x_)//2] = 1
x, y = x_, y_
return Z.astype(int)
[docs] def getGameState(self):
"""
Returns
-------
None
Does not have a non-visual representation of game state.
Would be possible to return the location of the maze end.
"""
return None
def getScore(self):
return self.score
def game_over(self):
obj_loc = self.obj_loc + 0.5
dist = np.sqrt(np.sum((self.pos - obj_loc)**2.0))
if dist < 1.0:
self.score += self.rewards["win"]
return True
else:
return False
def init(self):
self.pos = np.copy(self.init_pos)
self.dir = np.copy(self.init_dir)
self.plane = np.copy(self.init_plane)
self.map_ = self._make_maze()
self.obj_loc = self.rng.randint(3, high=self.map_size-1, size=(2))
self.map_[ self.obj_loc[0], self.obj_loc[1] ] = 2
def reset(self):
self.init()
def step(self, dt):
self.screen.fill((0,0,0))
pygame.draw.rect(self.screen, (92,92,92), (0, self.height/2, self.width, self.height))
self.score += self.rewards["tick"]
self._handle_player_events(dt)
c, t, b, col = self.draw()
for i in range(len(c)):
color = (col[i][0], col[i][1], col[i][2])
p0 = (c[i], t[i])
p1 = (c[i], b[i])
pygame.draw.line(self.screen, color, p0, p1, self.resolution)
if __name__ == "__main__":
import numpy as np
fps = 60
pygame.init()
game = RaycastMaze(
height=256,
width=256,
map_size=10
)
game.screen = pygame.display.set_mode(game.getScreenDims(), 0, 32)
game.clock = pygame.time.Clock()
game.rng = np.random.RandomState(24)
game.init()
while True:
dt = game.clock.tick_busy_loop(fps)
if game.game_over():
print "Game over!"
print "Resetting!"
game.reset()
game.step(dt)
pygame.display.update()