Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented perspective view occlusion #52

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9048dbd
Added pickup and drop functions
landrumb Oct 10, 2020
cf19635
added pickup() dispatch for non-item objects
landrumb Oct 10, 2020
a55e8a2
changed to use pickup function
landrumb Oct 10, 2020
a96646a
fixed missed argument change
landrumb Oct 10, 2020
bdc2a92
fixed wrong attribute name
landrumb Oct 10, 2020
4dd1670
fixed bug allowing passage through door w/o key
landrumb Oct 10, 2020
0d6c5a1
replaced NULL with `nothing`
landrumb Oct 11, 2020
922529c
Added trait-based implementation of pickup
landrumb Oct 12, 2020
26b536d
Made pickup and drop functors
landrumb Oct 12, 2020
50fb8ca
Fix error in train implementation
landrumb Oct 12, 2020
a896252
added type annotation for non-item isitem()
landrumb Oct 12, 2020
e511da0
made key annotation in isitem trait cleaner
landrumb Oct 12, 2020
849d537
moved drop functor definition to pickup definition
landrumb Oct 13, 2020
c9bc6e0
refactored item to transportable
landrumb Oct 13, 2020
a774ece
added array_agent
landrumb Oct 13, 2020
8fe6829
implemented pickup and drop for array_agent
landrumb Oct 13, 2020
1b3efdc
updated export statements
landrumb Oct 13, 2020
89759ae
Revert "updated export statements"
landrumb Oct 13, 2020
0fc0d26
Merge branch 'master' into landrumb-issue-36
landrumb Oct 13, 2020
a8dfb6d
Implemented Agent as a parametric struct
landrumb Oct 13, 2020
c2daeac
slightly polish
findmyway Oct 13, 2020
9c11a9c
fix DoorKey
findmyway Oct 13, 2020
43f4d7f
Merge pull request #1 from JuliaReinforcementLearning/master
landrumb Oct 13, 2020
6889205
Merge remote-tracking branch 'upstream/master'
landrumb Oct 15, 2020
08c42e3
Merge branch 'master' of https://github.com/landrumb/GridWorlds.jl
landrumb Oct 15, 2020
891e588
Added backend for perspective occlusion
landrumb Oct 16, 2020
8f47a52
added light docstring for shadow functor
landrumb Oct 16, 2020
08e48fa
Added very light test
landrumb Oct 16, 2020
501bca0
Implemented opacity trait
landrumb Oct 18, 2020
a4292af
added light docstring for get_agent_view!()
landrumb Oct 19, 2020
f2f5c11
Added w to arguments of get_agent_view and simplified
landrumb Oct 19, 2020
e71ea87
implemented occlusion to get_agent_view
landrumb Oct 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/abstract_grid_world.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,3 @@ get_actions(w::AbstractGridWorld) = (MOVE_FORWARD, TURN_LEFT, TURN_RIGHT)

get_agent_view_inds(w::AbstractGridWorld, s=(7,7)) = get_agent_view_inds(get_agent_pos(w).I, s, get_agent_dir(w))

get_agent_view!(v::BitArray{3}, w::AbstractGridWorld) = get_agent_view!(v, convert(GridWorldBase, w), get_agent_pos(w), get_agent_dir(w))

