Skip to content

Commit e630024

Browse files
authored
revamp playing and replaying (#175)
* remove Play module & move its contents to GW module * don't forward get_action_names for RLBaseEnv * revamp play.jl * update forwarding of play! method for RLBaseEnv
1 parent f7b8632 commit e630024

File tree

3 files changed

+88
-74
lines changed

3 files changed

+88
-74
lines changed

benchmark/benchmark.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function benchmark(Env, num_resets, steps_per_reset)
2828
benchmark[:reset] = BT.@benchmark RLBase.reset!($(Ref(env))[])
2929
benchmark[:state] = BT.@benchmark RLBase.state($(Ref(env))[])
3030

31-
action_names = GW.get_action_names(env)
31+
action_names = GW.get_action_names(env.env)
3232

3333
for action in RLBase.action_space(env)
3434
benchmark[action_names[action]] = BT.@benchmark $(Ref(env))[]($(Ref(action))[])

src/play.jl

Lines changed: 84 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
module Play
2-
3-
import ..GridWorlds as GW
4-
import REPL
5-
61
const ESC = Char(0x1B)
72
const HIDE_CURSOR = ESC * "[?25l"
83
const SHOW_CURSOR = ESC * "[?25h"
@@ -19,30 +14,93 @@ close_maybe(io::Nothing) = nothing
1914

2015
write_maybe(io::IO, content) = write(io, content)
2116
write_maybe(io::Nothing, content) = 0
22-
write_io1_maybe_io2(io1::IO, io2::Union{Nothing, IO}, content) = write(io1, content) + write_maybe(io2, content)
17+
write_maybe(io1, io2, content) = write_maybe(io1, content) + write_maybe(io2, content)
18+
19+
function play!(terminal::REPL.Terminals.UnixTerminal, env::AbstractGridWorld, file_name::Union{Nothing, AbstractString}, frame_start_delimiter::Union{Nothing, AbstractString})
20+
if !isnothing(file_name) && isnothing(frame_start_delimiter)
21+
error("Please additionally specify a string for the keyword argument `frame_start_delimiter`, for example `frame_start_delimiter = \"FRAME_START_DELIMITER\"`. This is needed when you want to replay the recording.")
22+
end
23+
if isnothing(file_name) && !isnothing(frame_start_delimiter)
24+
error("Please additionally specify a string for the keyword argument `file_name`, for example `file_name = \"recording.txt\"`. This is needed to save the recording in a text file and replay it later.")
25+
end
26+
27+
terminal_out = terminal.out_stream
28+
terminal_in = terminal.in_stream
29+
file = open_maybe(file_name)
30+
31+
action_keys = get_action_keys(env)
32+
key_bindings = "Key bindings to play: 'q': quit, 'r': GW.reset!, $(action_keys): GW.act!"
33+
34+
char = nothing
35+
36+
write(terminal_out, CLEAR_SCREEN)
37+
write(terminal_out, MOVE_CURSOR_TO_ORIGIN)
38+
write(terminal_out, HIDE_CURSOR)
39+
40+
REPL.Terminals.raw!(terminal, true)
41+
42+
try
43+
while true
44+
write_maybe(file, frame_start_delimiter)
45+
46+
frame = key_bindings
47+
frame = frame * "\n" * "Last play character read: $(char)"
48+
frame = frame * "\n" * repr(MIME"text/plain"(), env)
49+
50+
write_maybe(terminal_out, file, frame)
51+
52+
char = read(terminal_in, Char)
53+
54+
if char == 'q'
55+
write(terminal_out, SHOW_CURSOR)
56+
close_maybe(file)
57+
REPL.Terminals.raw!(terminal, false)
58+
return nothing
59+
elseif char == 'r'
60+
reset!(env)
61+
elseif char in action_keys
62+
act!(env, findfirst(==(char), action_keys))
63+
end
2364

24-
show_maybe(io::IO, mime::MIME, content) = show(io, mime, content)
25-
show_maybe(io::Nothing, mime::MIME, content) = nothing
26-
function show_io1_maybe_io2(io1::IO, io2::Union{Nothing, IO}, mime::MIME, content)
27-
show(io1, mime, content)
28-
show_maybe(io2, mime, content)
65+
write(terminal_out, EMPTY_SCREEN)
66+
end
67+
finally
68+
write(terminal_out, SHOW_CURSOR)
69+
close_maybe(file)
70+
REPL.Terminals.raw!(terminal, false)
71+
end
72+
73+
return nothing
2974
end
3075

31-
function replay(terminal::REPL.Terminals.UnixTerminal, file_name::AbstractString; frame_rate = Union{Nothing, Real} = nothing)
76+
play!(env::AbstractGridWorld; file_name = nothing, frame_start_delimiter = nothing) = play!(REPL.TerminalMenus.terminal, env, file_name, frame_start_delimiter)
77+
78+
function replay(terminal::REPL.Terminals.UnixTerminal, file_name::AbstractString, frame_start_delimiter::AbstractString, frame_rate = Union{Nothing, Real})
3279
terminal_out = terminal.out_stream
33-
delimiter = EMPTY_SCREEN
34-
frames = split(read(file_name, String), delimiter)
80+
strings = split(read(file_name, String), frame_start_delimiter)
81+
frames = @view strings[2:end]
3582
num_frames = length(frames)
3683

84+
write(terminal_out, CLEAR_SCREEN)
85+
write(terminal_out, MOVE_CURSOR_TO_ORIGIN)
86+
write(terminal_out, HIDE_CURSOR)
87+
3788
if isnothing(frame_rate)
3889
terminal_in = terminal.in_stream
39-
REPL.Terminals.raw!(terminal, true)
90+
replay_key_bindings = "Key bindings to replay: 'q': quit, 'f': first frame, 'n': next frame, 'p': previous frame"
4091
current_frame = 1
92+
char = nothing
93+
94+
REPL.Terminals.raw!(terminal, true)
95+
4196
try
4297
while true
43-
write(terminal_out, frames[current_frame])
44-
println(terminal_out)
45-
println(terminal_out, "replay key bindings: 'q': quit, 'f': go to first frame, 'n': go to next frame, 'p': go to previous frame")
98+
replay_frame = replay_key_bindings
99+
replay_frame = replay_frame * "\n" * "Last replay character read: $(char)"
100+
replay_frame = replay_frame * "\n" * "------------FRAME_START------------"
101+
replay_frame = replay_frame * "\n" * frames[current_frame]
102+
103+
write(terminal_out, replay_frame)
46104

47105
char = read(terminal_in, Char)
48106

@@ -66,63 +124,19 @@ function replay(terminal::REPL.Terminals.UnixTerminal, file_name::AbstractString
66124
return nothing
67125
end
68126
else
127+
write(terminal_out, CLEAR_SCREEN)
128+
write(terminal_out, MOVE_CURSOR_TO_ORIGIN)
129+
write(terminal_out, HIDE_CURSOR)
130+
69131
for frame in frames
70132
write(terminal_out, frame)
71133
sleep(1 / frame_rate)
72-
write(terminal_out, delimiter)
134+
write(terminal_out, EMPTY_SCREEN)
73135
end
74-
end
75-
76-
return nothing
77-
end
78-
79-
replay(file_name; frame_rate = nothing) = replay(REPL.TerminalMenus.terminal, file_name, frame_rate = frame_rate)
80136

81-
function play!(terminal::REPL.Terminals.UnixTerminal, env::GW.AbstractGridWorld; file_name::Union{Nothing, AbstractString} = nothing)
82-
REPL.Terminals.raw!(terminal, true)
83-
84-
terminal_out = terminal.out_stream
85-
terminal_in = terminal.in_stream
86-
file = Play.open_maybe(file_name)
87-
88-
write_io1_maybe_io2(terminal_out, file, CLEAR_SCREEN)
89-
write_io1_maybe_io2(terminal_out, file, MOVE_CURSOR_TO_ORIGIN)
90-
write_io1_maybe_io2(terminal_out, file, HIDE_CURSOR)
91-
92-
action_keys = GW.get_action_keys(env)
93-
action_names = GW.get_action_names(env)
94-
key_bindings = "play key bindings: 'q': quit, 'r': reset!, $(action_keys): $(action_names)\n"
95-
96-
try
97-
while true
98-
write_io1_maybe_io2(terminal_out, file, key_bindings)
99-
show_io1_maybe_io2(terminal_out, file, MIME"text/plain"(), env)
100-
101-
char = read(terminal_in, Char)
102-
103-
if char == 'q'
104-
write_io1_maybe_io2(terminal_out, file, SHOW_CURSOR)
105-
close_maybe(file)
106-
REPL.Terminals.raw!(terminal, false)
107-
return nothing
108-
elseif char == 'r'
109-
GW.reset!(env)
110-
elseif char in action_keys
111-
GW.act!(env, findfirst(==(char), action_keys))
112-
end
113-
114-
write_io1_maybe_io2(terminal_out, file, EMPTY_SCREEN)
115-
write_io1_maybe_io2(terminal_out, file, "Last character: $(char)\n")
116-
end
117-
finally
118-
write_io1_maybe_io2(terminal_out, file, SHOW_CURSOR)
119-
close_maybe(file)
120-
REPL.Terminals.raw!(terminal, false)
137+
write(terminal_out, SHOW_CURSOR)
138+
return nothing
121139
end
122-
123-
return nothing
124140
end
125141

126-
play!(env::GW.AbstractGridWorld; file_name = nothing) = play!(REPL.TerminalMenus.terminal, env, file_name = file_name)
127-
128-
end # module
142+
replay(; file_name, frame_start_delimiter, frame_rate = nothing) = replay(REPL.TerminalMenus.terminal, file_name, frame_start_delimiter, frame_rate)

src/rlbase.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ struct RLBaseEnv{E} <: RLBase.AbstractEnv
33
end
44

55
Base.show(io::IO, mime::MIME"text/plain", env::RLBaseEnv{E}) where {E <: AbstractGridWorld} = show(io, mime, env.env)
6-
Play.play!(terminal::REPL.Terminals.UnixTerminal, env::RLBaseEnv{E}; file_name::Union{Nothing, AbstractString} = nothing) where {E <: AbstractGridWorld} = Play.play!(terminal, env.env, file_name = file_name)
7-
Play.play!(env::RLBaseEnv{E}; file_name = nothing) where {E <: AbstractGridWorld} = Play.play!(REPL.TerminalMenus.terminal, env.env, file_name = file_name)
8-
get_action_names(env::RLBaseEnv{E}) where {E <: AbstractGridWorld} = get_action_names(env.env)
6+
7+
play!(terminal::REPL.Terminals.UnixTerminal, env::RLBaseEnv{E}, file_name::Union{Nothing, AbstractString}, frame_start_delimiter::Union{Nothing, AbstractString}) where {E <: AbstractGridWorld} = play!(terminal, env.env, file_name, frame_start_delimiter)
8+
play!(env::RLBaseEnv{E}; file_name = nothing, frame_start_delimiter = nothing) where {E <: AbstractGridWorld} = play!(REPL.TerminalMenus.terminal, env.env, file_name, frame_start_delimiter)

0 commit comments

Comments
 (0)