From 8bd93802b174783b2562590ae943bca7aa256de8 Mon Sep 17 00:00:00 2001 From: Nils Marten Mikk Date: Sat, 25 May 2024 15:20:54 +0200 Subject: [PATCH 1/6] Refactor MineRL/Probe (WIP) --- src/HerbSearch.jl | 11 +- src/minecraft/create_minerl_env.jl | 69 --------- src/minecraft/getting_started_minerl.jl | 75 ++------- src/minecraft/minerl.jl | 181 ++++++++++++++++++++++ src/probe/guided_search_iterator.jl | 2 +- src/probe/guided_trace_search_iterator.jl | 7 +- src/probe/new_program_iterator.jl | 6 +- src/probe/probe_iterator.jl | 58 +------ src/probe/program_cache.jl | 28 ++++ src/probe/select_partial_sols.jl | 23 ++- src/probe/update_grammar.jl | 3 +- 11 files changed, 254 insertions(+), 209 deletions(-) delete mode 100644 src/minecraft/create_minerl_env.jl create mode 100644 src/minecraft/minerl.jl create mode 100644 src/probe/program_cache.jl diff --git a/src/HerbSearch.jl b/src/HerbSearch.jl index ed364e53..cad0fb76 100644 --- a/src/HerbSearch.jl +++ b/src/HerbSearch.jl @@ -37,6 +37,14 @@ include("genetic_functions/select_parents.jl") include("genetic_search_iterator.jl") include("random_iterator.jl") + +include("probe/program_cache.jl") +include("probe/sum_iterator.jl") +include("probe/new_program_iterator.jl") +include("probe/select_partial_sols.jl") +include("probe/update_grammar.jl") +include("probe/guided_search_iterator.jl") +include("probe/guided_trace_search_iterator.jl") include("probe/probe_iterator.jl") export @@ -71,9 +79,8 @@ export VLSNSearchIterator, SASearchIterator, - ProbeSearchIterator, - GuidedSearchIterator, GuidedSearchIterator, + GuidedSearchTraceIterator, probe, mean_squared_error, diff --git a/src/minecraft/create_minerl_env.jl b/src/minecraft/create_minerl_env.jl deleted file mode 100644 index 087f14cb..00000000 --- a/src/minecraft/create_minerl_env.jl +++ /dev/null @@ -1,69 +0,0 @@ -using PyCall -pyimport("minerl") - -gym = pyimport("gym") -# our_seed = 95812 <- hard env -# our_seed = 958122 -our_seed = 958129 # initial env - -""" - resetEnv() -This function resets the enviornment with the global provided seed and saves the initial X,Y,Z positions. -""" -function resetEnv() - env.seed(our_seed) - obs = env.reset() - global x_player_start, y_player_start, z_player_start = get_xyz_from_env(obs) - print(x_player_start, y_player_start, z_player_start) - - action = env.action_space.noop() - action["forward"] = 1 - - # infinite health - env.set_next_chat_message("/effect @a minecraft:instant_health 1000000 100 true") - env.step(action) - - # infinite food - env.set_next_chat_message("/effect @a minecraft:saturation 1000000 255 true") - env.step(action) - - # disable mobs - env.set_next_chat_message("/gamerule doMobSpawning false") - env.step(action) - env.set_next_chat_message("/kill @e[type=!player]") - env.step(action) - - printstyled("Environment created. x: $x_player_start, y: $y_player_start, z: $z_player_start\n", color=:green) -end - -function set_start_xyz(x, y, z) - global x_player_start = x - global y_player_start = y - global z_player_start = z - println("New x: $x_player_start y: $y_player_start z: $z_player_start pos") -end -function get_xyz_from_env(obs) - return obs["xpos"][1], obs["ypos"][1], obs["zpos"][1] -end - -function resetPosition() - action = env.action_space.noop() - env.set_next_chat_message("/tp @a $(x_player_start) $(y_player_start) $(z_player_start)") - - obs = env.step(action)[1] - obsx, obsy, obsz = get_xyz_from_env(obs) - while obsx != x_player_start || obsy != y_player_start || obsz != z_player_start - obs = env.step(action)[1] - obsx, obsy, obsz = get_xyz_from_env(obs) - end - println((obsx, obsy, obsz)) -end - -if !@isdefined env - printstyled("Creating environment\n", color=:yellow) - env = gym.make("MineRLNavigateDenseProgSynth-v0") - resetEnv() - printstyled("Environment created. x: $x_player_start, y: $y_player_start, z: $z_player_start\n", color=:green) -else - printstyled("Environment already created\n", color=:green) -end \ No newline at end of file diff --git a/src/minecraft/getting_started_minerl.jl b/src/minecraft/getting_started_minerl.jl index 41361636..9104479c 100644 --- a/src/minecraft/getting_started_minerl.jl +++ b/src/minecraft/getting_started_minerl.jl @@ -1,82 +1,25 @@ -include("create_minerl_env.jl") -using HerbGrammar, HerbSpecification -using HerbSearch +using HerbGrammar, HerbSpecification, HerbSearch using Logging disable_logging(LogLevel(1)) -minerl_grammar = @pcsgrammar begin - 1:action_name = "forward" - 1:action_name = "left" - 1:action_name = "right" - 1:action_name = "back" - 1:action_name = "jump" - 1:sequence_actions = [sequence_actions; action] - 1:sequence_actions = [] - 1:action = (TIMES, Dict("camera" => [0, 0], action_name => 1)) - 5:TIMES = 1 | 5 | 25 | 50 | 75 | 100 -end +include("minerl.jl") + +SEED = 958129 -minerl_grammar_2 = @pcsgrammar begin +minerl_grammar = @pcsgrammar begin 1:SEQ = [ACT] 8:DIR = 0b0001 | 0b0010 | 0b0100 | 0b1000 | 0b0101 | 0b1001 | 0b0110 | 0b1010 # forward | back | left | right | forward-left | forward-right | back-left | back-right 1:ACT = (TIMES, Dict("move" => DIR, "sprint" => 1, "jump" => 1)) 6:TIMES = 5 | 10 | 25 | 50 | 75 | 100 end -function evaluate_trace_minerl(prog, grammar, env, show_moves) - resetPosition() - expr = rulenode2expr(prog, grammar) - - sequence_of_actions = eval(expr) - - sum_of_rewards = 0 - is_done = false - obs = nothing - for (times, action) ∈ sequence_of_actions - new_action = env.action_space.noop() - for (key, val) in action - if key == "move" - new_action["forward"] = val & 1 - new_action["back"] = val >> 1 & 1 - new_action["left"] = val >> 2 & 1 - new_action["right"] = val >> 3 - else - new_action[key] = val - end - end - - for i in 1:times - obs, reward, done, _ = env.step(new_action) - if show_moves - env.render() - end - - sum_of_rewards += reward - if done - is_done = true - printstyled("sum of rewards: $sum_of_rewards. Done\n", color=:green) - break - end - end - if is_done - break - end - end - println("Reward $sum_of_rewards") - return get_xyz_from_env(obs), is_done, sum_of_rewards -end - # make sure the probabilities are equal -@assert all(prob -> prob == minerl_grammar_2.log_probabilities[begin], minerl_grammar_2.log_probabilities) +@assert all(prob -> prob == minerl_grammar.log_probabilities[begin], minerl_grammar_2.log_probabilities) -function HerbSearch.set_env_position(x, y, z) - println("Setting env position: ($x, $y, $z)") - set_start_xyz(x, y, z) -end # overwrite the evaluate trace function -HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, env, show_moves) +HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, show_moves) HerbSearch.calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = HerbSearch.calculate_rule_cost_prob(rule_index, grammar) -# resetEnv() -iter = HerbSearch.GuidedTraceSearchIterator(minerl_grammar_2, :SEQ) +create_env("MineRLNavigateDenseProgSynth-v0"; seed=SEED, inf_health=true, inf_food=true, disable_mobs=true) +iter = HerbSearch.GuidedSearchTraceIterator(minerl_grammar, :SEQ) program = @time probe(Vector{Trace}(), iter, 3000000, 6) diff --git a/src/minecraft/minerl.jl b/src/minecraft/minerl.jl new file mode 100644 index 00000000..d2b9e603 --- /dev/null +++ b/src/minecraft/minerl.jl @@ -0,0 +1,181 @@ +using PyCall +pyimport("minerl") + +gym = pyimport("gym") + +mutable struct Environment + env::PyObject + settings + start_pos::Tuple{Float64,Float64,Float64} +end + +""" + create_env(name::String; ) + +Create environment. + +# Arguments +- `seed::Int`: the world seed. +- `inf_health::Bool`: enable infinite health. +- `inf_food::Bool`: enable infinite food. +- `disable_mobs`: disable mobs. +""" +function create_env(name::String; kwargs...) + if (@isdefined environment) && environment !== nothing + @warn "Environment already created" + return + end + global environment = Environment(gym.make(name), kwargs, (0, 0, 0)) + reset_env() +end + +""" + close_env() + +Close environment. +""" +function close_env() + if (@isdefined environment) && environment !== nothing + environment.env.close() + end + global environment = nothing +end + +""" + reset_env() + +Hard reset environment. +""" +function reset_env() + if (!@isdefined environment) || environment === nothing + @warn "Environment has not been created yet." + return + end + + env = environment.env + settings = environment.settings + + # set seed + if haskey(settings, :seed) + env.seed(settings[:seed]) + end + obs = env.reset() + + # set start position + environment.start_pos = get_xyz_from_obs(obs) + print(environment.start_pos) #TODO: remove/change print + + # weird bug fix + action = env.action_space.noop() + action["forward"] = 1 + env.step(action) + + # infinite health + if get(settings, :inf_health, false) + env.set_next_chat_message("/effect @a minecraft:instant_health 1000000 100 true") + env.step(action) + end + + # infinite food + if get(settings, :inf_food, false) + env.set_next_chat_message("/effect @a minecraft:saturation 1000000 255 true") + env.step(action) + end + + # disable mobs + if get(settings, :disable_mobs, false) + env.set_next_chat_message("/gamerule doMobSpawning false") + env.step(action) + env.set_next_chat_message("/kill @e[type=!player]") + env.step(action) + end + + printstyled("Environment created. x: $(environment.start_pos[1]), y: $(environment.start_pos[2]), z: $(environment.start_pos[3])\n", color=:green) #TODO: remove/change print +end + +""" + get_xyz_from_obs(obs)::Tuple{Float64, Float64, Float64} + +Get player coordinates from `obs`. +""" +function get_xyz_from_obs(obs)::Tuple{Float64,Float64,Float64} + return obs["xpos"][1], obs["ypos"][1], obs["zpos"][1] +end + +""" + soft_reset() + +Reset player position to `environment.start_pos`. +""" +function soft_reset_env() + if (!@isdefined environment) || environment === nothing + @warn "Environment has not been created yet." + return + end + + env = environment.env + action = env.action_space.noop() + x_player_start, y_player_start, z_player_start = environment.start_pos + env.set_next_chat_message("/tp @a $(x_player_start) $(y_player_start) $(z_player_start)") + + obs = env.step(action)[1] + obsx, obsy, obsz = get_xyz_from_obs(obs) + while obsx != x_player_start || obsy != y_player_start || obsz != z_player_start + obs = env.step(action)[1] + obsx, obsy, obsz = get_xyz_from_obs(obs) + end + println((obsx, obsy, obsz)) #TODO: remove/change print +end + +""" + evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, show_moves::Bool) + +Evaluate in MineRL environment. +""" +function evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, show_moves::Bool) + if (!@isdefined environment) || environment === nothing + error("Cannot evaluate MineRL program. Environment has not been created.") + end + + soft_reset_env() + + expr = rulenode2expr(prog, grammar) + sequence_of_actions = eval(expr) + + sum_of_rewards = 0 + is_done = false + obs = nothing + env = environment.env + for (times, action) ∈ sequence_of_actions + new_action = env.action_space.noop() + for (key, val) in action + if key == "move" + new_action["forward"] = val & 1 + new_action["back"] = val >> 1 & 1 + new_action["left"] = val >> 2 & 1 + new_action["right"] = val >> 3 + else + new_action[key] = val + end + end + + for i in 1:times + obs, reward, done, _ = env.step(new_action) + if show_moves + env.render() + end + + sum_of_rewards += reward + if done + is_done = true + printstyled("sum of rewards: $sum_of_rewards. Done\n", color=:green) #TODO: remove/change print + break + end + end + if is_done + break + end + end + println("Reward $sum_of_rewards") #TODO: remove/change print + return get_xyz_from_obs(obs), is_done, sum_of_rewards +end diff --git a/src/probe/guided_search_iterator.jl b/src/probe/guided_search_iterator.jl index 759ea552..db22124b 100644 --- a/src/probe/guided_search_iterator.jl +++ b/src/probe/guided_search_iterator.jl @@ -1,8 +1,8 @@ - @programiterator GuidedSearchIterator( spec::Vector{<:IOExample}, symboltable::SymbolTable, ) + Base.@kwdef mutable struct GuidedSearchState level::Int64 bank::Vector{Vector{RuleNode}} diff --git a/src/probe/guided_trace_search_iterator.jl b/src/probe/guided_trace_search_iterator.jl index 6da2674b..d455934c 100644 --- a/src/probe/guided_trace_search_iterator.jl +++ b/src/probe/guided_trace_search_iterator.jl @@ -1,7 +1,6 @@ +@programiterator GuidedSearchTraceIterator() -@programiterator GuidedTraceSearchIterator() - -function Base.iterate(iter::GuidedTraceSearchIterator) +function Base.iterate(iter::GuidedSearchTraceIterator) iterate(iter, GuidedSearchState( level=-1, bank=[], @@ -11,7 +10,7 @@ function Base.iterate(iter::GuidedTraceSearchIterator) )) end -function Base.iterate(iter::GuidedTraceSearchIterator, state::GuidedSearchState) +function Base.iterate(iter::GuidedSearchTraceIterator, state::GuidedSearchState) grammar = get_grammar(iter.solver) start_symbol = get_starting_symbol(iter.solver) # wrap in while true to optimize for tail call diff --git a/src/probe/new_program_iterator.jl b/src/probe/new_program_iterator.jl index 96c03a4b..0230cc3a 100644 --- a/src/probe/new_program_iterator.jl +++ b/src/probe/new_program_iterator.jl @@ -1,3 +1,5 @@ +calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = calculate_rule_cost_prob(rule_index, grammar) + struct NewProgramsIterator level::Int bank::Vector{Vector{RuleNode}} @@ -75,7 +77,7 @@ function Base.iterate(iter::NewProgramsIterator, state::NewProgramsState) return nothing end -function calculate_rule_cost_prob(rule_index, grammar, log_base = 2) +function calculate_rule_cost_prob(rule_index, grammar, log_base=2) log_prob = grammar.log_probabilities[rule_index] / log(log_base) return convert(Int64, round(-log_prob)) end @@ -84,8 +86,6 @@ function calculate_rule_cost_size(rule_index, grammar) return 1 end -calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = calculate_rule_cost_size(rule_index, grammar) - """ calculate_program_cost(program::RuleNode, grammar::ContextSensitiveGrammar) Calculates the cost of a program by summing up the cost of the children and the cost of the rule diff --git a/src/probe/probe_iterator.jl b/src/probe/probe_iterator.jl index ea47b875..0c35393e 100644 --- a/src/probe/probe_iterator.jl +++ b/src/probe/probe_iterator.jl @@ -1,46 +1,8 @@ -""" - struct ProgramCache - -Stores the evaluation cost and the program in a structure. -This -""" -mutable struct ProgramCache - program::RuleNode - correct_examples::Vector{Int} - cost::Int -end -function Base.:(==)(a::ProgramCache, b::ProgramCache) - return a.program == b.program -end -Base.hash(a::ProgramCache) = hash(a.program) - -mutable struct ProgramCacheTrace - program::RuleNode - cost::Int - reward::Float64 -end - -function Base.:(==)(a::ProgramCacheTrace, b::ProgramCacheTrace) - return a.program == b.program -end -Base.hash(a::ProgramCacheTrace) = hash(a.program) - -include("sum_iterator.jl") -include("new_program_iterator.jl") -include("guided_search_iterator.jl") -include("guided_trace_search_iterator.jl") - -include("select_partial_sols.jl") -include("update_grammar.jl") - -select_partial_solution(partial_sols::Vector{ProgramCache}, all_selected_psols::Set{ProgramCache}) = HerbSearch.selectpsol_largest_subset(partial_sols, all_selected_psols) -update_grammar!(grammar::ContextSensitiveGrammar, PSols_with_eval_cache::Vector{ProgramCache}, examples::Vector{<:IOExample}) = update_grammar(grammar, PSols_with_eval_cache, examples) - get_prog_eval(::ProgramIterator, prog::RuleNode) = (prog, []) get_prog_eval(::GuidedSearchIterator, prog::Tuple{RuleNode,Vector{Any}}) = prog -get_prog_eval(::GuidedTraceSearchIterator, prog::Tuple{RuleNode,Tuple{Any,Bool,Number}}) = prog +get_prog_eval(::GuidedSearchTraceIterator, prog::Tuple{RuleNode,Tuple{Any,Bool,Number}}) = prog """ probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_time::Int, iteration_size::Int) @@ -131,31 +93,13 @@ end evaluate_trace(program::RuleNode, grammar::ContextSensitiveGrammar) = error("Evaluate trace method should be overwritten") -# this is here just to be overwritten in getting_started_minerl.jl -set_env_position(x, y, z) = error("Set env position method should be overwritten") - -function select_partial_solution(partial_sols::Vector{ProgramCacheTrace}, all_selected_psols::Set{ProgramCacheTrace}) - if isempty(partial_sols) - return Vector{ProgramCache}() - end - push!(partial_sols, all_selected_psols...) - # sort partial solutions by reward - sort!(partial_sols, by=x -> x.reward, rev=true) - to_select = 5 - return partial_sols[1:min(to_select, length(partial_sols))] -end -""" - -Probe for a solution using the given `iterator` and `examples` with a time limit of `max_time` and `iteration_size`. -""" function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, iteration_size::Int) start_time = time() # store a set of all the results of evaluation programs eval_cache = Set() state = nothing grammar = get_grammar(iterator.solver) - symboltable = SymbolTable(grammar) best_reward = 0 # all partial solutions that were found so far diff --git a/src/probe/program_cache.jl b/src/probe/program_cache.jl new file mode 100644 index 00000000..5f0a9447 --- /dev/null +++ b/src/probe/program_cache.jl @@ -0,0 +1,28 @@ +""" + struct ProgramCache + +Stores the evaluation cost and the program in a structure. +This +""" +mutable struct ProgramCache + program::RuleNode + correct_examples::Vector{Int} + cost::Int +end +function Base.:(==)(a::ProgramCache, b::ProgramCache) + return a.program == b.program +end + +Base.hash(a::ProgramCache) = hash(a.program) + +mutable struct ProgramCacheTrace + program::RuleNode + cost::Int + reward::Float64 +end + +function Base.:(==)(a::ProgramCacheTrace, b::ProgramCacheTrace) + return a.program == b.program +end + +Base.hash(a::ProgramCacheTrace) = hash(a.program) diff --git a/src/probe/select_partial_sols.jl b/src/probe/select_partial_sols.jl index a039f365..a91fd269 100644 --- a/src/probe/select_partial_sols.jl +++ b/src/probe/select_partial_sols.jl @@ -1,3 +1,5 @@ +select_partial_solution(partial_sols::Vector{ProgramCache}, all_selected_psols::Set{ProgramCache}) = selectpsol_largest_subset(partial_sols, all_selected_psols) + """ selectpsol_largest_subset(partial_sols::Vector{ProgramCache}}, all_selected_psols::Set{ProgramCache})) @@ -24,12 +26,12 @@ function selectpsol_largest_subset(partial_sols::Vector{ProgramCache}, all_selec end """ - selectpsol_first_cheapest(partial_sols::Vector{ProgramCache}}, all_selected_psols::Set{ProgramCache})) + selectpsol_first_cheapest(partial_sols::Vector{ProgramCache}}, ::Set{ProgramCache})) This scheme selects a single cheapest program (first enumerated) that satisfies a unique subset of examples. """ -function selectpsol_first_cheapest(partial_sols::Vector{ProgramCache}, all_selected_psol::Set{ProgramCache}) +function selectpsol_first_cheapest(partial_sols::Vector{ProgramCache}, ::Set{ProgramCache}) # maps subset of examples to the cheapest program mapping = Dict{Vector{Int},ProgramCache}() for sol ∈ partial_sols @@ -48,11 +50,11 @@ function selectpsol_first_cheapest(partial_sols::Vector{ProgramCache}, all_selec end """ - selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, all_selected_psol::Set{ProgramCache}) + selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, ::Set{ProgramCache}) This scheme selects all cheapest programs that satisfies a unique subset of examples. """ -function selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, all_selected_psol::Set{ProgramCache}) +function selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, ::Set{ProgramCache}) # maps subset of examples to the cheapest program mapping = Dict{Vector{Int},Vector{ProgramCache}}() for sol ∈ partial_sols @@ -72,4 +74,15 @@ function selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, all_selecte end # get all cheapest programs that satisfy unique subsets of examples return collect(Iterators.flatten(values(mapping))) -end \ No newline at end of file +end + +function select_partial_solution(partial_sols::Vector{ProgramCacheTrace}, all_selected_psols::Set{ProgramCacheTrace}) + if isempty(partial_sols) + return Vector{ProgramCache}() + end + push!(partial_sols, all_selected_psols...) + # sort partial solutions by reward + sort!(partial_sols, by=x -> x.reward, rev=true) + to_select = 5 + return partial_sols[1:min(to_select, length(partial_sols))] +end diff --git a/src/probe/update_grammar.jl b/src/probe/update_grammar.jl index 18ed953f..411c8257 100644 --- a/src/probe/update_grammar.jl +++ b/src/probe/update_grammar.jl @@ -1,4 +1,3 @@ - """ update_grammar(grammar::ContextSensitiveGrammar, PSols_with_eval_cache::Vector{ProgramCache}, examples::Vector{<:IOExample}) @@ -10,7 +9,7 @@ Update the given `grammar` using the provided `PSols_with_eval_cache` and `examp - `examples::Vector{<:IOExample}`: The input-output examples. """ -function update_grammar(grammar::ContextSensitiveGrammar, PSols_with_eval_cache::Vector{ProgramCache}, examples::Vector{<:IOExample}) +function update_grammar!(grammar::ContextSensitiveGrammar, PSols_with_eval_cache::Vector{ProgramCache}, examples::Vector{<:IOExample}) sum = 0 for rule_index in eachindex(grammar.rules) # iterate for each rule_index highest_correct_nr = 0 From 96524c8afeb852febf8dad43fb386d1fdb2e1394 Mon Sep 17 00:00:00 2001 From: Nicolae Filat Date: Mon, 27 May 2024 12:07:48 +0200 Subject: [PATCH 2/6] Fix rendering --- src/minecraft/getting_started_minerl.jl | 3 +-- src/minecraft/minerl.jl | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/minecraft/getting_started_minerl.jl b/src/minecraft/getting_started_minerl.jl index 9104479c..3bc05511 100644 --- a/src/minecraft/getting_started_minerl.jl +++ b/src/minecraft/getting_started_minerl.jl @@ -2,7 +2,6 @@ using HerbGrammar, HerbSpecification, HerbSearch using Logging disable_logging(LogLevel(1)) -include("minerl.jl") SEED = 958129 @@ -14,7 +13,7 @@ minerl_grammar = @pcsgrammar begin end # make sure the probabilities are equal -@assert all(prob -> prob == minerl_grammar.log_probabilities[begin], minerl_grammar_2.log_probabilities) +@assert all(prob -> prob == minerl_grammar.log_probabilities[begin], minerl_grammar.log_probabilities) # overwrite the evaluate trace function HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, show_moves) diff --git a/src/minecraft/minerl.jl b/src/minecraft/minerl.jl index d2b9e603..f45a79c1 100644 --- a/src/minecraft/minerl.jl +++ b/src/minecraft/minerl.jl @@ -1,4 +1,5 @@ using PyCall +using HerbGrammar pyimport("minerl") gym = pyimport("gym") From 4e670f347c8c3177646fd7b941bc7468220ea30c Mon Sep 17 00:00:00 2001 From: Nicolae Filat Date: Mon, 27 May 2024 12:59:58 +0200 Subject: [PATCH 3/6] Minimize usage of global environment variable. Add nice probe ascii art print. Move the @isdefined environment check to the getting_started_minerl.jl --- src/minecraft/getting_started_minerl.jl | 15 +++++--- src/minecraft/logo_print.jl | 17 +++++++++ src/minecraft/minerl.jl | 49 +++++++------------------ 3 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 src/minecraft/logo_print.jl diff --git a/src/minecraft/getting_started_minerl.jl b/src/minecraft/getting_started_minerl.jl index 3bc05511..55a41592 100644 --- a/src/minecraft/getting_started_minerl.jl +++ b/src/minecraft/getting_started_minerl.jl @@ -1,10 +1,15 @@ +include("minerl.jl") +include("logo_print.jl") + +SEED = 958129 +if !(@isdefined environment) + environment = create_env("MineRLNavigateDenseProgSynth-v0"; seed=SEED, inf_health=true, inf_food=true, disable_mobs=true) +end + using HerbGrammar, HerbSpecification, HerbSearch using Logging disable_logging(LogLevel(1)) - -SEED = 958129 - minerl_grammar = @pcsgrammar begin 1:SEQ = [ACT] 8:DIR = 0b0001 | 0b0010 | 0b0100 | 0b1000 | 0b0101 | 0b1001 | 0b0110 | 0b1010 # forward | back | left | right | forward-left | forward-right | back-left | back-right @@ -16,9 +21,9 @@ end @assert all(prob -> prob == minerl_grammar.log_probabilities[begin], minerl_grammar.log_probabilities) # overwrite the evaluate trace function -HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, show_moves) +HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, environment, show_moves) HerbSearch.calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = HerbSearch.calculate_rule_cost_prob(rule_index, grammar) -create_env("MineRLNavigateDenseProgSynth-v0"; seed=SEED, inf_health=true, inf_food=true, disable_mobs=true) +print_logo() iter = HerbSearch.GuidedSearchTraceIterator(minerl_grammar, :SEQ) program = @time probe(Vector{Trace}(), iter, 3000000, 6) diff --git a/src/minecraft/logo_print.jl b/src/minecraft/logo_print.jl new file mode 100644 index 00000000..898d1778 --- /dev/null +++ b/src/minecraft/logo_print.jl @@ -0,0 +1,17 @@ +""" + print_logo() + +Prints a stylized ascii art of the word probe. +""" +function print_logo() + printstyled(raw""" _ + | | + _ __ _ __ ___ | |__ ___ +| '_ \| '__/ _ \| '_ \ / _ \ +| |_) | | | (_) | |_) | __/ +| .__/|_| \___/|_.__/ \___| +| | +|_| """, color=:magenta, bold=true) + println() + println(repeat("=", 80) * "\n") +end \ No newline at end of file diff --git a/src/minecraft/minerl.jl b/src/minecraft/minerl.jl index f45a79c1..8ce39ab4 100644 --- a/src/minecraft/minerl.jl +++ b/src/minecraft/minerl.jl @@ -1,12 +1,11 @@ using PyCall -using HerbGrammar pyimport("minerl") gym = pyimport("gym") mutable struct Environment env::PyObject - settings + settings::Dict{Symbol,Integer} start_pos::Tuple{Float64,Float64,Float64} end @@ -22,37 +21,26 @@ Create environment. - `disable_mobs`: disable mobs. """ function create_env(name::String; kwargs...) - if (@isdefined environment) && environment !== nothing - @warn "Environment already created" - return - end - global environment = Environment(gym.make(name), kwargs, (0, 0, 0)) - reset_env() + environment = Environment(gym.make(name), Dict(kwargs), (0, 0, 0)) + reset_env(environment) + return environment end """ - close_env() + close_env(environment::Environment) Close environment. """ -function close_env() - if (@isdefined environment) && environment !== nothing - environment.env.close() - end - global environment = nothing +function close_env(environment::Environment) + environment.env.close() end """ - reset_env() + reset_env(environment::Environment) Hard reset environment. """ -function reset_env() - if (!@isdefined environment) || environment === nothing - @warn "Environment has not been created yet." - return - end - +function reset_env(environment::Environment) env = environment.env settings = environment.settings @@ -104,16 +92,11 @@ function get_xyz_from_obs(obs)::Tuple{Float64,Float64,Float64} end """ - soft_reset() + soft_reset(environment::Environment) Reset player position to `environment.start_pos`. """ -function soft_reset_env() - if (!@isdefined environment) || environment === nothing - @warn "Environment has not been created yet." - return - end - +function soft_reset_env(environment::Environment) env = environment.env action = env.action_space.noop() x_player_start, y_player_start, z_player_start = environment.start_pos @@ -129,16 +112,12 @@ function soft_reset_env() end """ - evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, show_moves::Bool) + evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, environment::Environment, show_moves::Bool) Evaluate in MineRL environment. """ -function evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, show_moves::Bool) - if (!@isdefined environment) || environment === nothing - error("Cannot evaluate MineRL program. Environment has not been created.") - end - - soft_reset_env() +function evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, environment::Environment, show_moves::Bool) + soft_reset_env(environment) expr = rulenode2expr(prog, grammar) sequence_of_actions = eval(expr) From 029a2efd9ecf6beafb28e7504c68c454c2feb72e Mon Sep 17 00:00:00 2001 From: Nicolae Filat Date: Mon, 27 May 2024 13:27:19 +0200 Subject: [PATCH 4/6] Found the reason why the render would not work. Add a fix and a comment such that we know what weng wrong. --- src/minecraft/getting_started_minerl.jl | 12 ++++++------ src/minecraft/minerl.jl | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/minecraft/getting_started_minerl.jl b/src/minecraft/getting_started_minerl.jl index 55a41592..217de147 100644 --- a/src/minecraft/getting_started_minerl.jl +++ b/src/minecraft/getting_started_minerl.jl @@ -1,11 +1,6 @@ include("minerl.jl") include("logo_print.jl") -SEED = 958129 -if !(@isdefined environment) - environment = create_env("MineRLNavigateDenseProgSynth-v0"; seed=SEED, inf_health=true, inf_food=true, disable_mobs=true) -end - using HerbGrammar, HerbSpecification, HerbSearch using Logging disable_logging(LogLevel(1)) @@ -21,9 +16,14 @@ end @assert all(prob -> prob == minerl_grammar.log_probabilities[begin], minerl_grammar.log_probabilities) # overwrite the evaluate trace function -HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=false) = evaluate_trace_minerl(prog, grammar, environment, show_moves) +HerbSearch.evaluate_trace(prog::RuleNode, grammar::ContextSensitiveGrammar; show_moves=true) = evaluate_trace_minerl(prog, grammar, environment, show_moves) HerbSearch.calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = HerbSearch.calculate_rule_cost_prob(rule_index, grammar) +SEED = 958129 +if !(@isdefined environment) + environment = create_env("MineRLNavigateDenseProgSynth-v0"; seed=SEED, inf_health=true, inf_food=true, disable_mobs=true) +end print_logo() iter = HerbSearch.GuidedSearchTraceIterator(minerl_grammar, :SEQ) program = @time probe(Vector{Trace}(), iter, 3000000, 6) + \ No newline at end of file diff --git a/src/minecraft/minerl.jl b/src/minecraft/minerl.jl index 8ce39ab4..765ecef6 100644 --- a/src/minecraft/minerl.jl +++ b/src/minecraft/minerl.jl @@ -3,6 +3,8 @@ pyimport("minerl") gym = pyimport("gym") +# WARNING: !!! NEVER MOVE THIS. It should ALWAYS be after the `pyimport`. I spent hours debugging this !!! +using HerbGrammar mutable struct Environment env::PyObject settings::Dict{Symbol,Integer} From 05cf1a17d8ec980c4657bb79c15d0e9e5f43b8b1 Mon Sep 17 00:00:00 2001 From: Nils Marten Mikk Date: Mon, 27 May 2024 13:42:36 +0200 Subject: [PATCH 5/6] Improve Probe documentation --- src/minecraft/getting_started_minerl.jl | 2 +- src/minecraft/minerl.jl | 8 ++-- src/probe/benchmark.jl | 50 ----------------------- src/probe/guided_search_iterator.jl | 3 +- src/probe/guided_trace_search_iterator.jl | 5 +-- src/probe/new_program_iterator.jl | 23 +++++++++-- src/probe/probe_iterator.jl | 48 ++++++++++++++-------- src/probe/select_partial_sols.jl | 5 +++ src/probe/update_grammar.jl | 4 +- test/test_probe.jl | 2 +- 10 files changed, 64 insertions(+), 86 deletions(-) delete mode 100644 src/probe/benchmark.jl diff --git a/src/minecraft/getting_started_minerl.jl b/src/minecraft/getting_started_minerl.jl index 217de147..05852bf8 100644 --- a/src/minecraft/getting_started_minerl.jl +++ b/src/minecraft/getting_started_minerl.jl @@ -25,5 +25,5 @@ if !(@isdefined environment) end print_logo() iter = HerbSearch.GuidedSearchTraceIterator(minerl_grammar, :SEQ) -program = @time probe(Vector{Trace}(), iter, 3000000, 6) +program = @time probe(Vector{Trace}(), iter, max_time=3000000, cycle_length=6) \ No newline at end of file diff --git a/src/minecraft/minerl.jl b/src/minecraft/minerl.jl index 765ecef6..6186821f 100644 --- a/src/minecraft/minerl.jl +++ b/src/minecraft/minerl.jl @@ -31,7 +31,7 @@ end """ close_env(environment::Environment) -Close environment. +Close `environment`. """ function close_env(environment::Environment) environment.env.close() @@ -40,7 +40,7 @@ end """ reset_env(environment::Environment) -Hard reset environment. +Hard reset `environment`. """ function reset_env(environment::Environment) env = environment.env @@ -94,7 +94,7 @@ function get_xyz_from_obs(obs)::Tuple{Float64,Float64,Float64} end """ - soft_reset(environment::Environment) + soft_reset_env(environment::Environment) Reset player position to `environment.start_pos`. """ @@ -116,7 +116,7 @@ end """ evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, environment::Environment, show_moves::Bool) -Evaluate in MineRL environment. +Evaluate in MineRL `environment`. """ function evaluate_trace_minerl(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, environment::Environment, show_moves::Bool) soft_reset_env(environment) diff --git a/src/probe/benchmark.jl b/src/probe/benchmark.jl deleted file mode 100644 index 19ed76db..00000000 --- a/src/probe/benchmark.jl +++ /dev/null @@ -1,50 +0,0 @@ -using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret -using BenchmarkTools - -my_replace(x, y, z) = replace(x, y => z, count=1) - -example_grammar = @pcsgrammar begin - 0.188:S = arg - 0.188:S = "" - 0.188:S = "<" - 0.188:S = ">" - 0.188:S = my_replace(S, S, S) - 0.059:S = S * S -end - -examples = [ - IOExample(Dict(:arg => "a < 4 and a > 0"), "a 4 and a 0") # <- e0 with correct space - IOExample(Dict(:arg => ""), "open and close") # <- e1 -] - -# benchmarks the guided search iteartor until certain level -function time_iter(; max_level, iterator) - iter = iterator(example_grammar, :S, examples, SymbolTable(example_grammar)) - next = iterate(iter) - level = 0 - @time while !isnothing(next) && level <= max_level - program, state = next - level = state.level - next = iterate(iter, state) - end -end - -using Logging -Logging.disable_logging(LogLevel(1000)) - -# use probability -HerbSearch.calculate_rule_cost(rule_index::Int, grammar::ContextSensitiveGrammar) = HerbSearch.calculate_rule_cost_prob(rule_index, grammar) - -max_level = 30 -runs = 4 -printstyled("Optimized\n"; color=:green) -for _ ∈ 1:runs - time_iter(max_level=max_level, iterator=GuidedSearchIterator) -end - -println("=====================================") -printstyled("Normal version\n", color=:green) - -for _ ∈ 1:runs - time_iter(max_level=max_level, iterator=GuidedSearchIterator) -end \ No newline at end of file diff --git a/src/probe/guided_search_iterator.jl b/src/probe/guided_search_iterator.jl index db22124b..4759ceb2 100644 --- a/src/probe/guided_search_iterator.jl +++ b/src/probe/guided_search_iterator.jl @@ -39,7 +39,6 @@ function Base.iterate(iter::GuidedSearchIterator, state::GuidedSearchState)::Uni end # go over all programs in a level while state.next_iter !== nothing - # prog = pop!(state.programs) # get next program prog::RuleNode, next_state = state.next_iter # move in advance state.next_iter = iterate(state.iter, next_state) @@ -66,4 +65,4 @@ function Base.iterate(iter::GuidedSearchIterator, state::GuidedSearchState)::Uni push!(state.bank[state.level+1], prog) # add program to bank end end -end \ No newline at end of file +end diff --git a/src/probe/guided_trace_search_iterator.jl b/src/probe/guided_trace_search_iterator.jl index d455934c..e81b57a6 100644 --- a/src/probe/guided_trace_search_iterator.jl +++ b/src/probe/guided_trace_search_iterator.jl @@ -28,7 +28,6 @@ function Base.iterate(iter::GuidedSearchTraceIterator, state::GuidedSearchState) end # go over all programs in a level while state.next_iter !== nothing - # prog = pop!(state.programs) # get next program prog::RuleNode, next_state = state.next_iter # move in advance state.next_iter = iterate(state.iter, next_state) @@ -38,8 +37,6 @@ function Base.iterate(iter::GuidedSearchTraceIterator, state::GuidedSearchState) eval_observation, is_done, final_reward = evaluate_trace(prog, grammar) eval_observation_rounded = round.(eval_observation, digits=1) if eval_observation_rounded in state.eval_cache # program already cached - # print("Skipping this.") - @info "Skipping program" continue end @@ -52,4 +49,4 @@ function Base.iterate(iter::GuidedSearchTraceIterator, state::GuidedSearchState) push!(state.bank[state.level+1], prog) # add program to bank end end -end \ No newline at end of file +end diff --git a/src/probe/new_program_iterator.jl b/src/probe/new_program_iterator.jl index 0230cc3a..eddff800 100644 --- a/src/probe/new_program_iterator.jl +++ b/src/probe/new_program_iterator.jl @@ -77,18 +77,33 @@ function Base.iterate(iter::NewProgramsIterator, state::NewProgramsState) return nothing end -function calculate_rule_cost_prob(rule_index, grammar, log_base=2) +""" + calculate_rule_cost_prob(rule_index::Int, grammar::ContextSensitiveGrammar, log_base::Int=2) + +Calculate cost of rule `rule_index` in `grammar` based on its probability. + +``cost = -log_{base}(probability)`` +""" +function calculate_rule_cost_prob(rule_index::Int, grammar::ContextSensitiveGrammar, log_base::Int=2) log_prob = grammar.log_probabilities[rule_index] / log(log_base) return convert(Int64, round(-log_prob)) end -function calculate_rule_cost_size(rule_index, grammar) +""" + calculate_rule_cost_size(::Int, ::ContextSensitiveGrammar) + +Calculate rule cost based on size. + +This will always return 1. +""" +function calculate_rule_cost_size(::Int, ::ContextSensitiveGrammar) return 1 end """ - calculate_program_cost(program::RuleNode, grammar::ContextSensitiveGrammar) -Calculates the cost of a program by summing up the cost of the children and the cost of the rule + calculate_program_cost(program::RuleNode, grammar::ContextSensitiveGrammar) + +Calculates the cost of a program by summing up the cost of the children and the cost of the rule. """ function calculate_program_cost(program::RuleNode, grammar::ContextSensitiveGrammar) cost_children = sum([calculate_program_cost(child, grammar) for child ∈ program.children], init=0) diff --git a/src/probe/probe_iterator.jl b/src/probe/probe_iterator.jl index 0c35393e..8f7c16fa 100644 --- a/src/probe/probe_iterator.jl +++ b/src/probe/probe_iterator.jl @@ -1,3 +1,8 @@ +""" + get_prog_eval(iterator, prog) + +Get the program and its evaluation. +""" get_prog_eval(::ProgramIterator, prog::RuleNode) = (prog, []) get_prog_eval(::GuidedSearchIterator, prog::Tuple{RuleNode,Vector{Any}}) = prog @@ -5,11 +10,16 @@ get_prog_eval(::GuidedSearchIterator, prog::Tuple{RuleNode,Vector{Any}}) = prog get_prog_eval(::GuidedSearchTraceIterator, prog::Tuple{RuleNode,Tuple{Any,Bool,Number}}) = prog """ - probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_time::Int, iteration_size::Int) + probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_time::Int, cycle_length::Int) + +Probe for a solution using the given `iterator` and `examples` with a time limit of `max_time` and a cycle length of `cycle_length`. -Probe for a solution using the given `iterator` and `examples` with a time limit of `max_time` and `iteration_size`. +The selection, update, and cost functions can be changed by overriding the following functions: +- [`select_partial_solution`](@ref) +- [`update_grammar!`](@ref) +- [`calculate_rule_cost`](@ref) """ -function probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_time::Int, iteration_size::Int) +function probe(examples::Vector{<:IOExample}, iterator::ProgramIterator; max_time::Int, cycle_length::Int) start_time = time() # store a set of all the results of evaluation programs eval_cache = Set() @@ -24,7 +34,7 @@ function probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_tim # partial solutions for the current synthesis cycle psol_with_eval_cache = Vector{ProgramCache}() next = state === nothing ? iterate(iterator) : iterate(iterator, state) - while next !== nothing && i < iteration_size # run one iteration + while next !== nothing && i < cycle_length # run one cycle program, state = next # evaluate program @@ -70,11 +80,12 @@ function probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_tim if next === nothing return nothing end + partial_sols = filter(x -> x ∉ all_selected_psols, select_partial_solution(psol_with_eval_cache, all_selected_psols)) if !isempty(partial_sols) push!(all_selected_psols, partial_sols...) - # update probabilites if any promising partial solutions update_grammar!(grammar, partial_sols, examples) # update probabilites + # restart iterator eval_cache = Set() state = nothing @@ -91,10 +102,14 @@ function probe(examples::Vector{<:IOExample}, iterator::ProgramIterator, max_tim return nothing end +""" + evaluate_trace(program::RuleNode, grammar::ContextSensitiveGrammar) -evaluate_trace(program::RuleNode, grammar::ContextSensitiveGrammar) = error("Evaluate trace method should be overwritten") +Evaluate the `program` with the `grammar`. +""" +evaluate_trace(program::RuleNode, grammar::ContextSensitiveGrammar; show_moves::Bool) = error("Evaluate trace method should be overwritten") -function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, iteration_size::Int) +function probe(traces::Vector{Trace}, iterator::ProgramIterator; max_time::Int, cycle_length::Int) start_time = time() # store a set of all the results of evaluation programs eval_cache = Set() @@ -110,7 +125,7 @@ function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, # partial solutions for the current synthesis cycle psol_with_eval_cache = Vector{ProgramCacheTrace}() next = state === nothing ? iterate(iterator) : iterate(iterator, state) - while next !== nothing && i < iteration_size # run one iteration + while next !== nothing && i < cycle_length # run one cycle program, state = next # evaluate @@ -132,15 +147,12 @@ function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, elseif is_partial_sol # partial solution cost = calculate_program_cost(program, grammar) push!(psol_with_eval_cache, ProgramCacheTrace(program, cost, reward)) - # if length(psol_with_eval_cache) >= 2 # play with this threshold - # break - # end end push!(eval_cache, eval_observation_rounded) i += 1 - if i < iteration_size + if i < cycle_length next = iterate(iterator, state) end end @@ -150,7 +162,7 @@ function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, return nothing end - partial_sols = select_partial_solution(psol_with_eval_cache, all_selected_psols) + partial_sols = filter(x -> x ∉ all_selected_psols, select_partial_solution(psol_with_eval_cache, all_selected_psols)) if !isempty(partial_sols) printstyled("Restarting!\n", color=:magenta) @@ -161,11 +173,11 @@ function probe(traces::Vector{Trace}, iterator::ProgramIterator, max_time::Int, state = nothing #for loop to update all_selected_psols with new costs - # for prog_with_cache ∈ all_selected_psols - # program = prog_with_cache.program - # new_cost = calculate_program_cost(program, grammar) - # prog_with_cache.cost = new_cost - # end + for prog_with_cache ∈ all_selected_psols + program = prog_with_cache.program + new_cost = calculate_program_cost(program, grammar) + prog_with_cache.cost = new_cost + end end end diff --git a/src/probe/select_partial_sols.jl b/src/probe/select_partial_sols.jl index a91fd269..0434e145 100644 --- a/src/probe/select_partial_sols.jl +++ b/src/probe/select_partial_sols.jl @@ -76,6 +76,11 @@ function selectpsol_all_cheapest(partial_sols::Vector{ProgramCache}, ::Set{Progr return collect(Iterators.flatten(values(mapping))) end +""" + select_partial_solution(partial_sols::Vector{ProgramCacheTrace}, all_selected_psols::Set{ProgramCacheTrace}) + +Select five programs with the highest reward. +""" function select_partial_solution(partial_sols::Vector{ProgramCacheTrace}, all_selected_psols::Set{ProgramCacheTrace}) if isempty(partial_sols) return Vector{ProgramCache}() diff --git a/src/probe/update_grammar.jl b/src/probe/update_grammar.jl index 411c8257..f1ab62b3 100644 --- a/src/probe/update_grammar.jl +++ b/src/probe/update_grammar.jl @@ -72,10 +72,10 @@ end """ contains_rule(program::RuleNode, rule_index::Int) -Check if a given `RuleNode` contains has used a derivation rule with the specified `rule_index` +Check if a given `program` contains a derivation rule with the specified `rule_index`. # Arguments -- `program::RuleNode`: The `RuleNode` to check. +- `program::RuleNode`: The `program` to check. - `rule_index::Int`: The index of the rule to check for. """ diff --git a/test/test_probe.jl b/test/test_probe.jl index 10124a5f..d42ff398 100644 --- a/test/test_probe.jl +++ b/test/test_probe.jl @@ -254,7 +254,7 @@ end deep_copy_grammar = deepcopy(grammar_to_use) iter = HerbSearch.GuidedSearchIterator(deep_copy_grammar, :S, examples, symboltable) max_time = 5 - runtime = @timed program = probe(examples, iter, max_time, 100) + runtime = @timed program = probe(examples, iter, max_time=max_time, cycle_length=100) expression = rulenode2expr(program, grammar_to_use) @test runtime.time <= max_time From 773e6170ddadb3fcb47f0583f13e52a8def2d8da Mon Sep 17 00:00:00 2001 From: Nicolae Filat Date: Tue, 28 May 2024 12:18:59 +0200 Subject: [PATCH 6/6] Update logo --- src/minecraft/logo_print.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/minecraft/logo_print.jl b/src/minecraft/logo_print.jl index 898d1778..07c63569 100644 --- a/src/minecraft/logo_print.jl +++ b/src/minecraft/logo_print.jl @@ -4,14 +4,14 @@ Prints a stylized ascii art of the word probe. """ function print_logo() - printstyled(raw""" _ - | | - _ __ _ __ ___ | |__ ___ -| '_ \| '__/ _ \| '_ \ / _ \ -| |_) | | | (_) | |_) | __/ -| .__/|_| \___/|_.__/ \___| -| | -|_| """, color=:magenta, bold=true) + printstyled(raw"""______ _ __ ___ ____ ______ _ +| ___ \ | | / / | \/ (_) | ___ \ | +| |_/ / __ ___ | |__ ___ / / | . . |_ _ __ ___| |_/ / | +| __/ '__/ _ \| '_ \ / _ \ / / | |\/| | | '_ \ / _ \ /| | +| | | | | (_) | |_) | __/ / / | | | | | | | | __/ |\ \| |____ +\_| |_| \___/|_.__/ \___| /_/ \_| |_/_|_| |_|\___\_| \_\_____/ + + """, color=:magenta, bold=true) println() println(repeat("=", 80) * "\n") end \ No newline at end of file