92 changes: 86 additions & 6 deletions src/grid_world_base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,54 @@ function Random.rand(f::Function, w::GridWorldBase; max_try=typemax(Int), rng=Ra
return nothing
end

#####
# Occlusion
#####

radius(x, y) = √(x^2 + y^2)
radius(p::Tuple) = radius(p[1], p[2])
theta(x, y) = x == 0 ? sign(y)*π/2 : atan(y, x)
theta(p::Tuple) = theta(p[1], p[2])

struct PolarCoord
θ::AbstractFloat
r::AbstractFloat
end
PolarCoord(θ::Real, r::Real) = PolarCoord((θ + (r<0 && π)) % 2π, abs(r))
PolarCoord(p::Tuple) = PolarCoord(theta(p), radius(p))
PolarCoord(p::CartesianIndex) = PolarCoord(Tuple(p))

struct Shadow
minθ::AbstractFloat
maxθ::AbstractFloat
r::AbstractFloat
end
function Shadow(p::CartesianIndex)
r = radius(Tuple(p))
corners = [(p[1]+x, p[2]+y) for x in -.5:.5, y in -.5:.5]
corners = theta.(corners)
Shadow(minimum(corners), maximum(corners), r)
end

"""
returns a 2D array of boolean values, where `true` represents an index which is
occluded by the shadow `s` evaluating `v`
"""
function (s::Shadow)(v::CartesianIndices)
polar = PolarCoord.(v)``
f(x) = x.r > s.r && s.minθ <= x.θ <= s.maxθ
f.(polar)
end

function shadowmap(m::Matrix{Bool})
indices = CartesianIndices(((-1*size(m)[1]÷2):(size(m)[1]÷2), 0:(size(m)[2]-1)))
shadows = Shadow.(indices[m])
output = fill(false, size(a))
for s in shadows
output |= s(indices)
end
output
end
#####
# get_agent_view
#####
Expand All @@ -74,14 +122,46 @@ ind_map((i,j), (m, n), ::Right) = (j, n-i+1)
ind_map((i,j), (m, n), ::Up) = (m-i+1, n-j+1)
ind_map((i,j), (m, n), ::Down) = (i,j)

function get_agent_view!(v::AbstractArray{Bool,3}, a::AbstractArray{Bool,3}, p::CartesianIndex, dir::LRUD)
"""
Updates the agent view

Args:
v::AbstractArray{Bool,3}: the current agent view as a 3D array with indices [type, x, y]
a::AbstractArray{Bool,3}: the current environment as a 3D array with indices [type, x, y]
p::CartesianIndex: location of the agent
dir::LRUD: direction the agent is looking
"""
function get_agent_view!(v::AbstractArray{Bool,3}, w::AbstractGridWorld; perspective::Bool=true)
a = convert(GridWorldBase, w)
p = get_agent_pos(w)
dir = get_agent_dir(w)

view_size = (size(v, 2), size(v, 3))
grid_size = (size(a,2),size(a,3))
inds = get_agent_view_inds(p.I, view_size, dir)
valid_inds = CartesianIndices(grid_size)
for ind in CartesianIndices(inds)
if inds[ind] ∈ valid_inds
v[:, ind_map(ind.I, view_size, dir)...] .= a[:, inds[ind]]
inds = get_agent_view_inds(p.I, view_size, dir) # indices of the visible points
valid_inds = CartesianIndices(grid_size) # CartesianIndices representing the whole environment

if perspective
m = convert(Matrix{Bool}, copy(v))
shadows = []
for ind in CartesianIndices(inds) # for every index in view...
if inds[ind] ∈ valid_inds # if it's within the environment...
o = findfirst(w[:, inds[ind]]) # first object at the index
m[ind_map(ind.I, view_size, dir)...] .= isnothing(o) ? false : isopaque(w.objects[o]) <: Opaque
end
end
shadows = .!(shadowmap(m))
for ind in CartesianIndices(inds)
if inds[ind] ∈ valid_inds && shadows[ind_map(ind.I, view_size, dir)...]
v[:, ind_map(ind.I, view_size, dir)...] .= a[:, inds[ind]]
end
end
else
for ind in CartesianIndices(inds) # for every index in view...
if inds[ind] ∈ valid_inds # if it's within the environment...
# set its corresponding value in the view to the value in the environment
v[:, ind_map(ind.I, view_size, dir)...] .= a[:, inds[ind]]
end
end
end
v
Expand Down
16 changes: 14 additions & 2 deletions src/objects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ const GOAL = Goal()
Base.convert(::Type{Char}, ::Goal) = '♥'
get_color(::Goal) = :red

struct Door{C} <: AbstractObject end
Door(c) = Door{c}()
struct Door{C} <: AbstractObject
open::Bool
end
Door(c) = Door{c}(false)
Base.convert(::Type{Char}, ::Door) = '⩎'
get_color(::Door{C}) where C = C

Expand Down Expand Up @@ -84,6 +86,16 @@ istransportable(::Type{<:Key}) = TRANSPORTABLE
istransportable(::Type{Gem}) = TRANSPORTABLE
istransportable(x::AbstractObject) = istransportable(typeof(x))

struct Opaque end
struct Transparent end
const OPAQUE = Opaque()
const TRANSPARENT = Transparent()
isopaque(::Type{<:AbstractObject}) = OPAQUE
isopaque(::Type{<:Key}) = TRANSPARENT
isopaque(::Type{Gem}) = TRANSPARENT
isopaque(x::AbstractObject) = isopaque(typeof(x))
isopaque(x<:Door) = x.open ? TRANSPARENT : OPAQUE

(x::Pickup)(a::Agent, o) = x(istransportable(o), a, o)

function (::Pickup)(::Transportable, a::Agent, o::AbstractObject)
Expand Down
17 changes: 17 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,21 @@ ACTIONS = [TURN_LEFT, TURN_RIGHT, MOVE_FORWARD]
end
end
end
@testset "grid_world_base.jl" begin
grid = CartesianIndices((-3:3, 0:6))
soln =
[[1,1,1,1,1,1,1]
[1,1,1,1,1,1,1]
[1,1,1,1,1,1,1]
[1,1,1,1,1,1,1]
[1,1,1,1,0,0,1]
[0,0,1,1,0,0,1]
[0,1,1,1,0,0,0]]

s = GridWorlds.Shadow(CartesianIndex((1,3)))
println("minθ:$(s.minθ)|maxθ:$(s.maxθ)|r:$(s.r)")
println(s(grid))
println("($(GridWorlds.theta(3,4)), $(GridWorlds.radius(3,4)))")
println(s(CartesianIndices((3:3, 5:5))))
end
end