text:
# # Fitting and Predicting - Frequency Data
#md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/notebooks/fitpredictFreq.ipynb)
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/notebooks/fitpredictFreq.ipynb)

# RHEOS can also fit models to dynamic mechanical analysis data from oscillatory tests.

using RHEOS

# ## Fitting

# #### Step 1: Loading Experimental Data

# RHEOS has a convenient function to import data from CSV files (more information in [File I/O](@ref) section).

data = importcsv("assets/data_freq.csv", ω_col = 1, Gp_col = 2, Gpp_col = 3)
rheofreqdatatype(data)

# The frequency, storage modulus and loss modulus are stored into a [`RheoFreqData`](@ref) struct.

# #### Step 2: Model Fit

# Let's fit a Fractional Kelvin-Voigt model. The first argument is our data, the second argument tells RHEOS which model to fit ([`RheoModelClass`](@ref)). The function will return a [`RheoModel`](@ref) object, i.e. a [`RheoModelClass`](@ref) with fixed values for the parameters. The function [`dynamicmodelfit`](@ref) possesses all the additional arguments (lower and upper bounds, initial parameters, tolerance) as for the fitting of time data. Note that initial parameters, upper bounds and lower bounds can be partially specified and the rest will be filled in automatically.

## Lower bounds
Lo = (cₐ = 0.0, a = 0.01, cᵦ = 0.0, β = 0.01)
## Upper bounds
Hi = (cₐ = 1e2, a = 0.99, cᵦ = 1e2, β = 0.99)
## Initial parameters
P0 = (cₐ = 1.0, a = 0.36, cᵦ = 1.0, β = 0.03)

FractKV_model = dynamicmodelfit(data, Fract_KelvinVoigt, weights = "log", lo = Lo, hi = Hi, p0 = P0)

# For the fitting process RHEOS relies on the optimistion package [NLopt.jl](https://nlopt.readthedocs.io/en/latest/). RHEOS makes use of a local derivative free algorithm, specifically the Tom Rowan's "Subplex" algorithm originally introduced in his [PhD thesis at The University of Texas at Austin, 1990](https://dl.acm.org/doi/book/10.5555/100816).

# The storage and loss moduli can sometimes occupy different orders of magnitude. This can cause problems during fitting as the optimisation routine will weight errors at the higher orders of magnitude more strongly than those at the lower orders of magnitude. This is a more general problem often faced during multi-objective optimisation problems. RHEOS offers four rescaling options (weight):
# - The `local` option rescales the cost at each point by the point itself.
# - The `log` approach simply re-scales all the storage and loss moduli and their predicted values logarithmically before finding the error between them.
# - A third option in RHEOS divides the cost at each point by the mean value of the storage and loss modulus respectively depending on which cost is being calculated. This can work well but performance is hindered if the storage or loss moduli individually vary over many orders of magnitude (`mean`).
# - The fourth option offered by RHEOS is simply manual weightings provided by the user for each modulus (`manual`).
# In the first two cases, the benefit arises from the fact that the optimisation weighting is rebalanced in favour of smaller values. The logarithmic rescaling method seems to work particularly well but runs into problems if the storage or loss moduli are exactly 0 at any frequency, this is due to the negative singularity of the logarithmic function for the 0 argument. 0 arguments can also cause problems using the `local` option due to division by 0.

# ## Predicting

# RHEOS allows the user to simulate the frequency response of a model (with defined parameters, [`RheoModel`](@ref) struct) to an imposed loading.

# Given an incomplete data set (frequency only) and model with values substituted into parameters ([`RheoModel`](@ref)), return a new "complete" dataset based on the model with the simulated missing variable (storage and loss).

# #### Assessing the Quality of Fit

# The ability of predicting model's response is first exploited to assess the quality of the fits above. The incomplete [`RheoFreqData`](@ref) variable is defined by extracting the frequency from the original data or by defining a new frequency vector.

data_ext = onlyfreq(data)
## Alternatively, a RheoFreqData with only frequency data can be generated as
## data_ext = frequencyspec(ω_start = 1.0e-2, ω_end = 1.0e2, logstep = 0.1)

rheofreqdatatype(data_ext)

# For the prediction, RHEOS' function [`dynamicmodelpredict`](@ref) requires the incomplete data set and a model with fixed parameters ([`RheoModel`](@ref)). For the assessment of the fitting quality the [`RheoModel`](@ref) is the output of the fitting function.

fractKV_predict = dynamicmodelpredict(data_ext, FractKV_model)

## Now we can plot data and model together for comparison
using PyPlot
fig, ax = subplots(1, 1, figsize = (7, 5))
ax.loglog(data.ω, data.Gp, "o", markersize = 5, color = "blue")
ax.loglog(data.ω, data.Gpp, "o", markersize = 5, color = "red")
ax.loglog(fractKV_predict.ω, fractKV_predict.Gp, "--", color = "blue")
ax.loglog(fractKV_predict.ω, fractKV_predict.Gpp, "--", color = "red")
ax.set_xlabel("Frequency")
ax.set_ylabel("Storage and Loss moduli")
#!nb fig #hide

# #### Simulate Different Model Behaviours

# The ability of predicting model's response can be exploited to simulate the behaviour of the model to other external loading conditions. For example, we will explore the response of the fitted model to a creep experiment.

# First we need to define an incomplete [`RheoTimeData`](@ref) struct, which can be achieved via the data generation functions provided in RHEOS (see section [Generating Data](@ref)).

## Define timeline
dσ = timeline(t_end = 10)
## and a step in stress
dσ = stressfunction(dσ, hstep())

## we can now predict the creep response of the Maxwell model
FractKV_creepPredict = modelsteppredict(dσ, FractKV_model)
## Visualisation of the simulated response
fig, ax = subplots(1, 1, figsize = (7, 5))
ax.plot(FractKV_creepPredict.t, FractKV_creepPredict.ϵ)
ax.set_xlabel("Time")
ax.set_ylabel("Strain")
#!nb fig #hide

# **Reference frequency data**: Deng, Linhong, et al. "Fast and slow dynamics of the cytoskeleton." Nature materials 5.8 (2006): 636.


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "blis"
version = v"0.8.0"

# Collection of sources required to complete build
sources = [
    GitSource("https://github.com/flame/blis.git", "9b387f6d5a010969727ec583c0cdd067a5274ed8")
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir
cd blis/

for i in ./config/*/*.mk; do

    # Building in container forbids -march options
    sed -i "s/-march[^ ]*//g" $i
    # Building in container forbids unsafe optimization.
    sed -i "s/-ffast-math//g" $i
    sed -i "s/-funsafe-math-optimizations//g" $i

done

case ${target} in

    *"x86_64"*"linux"*)
        export BLI_CONFIG=x86_64
        export BLI_THREAD=openmp
        ;;
    *"x86_64"*"w64"*)
        # MinGW doesn't support savexmm instructions
        # Build only for AMD processors.
        export BLI_CONFIG=amd64
        export BLI_THREAD=openmp
        ;;
    *"x86_64"*"apple"*)
        export BLI_CONFIG=x86_64
        export BLI_THREAD=openmp
        export CC=gcc
        export CXX=g++
        ;;
    *"x86_64"*"freebsd"*)
        export BLI_CONFIG=x86_64
        export BLI_THREAD=openmp
        export CC=gcc
        export CXX=g++
        ;;
    *"aarch64"*"linux"*)
        # Aarch64 has no metaconfiguration support yet.
        # Use Cortex-A57 for the moment.
        export BLI_CONFIG=cortexa57
        export BLI_THREAD=openmp
        ;;
    *"arm"*"linux"*)
        export BLI_CONFIG=cortexa9
        export BLI_THREAD=none
        ;;
    *)
        ;;

esac

./configure -p ${prefix} -t ${BLI_THREAD} ${BLI_CONFIG}
make -j${nproc}
make install

# Rename .dll for Windows targets.
if [[ "${target}" == *"x86_64"*"w64"* ]]; then
    mkdir -p ${prefix}/bin
    mv ${prefix}/lib/libblis.3.dll ${prefix}/bin/libblis.dll
fi
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = [
    Platform("x86_64", "linux"; libc="musl"),
    Platform("armv7l", "linux"; libc="glibc"),
    Platform("x86_64", "windows"),
    Platform("x86_64", "macos"),
    Platform("x86_64", "linux"; libc="glibc"),
    Platform("aarch64", "linux"; libc="glibc"),
    Platform("x86_64", "freebsd")
]

# The products that we will ensure are always built
products = [
    LibraryProduct("libblis", :blis)
]

# Dependencies that must be installed before this package can be built
dependencies = [
    Dependency(PackageSpec(name="CompilerSupportLibraries_jll", uuid="e66e0078-7015-5450-92f7-15fbd957f2ae"))
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; preferred_gcc_version = v"8.1.0")

ext: jl

max_stars_repo_name: Krastanov/Yggdrasil

max_issues_repo_name: Krastanov/Yggdrasil

max_forks_repo_name: Krastanov/Yggdrasil


text:
# ---
# title: 1040. Moving Stones Until Consecutive II
# id: problem1040
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Array, Sliding Window
# link: <https://leetcode.com/problems/moving-stones-until-consecutive-ii/description/>
# hidden: true
# ---
#
# On an **infinite** number line, the position of the i-th stone is given by
# `stones[i]`.  Call a stone an _endpoint stone_ if it has the smallest or
# largest position.
#
# Each turn, you pick up an endpoint stone and move it to an unoccupied position
# so that it is no longer an endpoint stone.
#
# In particular, if the stones are at say, `stones = [1,2,5]`, you **cannot**
# move the endpoint stone at position 5, since moving it to any position (such
# as 0, or 3) will still keep that stone as an endpoint stone.
#
# The game ends when you cannot make any more moves, ie. the stones are in
# consecutive positions.
#
# When the game ends, what is the minimum and maximum number of moves that you
# could have made?  Return the answer as an length 2 array: `answer =
# [minimum_moves, maximum_moves]`
#
#
#
# **Example 1:**
#
#
#
#     Input: [7,4,9]
#     Output: [1,2]
#     Explanation:
#     We can move 4 -> 8 for one move to finish the game.
#     Or, we can move 9 -> 5, 4 -> 6 for two moves to finish the game.
#
#
# **Example 2:**
#
#
#
#     Input: [6,5,4,3,10]
#     Output: [2,3]
#     We can move 3 -> 8 then 10 -> 7 to finish the game.
#     Or, we can move 3 -> 7, 4 -> 8, 5 -> 9 to finish the game.
#     Notice we cannot move 10 -> 2 to finish the game, because that would be an illegal move.
#
#
# **Example 3:**
#
#
#
#     Input: [100,101,104,102,103]
#     Output: [0,0]
#
#
#
# **Note:**
#
#   1. `3 <= stones.length <= 10^4`
#   2. `1 <= stones[i] <= 10^9`
#   3. `stones[i]` have distinct values.
#
#
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
using LinearAlgebra
using SparseArrays
using Plots

"This routine solves the periodic advection-diffusion equation using Forward Euler
in time and finite differences in space."

m = 100 # number of points

# define spatial grid
x = LinRange(-1,1,m+2)
xint = x[1:end-1]
h = x[2]-x[1]

dt = .5*h # timestep
T = 2.0 # final time

# initial condition and forcing
u0(x) = sin(pi*x)
f(x,t) = 0.0

# upwind approximation of first derivative
Q = (1/h) * spdiagm(0=>ones(m+1),-1=>-ones(m))
Q[1,end] = -1/h

u = u0.(xint)
Nsteps = ceil(Int,T/dt)
dt = T / Nsteps

interval = 10
@gif for k = 1:Nsteps
    tk = k*dt
    u .= u - dt * Q*u
    if k % interval==0
        plot(xint,u,linewidth=2,label="Solution")
        plot!(x,u0.(x .- tk),linewidth=2,label="Exact solution",ylims=(-1.0,2.0))
        # error = @. abs(u - u0(xint - tk))
        # plot(xint,error,linewidth=2,label="Error",ylims=(-1e-13,1e-13))
        println("on timestep $k out of $Nsteps.")
    end
end every interval

plot(xint,u,linewidth=2,label="Solution")
plot!(x,u0.(x .- T),linewidth=2,label="Exact solution",ylims=(-1.0,2.0))


text:
"""
	grdfill(cmd0::String="", arg1=nothing, kwargs...)

Reads a grid that presumably has unfilled holes that the user wants to fill in some fashion.
Holes are identified by NaN values but this criteria can be changed.

Full option list at [`grdfill`]($(GMTdoc)grdfill.html)

Parameters
----------

- **A** | **algo** :: [Type => Str]		``Arg = mode[arg]``

    Specify the hole-filling algorithm to use. Choose from c for constant fill and append the constant value,
    n for nearest neighbor (and optionally append a search radius in pixels).
    ($(GMTdoc)grdfill.html#a)
- **G** | **outgrid** :: [Type => Str]

    Output grid file name. Note that this is optional and to be used only when saving
    the result directly on disk. Otherwise, just use the G = grdfill(....) form.
    ($(GMTdoc)grdfill.html#g)
- **L** | **list** :: [Type => Str]	``Arg = [p]``

    Just list the rectangular subregions west east south north of each hole. No grid fill takes place and
    **outgrid** is ignored. Optionally, append **p** to instead write closed polygons for all subregions.
- **N** | **nodata** :: [Type => Str]	``Arg = nodata``

    Sets the node value that identifies a point as a member of a hole [Default is NaN].
    ($(GMTdoc)grdfill.html#n)
- $(GMT.opt_R)
- $(GMT.opt_V)
- $(GMT.opt_f)
"""
function grdfill(cmd0::String="", arg1=nothing; kwargs...)

	length(kwargs) == 0 && return monolitic("grdfill", cmd0, arg1)

	d = KW(kwargs)
	cmd = parse_common_opts(d, "", [:R :V_params :f :params])
	cmd = parse_these_opts(cmd, d, [[:A :algo], [:G :outgrid], [:L :list], [:N :nodata]])

	common_grd(d, cmd0, cmd, "grdfill ", arg1)		# Finish build cmd and run it
end

# ---------------------------------------------------------------------------------------------------
grdfill(arg1, cmd0::String=""; kw...) = grdfill(cmd0, arg1; kw...)


text:
## the learning algorithm: PLS1 - single target
function trainer(model::PLS1Model{T},
                 X::AbstractArray{T}, Y::Vector{T}) where T<:AbstractFloat

    W,b,P  = model.W,model.b,model.P
    nfactors = model.nfactors
    for i = 1:nfactors
        W[:,i] = X'Y
        W[:,i] /= norm(W[:,i])#sqrt.(W[:,i]'*W[:,i])
        R      = X*W[:,i]
        Rn     = R'/(R'R) # change to use function...
        P[:,i] = Rn*X
        b[i]   = Rn * Y
        if abs(b[i]) <= 1e-3
           print("PLS1 converged. No need learning with more than $(i) factors")
           model.nfactors = i
           break
        end
        X      = X - R * P[:,i]'
        Y      = Y - R * b[i]
    end

    return model
end

function predictor(model::PLS1Model{T},
                   X::AbstractArray{T}) where T<:AbstractFloat

    W,b,P    = model.W,model.b,model.P
    nfactors = model.nfactors
    nrows    = size(X,1)
    Y = zeros(T,nrows)

    for i = 1:nfactors
        R      = X*W[:,i]
        Y      = Y + R*b[i]
        X      = X - R*P[:,i]'
    end

    return Y
end


text:
function toDict(diag::Diagram; maxdepth::Int)
    @assert maxdepth == 1 "deep convert has not yet been implemented!"

    d = Dict{Symbol,Any}()
    d[:hash] = diag.hash
    d[:id] = diag.id
    d[:name] = diag.name
    d[:diagram] = diag
    d[:subdiagram] = Tuple(d.hash for d in diag.subdiagram)
    d[:operator] = diag.operator
    d[:factor] = diag.factor
    d[:weight] = diag.weight

    return d
end

function toDict(v::DiagramId)
    d = Dict{Symbol,Any}()
    for field in fieldnames(typeof(v))
        data = getproperty(v, field)
        #DataFrame will expand a vector into multiple rows. To prevent it, we transform all vectors into tuples
        d[field] = data isa AbstractVector ? Tuple(data) : data
    end
    return d
end

# function toDataFrame(diagVec::AbstractVector; expand::Bool = false)

# end

function toDataFrame(diagVec::AbstractVector; expand::Bool = false, maxdepth::Int = 1)
    vec_of_diag_dict = [toDict(d, maxdepth = maxdepth) for d in diagVec]
    vec_of_id_dict = [toDict(d.id) for d in diagVec]
    names = Set(reduce(union, keys(d) for d in vec_of_diag_dict))
    if expand
        idnames = Set(reduce(union, keys(d) for d in vec_of_id_dict))
        @assert isempty(intersect(names, idnames)) "collision of diagram names $names and id names $idnames"
        names = union(names, idnames)

        for (di, d) in enumerate(vec_of_diag_dict)
            merge!(d, vec_of_id_dict[di]) #add id dict into the diagram dict
        end
    end
    # println(names)
    df = DataFrame([name => [] for name in names])

    for dict in vec_of_diag_dict
        append!(df, dict, cols = :union)
    end
    return df
end

function _summary(diag::Diagram{W}, color = true) where {W}

    function short(factor, ignore = nothing)
        if isnothing(ignore) == false && applicable(isapprox, factor, ignore) && factor ≈ ignore
            return ""
        end
        str = "$(factor)"
        if factor isa Float64
            return length(str) <= 4 ? str : @sprintf("%6.3e", factor)
        elseif factor isa Vector{Float64}
            return length(str) <= 4 ? str : reduce(*, [@sprintf("%6.3e", f) for f in factor])
        else
            return str
        end
    end

    namestr = diag.name == :none ? "" : "$(diag.name) "
    idstr = "$namestr$(diag.id)"
    fstr = short(diag.factor, one(diag.factor))
    wstr = short(diag.weight)
    # =$(node.weight*(2π)^(3*node.id.para.innerLoopNum))

    if length(diag.subdiagram) == 0
        return isempty(fstr) ? "$idstr=$wstr" : "$(idstr)⋅$(fstr)=$wstr"
    else
        return "$idstr=$wstr=$fstr$(diag.operator) "
    end
end

function Base.show(io::IO, diag::Diagram)
    if length(diag.subdiagram) == 0
        typestr = ""
    else
        subdiag = prod(["$(d.hash), " for d in diag.subdiagram[1:end-1]])
        subdiag *= "$(diag.subdiagram[end].hash)"
        typestr = "($subdiag)"
    end
    print(io, "$(_summary(diag, true))$typestr")
end

"""
    function plot_tree(diag::Diagram; verbose = 0, maxdepth = 6)

    Visualize the diagram tree using ete3 python package

#Arguments
- `diag`        : the Diagram struct to visualize
- `verbose=0`   : the amount of information to show
- `maxdepth=6`  : deepest level of the diagram tree to show
"""
function plot_tree(diag::Diagram; verbose = 0, maxdepth = 6)

    # pushfirst!(PyVector(pyimport("sys")."path"), @__DIR__) #comment this line if no need to load local python module
    ete = PyCall.pyimport("ete3")

    function treeview(node, level, t = ete.Tree(name = " "))
        if level > maxdepth
            return
        end
        nt = t.add_child(name = "$(node.hash): $(_summary(node, false))")

        if length(node.subdiagram) > 0
            name_face = ete.TextFace(nt.name, fgcolor = "black", fsize = 10)
            nt.add_face(name_face, column = 0, position = "branch-top")
            for child in node.subdiagram
                treeview(child, level + 1, nt)
            end
        end

        return t
    end

    t = treeview(diag, 1)
    # style = ete.NodeStyle()
    # style["bgcolor"] = "Khaki"
    # t.set_style(style)

    ts = ete.TreeStyle()
    ts.show_leaf_name = true
    # ts.show_leaf_name = True
    # ts.layout_fn = my_layout
    ####### show tree vertically ############
    # ts.rotation = 90 #show tree vertically

    ####### show tree in an arc  #############
    # ts.mode = "c"
    # ts.arc_start = -180
    # ts.arc_span = 180
    # t.write(outfile="/home/kun/test.txt", format=8)
    t.show(tree_style = ts)
end
function plot_tree(diags::Vector{Diagram{W}}; kwargs...) where {W}
    for diag in diags
        plot_tree(diag; kwargs...)
    end
end
function plot_tree(df::DataFrame; kwargs...)
    plot_tree(df.diagram; kwargs...)
end


text:
using StatsBase
using Flux
using Zygote
using ReinforcementLearning

export Field, empty, cross, naught, TTTEnv, reset!, state, is_win, is_draw,
    is_done, TTTLearner, find_boards, train!, play_game, pp, eval_learner,
    play_against, play_and_learn, NNTTTLearner

abstract type AbstractTTTLearner <: AbstractLearner end

# First, let's get some data structures set up. A board is a $3 \times 3$ matrix with each field being either empty, or occupied by a cross or a naught. Let's define a Field as being either empty, having a cross or having a naught.
@enum Field empty cross naught
Board = Array{Field, 2}

# Let's give ourselves a utility-function to switch players.
function other_player(p::Field)
    @assert p != empty
    if p == cross
        return naught
    else
        return cross
    end
end

# In the above terminology the state-space is the space of all legal tic tac toe board configurations. The following function discovers every such board given a base-case of a board filled with `empty` and the starting player.
function find_boards(board::Board, player::Field)
    boards = Vector{}()
    append!(boards, [board])
    for i in 1:3
        for j in 1:3
            if board[i, j] == empty
                b = copy(board)
                b[i, j] = player
                push!(boards, b)
                for bb in find_boards(b, other_player(player))
                    if ! (bb in boards)
                        push!(boards, bb)
                    end
                end
            end
        end
    end
    return boards
end

# The action space is all coordinates $\{(i, j) | 1 \leq i, j \leq 3\}$. Julia has no way to enforce the size of the coordinates, so an action is simply a tuple (i, j) in Julia called a cartesian 2-index, and we'll take care to only construct valid coordinates in the rest of the code.
Action = CartesianIndex{2}

# Now, let's make a type for the environment. Notice, that this doesn't strictly follow the definition of an environment. I hope you will see later, that this style is actually very natural. We hold the important data in a tuple and define functions elsewhere to extract permissible actions, rewards termination, as well as an update function. This allows us to keep the interface decoupled from the representation of our data. Thus, if we need to change for instance the representation of a board, say for efficiency reasons, we can do so and only have to update a few functions instead of every place we use the data.
mutable struct TTTEnv
    state::Board
    reward::Float64
    done::Bool
end

TTTEnv() = TTTEnv(fill(empty, (3, 3)), 0.0, false)

# Now, let's define our functions. Notice that our functions now take an environment as first argument instead of a state. This has no significant consequences, it just makes for better organized code.
state(env::TTTEnv) = env.state
action_mask(env::TTTEnv) = vec([CartesianIndex(i, j) for i in 1:3 , j in 1:3])
reward(env::TTTEnv) = env.reward
terminated(env::TTTEnv) = env.done

# We'll wait a bit to define the update function, as we need some auxillary functions first. First, we need to figure out if we've won, lost or is in a draw.
function is_win(state::Board, player::Field)
    b = state .== player
    @inbounds begin
        b[1, 1] & b[1, 2] & b[1, 3] ||
            b[2, 1] & b[2, 2] & b[2, 3] ||
            b[3, 1] & b[3, 2] & b[3, 3] ||
            b[1, 1] & b[2, 1] & b[3, 1] ||
            b[1, 2] & b[2, 2] & b[3, 2] ||
            b[1, 3] & b[2, 3] & b[3, 3] ||
            b[1, 1] & b[2, 2] & b[3, 3] ||
            b[1, 3] & b[2, 2] & b[3, 1]
    end
end
function is_draw(state::Board)
    is_full = reduce(&, .! (state .== empty))
    has_winner = is_win(state, naught) || is_win(state, cross)
    return is_full && (! has_winner)
end
function is_done(state::Board)
    return is_draw(state) || is_win(state, cross) || is_win(state, naught)
end

# Now, we're ready to define $\epsilon$. We won't call it $\epsilon$, as it is morally an evaluation-function, so we'll say $env(action) = \epsilon(state(env), action)$.
function (env::TTTEnv)(a::Action)
# First we make our move
    if state(env)[a] != empty
        println(state(env), a)
    end

    @assert state(env)[a] == empty
    state(env)[a] = cross
# Then we check if the game is done.

    if is_win(state(env), cross)
        env.done = true
        env.reward = win_rew
        return
    elseif is_draw(state(env))
        env.done = true
        env.reward = draw_rew
        return
    end
# Now, let naught make a random move.
    naught_move = rand(findall(state(env) .== empty))
    env.state[naught_move] = naught
# Check if the game is done.
    if is_win(state(env), naught)
        env.done = true
        env.reward = loss_rew
    elseif is_draw(state(env))
        env.done = true
        env.reward = draw_rew
    end
end
# Notice that we havent' specified the rewards for winning, losing or getting a draw. This is because these are so-called hyper-parameters that we need to tweak in order for the learning to work properly. We'll discuss this later. For now, the important part is that a reward > 1 will reinforce the behaviour that lead to this reward and a reward < 1 will punish it. Be aware that this is not intrinsic to reinforcement learning, it just makes our naive training process easier. You'll see.

win_rew = 1.2
draw_rew = 0.2
loss_rew = 0.1

# That's our update function. This is usually where the domain-knowledge about the problem lives. If we navigate a physical space, this contains the physics simulation. In this case we simulate an opponent.
#
# Before we move on, let's just define a simple function to reset an evnironment before a new game.
function reset!(env::TTTEnv)
    env.state = fill(empty, (3, 3))
    env.reward = 0
    env.done = false
end

# That was the environment. It wasn't too bad, was it? Now, we're getting into the agent or learner and this is where the magic happens. First, I have to decide how to model what I've learned. For a very simple first model, let's just keep track of every possible board and try to learn what the best move is in each. Since tic tac toe isn't that big of a game, this is a feasible strategy. To facilitate learning, we'll keep track of how likely we think it is that each field is the best one to pick. This would look something like this:

#=
# +-----------+           +--------------------+
# | x |   | o |           | 0.00 | 0.03 | 0.00 |
# | x |   | o |   |---->  | 0.00 | 0.05 | 0.00 |
# |   |   |   |           | 0.50 | 0.02 | 0.40 |
# +-----------+           +--------------------+
=#

# This picture would tell us that bottom left corner is probably the best choice, but bottom right is probably also good.
#
# The idea is to try some games, figure out what works and what doesn't and then update the probabilities somehow. With that in mind, let's define an agent to simply be such a map from states to probabilities:
mutable struct TTTLearner <: AbstractTTTLearner
    model::Dict{Board, Array{Float64, 2}}
end

# Now, we need to populate this map with some initial values. To do this, I've made this little function that uses the statespace-function from before and initializes everything with random probabilities.
function TTTLearner()
    boards = find_boards(fill(empty, (3, 3)), cross)
    model = Dict{Board, Array{Float64, 2}}()
    for b in boards
        is = findall(b .== empty)
        # We just write a random number to every empty position and then normalize such that the sum of the probabilities is one.
        weights = fill(1, length(is))
        weights = Base.:/.(weights, sum(weights))
        value = fill(0.0, (3, 3))
        value[is] = weights
        model[b] = value
    end
    return TTTLearner(model)
end

# Let's give life to our agent by giving it a will of it's own. To take an action, we simply sample from the positions with weights given by the learned probabilities. Notice that we naively sample from all positions, but that the probability of choosing non-empty fields will always be zero.
(learner::TTTLearner)(env::TTTEnv) = learner(state(env))
function (learner::TTTLearner)(state::Board)
    values = vec([CartesianIndex(i, j) for i in 1:3 , j in 1:3])
    probs = vec(learner.model[state])
    w = Weights(probs)
    return sample(values, w)
end

# Finally, the magic part. This is where the learning takes place after we playes through a game. There's a bit going on here, so let's work through it. The parameters for the function are:
# - A learner to train
# - A trajectory. This is lingo. It's just a record of every state we went through in a game
# - Actions holds which actions we took at each board in the trajectory
# - A reward, telling us if the game was good or bad
#
# The idea is the following: if we won, every step we took must have been at least somewhat good. Thus, we can multiply the probability that the action we took was good by the reward and renormalize the probabilities. Similarly is we loose, we multiply every action we took by the reward to learn that those were bad moves. This is where it's important to choose our rewards to be grater than 1 if we won and less than 1 if we lost.
function update!(learner::TTTLearner,
                 trajectory::Vector{Board},
                 actions::Vector{Action},
                 reward::Float64)
    @assert length(trajectory) == length(actions)
    for i in 1:(length(trajectory) - 1)
        b = trajectory[i]
        a = actions[i]
        learner.model[b][a] *= reward

        learner.model[b] /= sum(learner.model[b])
    end
end

# Let's talk a bit about the choice of rewards. As you can see, any reward grater than 1 will encourage the behaviour that lead to that reward and a reward less than 1 will discourage it.

# That's the setup. All there's left to do is to play some games:
function play_game(env::TTTEnv, learner::AbstractTTTLearner)
    trajectory = Vector{Board}()
    actions = Vector{Action}()
    reset!(env)
    while ! env.done
        action = learner(env)
        push!(trajectory, copy(state(env)))
        push!(actions, action)
        env(action)
    end
    return (trajectory, actions)
end
# Most of this function is book-keeping to build the trajectory. Actually, a single round of a game can be expressed as simply `env(learner(env))`.

# To make things easier for ourselves, we'll create a function to play lots of games in a row and learn from them. By default, we play 1 000 000 games at a time.
function train!(learner::AbstractTTTLearner, env::TTTEnv ; n=1_000_000)
    for _ in 1:n
        trajectory, actions = play_game(env, learner)
        update!(learner, trajectory, actions, reward(env))
    end
end

# We want to see if our model has learned something. This function plays 1000 games and records the number of losses, draw and wins.
function eval_learner(learner::AbstractTTTLearner, env::TTTEnv ; n=1000)
    rewards = Vector{Float64}()
    for _ in 1:n
        play_game(env, learner)
        push!(rewards, reward(env))
    end
    losses = count(rewards .== -2)
    draws = count(rewards .== 1)
    wins = count(rewards .== 2)
    return Base.:/(sum(rewards), n), losses, draws, wins
end

# That's it, that's all it takes to teach a computer how to play tic tac toe. Just for fun, let's write a function to play againt it:

function play_against(learner::AbstractTTTLearner)
    board = fill(empty, (3, 3))
    trajectory = Vector{Board}()
    while ! is_done(board)
        board[learner(board)] = cross
        pp(board)
        if is_done(board)
            break
        end
        println("What's your move? ")
        s = readline()
        coords = split(s, ' ', keepempty = false)
        i = parse(Int, coords[1])
        j = parse(Int, coords[2])
        board[i, j] = naught
        push!(trajectory, copy(board))
    end
    return trajectory
end

function play_and_learn(learner::TTTLearner)
    trajectory = Vector{Board}()
    actions = Vector{Action}()
    board = fill(empty, (3, 3))
    while ! is_done(board)
        action = learner(board)
        push!(trajectory, copy(board))
        push!(actions, action)
        board[action] = cross
        pp(board)
        if is_done(board)
            break
        end
        println("What's your move? ")
        s = readline()
        coords = split(s, ' ', keepempty = false)
        i = parse(Int, coords[1])
        j = parse(Int, coords[2])
        board[i, j] = naught
    end
    reward = draw_rew
    if is_win(board, cross)
        reward = win_rew
    elseif is_win(board, naught)
        reward = loss_rew
    end

    update!(learner, trajectory, actions, reward)
end

# Aaand that's it. After just 1 000 000 games our agent have learned to never lose. If you try to play against it you will notice some curious behaviour from the agent. Try letting it have some wins. Notice, that it often won't go directly for the win and instead secure more ways to win. This is not a problem, since it's going to win either way, and we never told it to win quickly. This teaches us a valuable lesson: we often don't know what behaviour we're encuraging until we see the result of the learning.

# That was fun. But a little unsatisfactory. This solution doesn't scale very well to larger problems or even unbounded problems. The bottleneck is our model. Keeping a complete mapping between states and actions just isn't very practical when the statespace grows. To handle more complex situations, we need something else. It's time to introduce deep neural networks. *fanfare*.
#
# Hopefully you know what neural networks are and have at least a basic understanding of how they operate. The neat thing is, that we can reuse most of our machinery from before, we just have to create a new kind of agent. So let's do that:

mutable struct NNTTTLearner{Q} <: AbstractTTTLearner
    model :: Q
end

function NNTTTLearner()
    NNTTTLearner(NeuralNetworkApproximator(Chain(Dense(9, 128, relu), Dense(128, 32, relu), Dense(32, 9)), ADAM()))
end

function board2ints(board::Board)
    state = fill(0, (3, 3))
    state += map(x -> (x == cross) ? 1 : 0, board)
    state += map(x -> (x == naught) ? -1 : 0, board)
    return state
end

(learner::NNTTTLearner)(env::TTTEnv) = learner(state(env))
function (learner::NNTTTLearner)(board::Board)

    @assert any(empty .== board)

    state = board2ints(board)

    values = learner.model(vec(state))
    valid_actions = findall(vec(board) .== empty)
    index = findfirst((values .== maximum(values[valid_actions])) .& (vec(board) .== empty))
    action = findfirst(reshape(1:9, (3, 3)) .== index)

    if board[action] != empty
        println(action, values)
    end

    @assert board[action] == empty

    return action
end

function update!(learner::NNTTTLearner,
                 trajectory::Vector{Board},
                 actions::Vector{Action},
                 reward::Float64)
    Q = learner.model
    s = map(vec, board2ints.(trajectory))
    indices = reshape(1:9, (3, 3))
    a = map(x -> indices[x], actions)
    gs = gradient(params(Q)) do
        # Zygote.ignore() do
        #     println(a)
        #     println(s)
        # end

        q = [Q(s[i])[a[i]] for i in 1:length(s)]
        q_ = vcat([maximum(Q(s[i])) for i in 1:(length(s)-1)], [reward])
        # q_ = vcat(map(maximum, Q.(s[1:end-1])), [reward])
        # Zygote.ignore() do
        #     println(q)
        #     println(q_)
        # end

        loss = Flux.huber_loss(q, q_)

        return loss
    end
    RLBase.update!(Q, gs)
end

function pp(board::Board)
    function to_text(f::Field)
        if f == empty
            "."
        elseif f == cross
            "x"
        else
            "o"
        end
    end
    display(map(to_text, board))
end


text:
RpcApi.SimpleOrganism[RpcApi.SimpleOrganism(0x0000000000003850,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::String="vKfNYGka",var_10::Int8=-111)
            Creature.eatRight(c,o,Int(var_10))
            return var_9
        end
        function func_8(var_6::Int16=-28670,var_7::Int8=-36)
            o.mem[var_6] = var_6
            o.mem[var_6] = var_6
            Creature.eatUp(c,o,Int(var_7))
            var_7 = var_7 + var_7
            return var_6
        end
        local var_1::Int8 = 119
        function func_4(var_2::Int64=7723196059316421323,var_3::Bool=true)
            local var_5::Int64 = 128453109842673326
            Creature.stepRight(o)
            Creature.stepLeft(o)
            if var_3 == var_3
            end
            var_3 = var_3 | var_3
            Creature.stepDown(o)
            return var_2
        end
        function func_13(var_12::String="MTC7XcIE")
            return var_12
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepLeft(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,916,1,171103,0x0000003a,Dict{Int16,Int16}(),[124,183],23),RpcApi.SimpleOrganism(0x00000000000040c7,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_7::Int64=4047437063232144020)
            local var_12::Int16 = 14722
            local var_10::Bool = true
            if var_12 != var_12
                Creature.stepLeft(o)
                Creature.stepLeft(o)
            end
            if var_7 >= var_7
            end
            var_10 = Bool(abs(var_10 - var_10))
            return var_7
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_6::Int64 = 2108313727829142897
            local var_5::Int64 = 3293689547575976404
            local var_4::Int16 = -14134
            Creature.stepLeft(o)
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            var_5 = var_5 / var_6
            if var_4 != var_4
                Creature.stepUp(o)
            end
            Creature.stepDown(o)
            var_4 = var_4 / var_4
            Creature.stepRight(o)
            var_4 = var_4 * var_4
            var_4 = var_4 / var_4
            var_4 = var_4 * var_4
            Creature.stepUp(o)
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepLeft(o)
            Creature.stepRight(o)
        end
        Creature.stepLeft(o)
        Creature.stepDown(o)
        Creature.stepLeft(o)
        var_1 = var_1 / var_1
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepUp(o)
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepDown(o)
        return true
    end),[100,300,30,0,1,1,1],23,919,1,159486,0x000000c0,Dict{Int16,Int16}(),[125,181],44),RpcApi.SimpleOrganism(0x0000000000003972,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::String="vKfNYGka",var_10::Int8=-111)
            Creature.stepRight(o)
            return var_9
        end
        function func_8(var_6::Int16=-28670,var_7::Int8=-36)
            for i::Int8 = 1:div(var_7,_LOOP_STEPS_DIVIDER)
            end
            o.mem[var_6] = var_6
            Creature.eatUp(c,o,Int(var_7))
            return var_6
        end
        local var_1::Int8 = 119
        function func_4(var_2::Int64=7723196059316421323,var_3::Bool=true)
            local var_5::Int64 = 128453109842673326
            Creature.stepRight(o)
            Creature.stepLeft(o)
            if var_5 != var_2
            end
            Creature.stepDown(o)
            var_3 = var_3 | var_3
            return var_2
        end
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,1000,1,147212,0x00000037,Dict{Int16,Int16}(),[52,183],19),RpcApi.SimpleOrganism(0x0000000000003d92,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_7::Int8 = -48
        local var_5::Int16 = 23602
        function func_4(var_2::Bool=true,var_3::Int64=420931049847432977)
            Creature.stepUp(o)
            Creature.stepDown(o)
            if var_3 === var_3
                Creature.stepRight(o)
            end
            Creature.stepRight(o)
            Creature.stepRight(o)
            var_3 = var_3 / var_3
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        if var_5 === var_5
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],6,1000,1,118819,0x00000033,Dict{Int16,Int16}(),[236,162],23),RpcApi.SimpleOrganism(0x0000000000003d98,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_7::Int64=4047437063232144020)
            Creature.stepLeft(o)
            return var_7
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_6::Int64 = 2108313727829142897
            local var_5::Int64 = 3293689547575976404
            local var_4::Int16 = -14134
            o.mem[var_4] = var_4
            var_5 = var_2 - var_5
            var_5 = var_5 / var_6
            Creature.stepLeft(o)
            var_4 = var_4 / var_4
            Creature.stepUp(o)
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.stepLeft(o)
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepDown(o)
        return true
    end),[100,300,30,0,1,1,1],23,919,1,118470,0x0000008c,Dict{Int16,Int16}(),[119,180],28),RpcApi.SimpleOrganism(0x0000000000003ff4,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_12(var_10::Bool=true,var_11::Int64=2717859465029702693)
            return var_10
        end
        function func_9(var_7::Int8=-117,var_8::Int8=30)
            Creature.stepLeft(o)
            Creature.eatLeft(c,o,Int(var_7))
            Creature.stepLeft(o)
            Creature.eatLeft(c,o,Int(var_7))
            Creature.stepDown(o)
            Creature.stepDown(o)
            return var_7
        end
        local var_4::Bool = false
        local var_2::Int16 = 15131
        local var_1::Int8 = 119
        var_2 = if haskey(o.mem,var_2)
                o.mem[var_2]
            else
                var_2
            end
        Creature.stepLeft(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepLeft(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepRight(o)
        end
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],13,1000,1,116618,0x00000053,Dict{Int16,Int16}(),[255,26],24),RpcApi.SimpleOrganism(0x0000000000003eb3,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_7::Int16=30371)
            var_7 = var_7 / var_7
            return var_7
        end
        function func_4(var_2::Bool=true,var_3::Int64=420931049847432977)
            local var_6::Int16 = 28952
            return var_2
        end
        local var_1::Int8 = 119
        Creature.stepRight(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        if var_1 !== var_1
        end
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        var_1 = var_1 / var_1
        return true
    end),[100,300,30,0,1,1,1],6,1000,1,114517,0x00000028,Dict{Int16,Int16}(),[212,126],16),RpcApi.SimpleOrganism(0x0000000000003d9d,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::String="vKfNYGka",var_10::Int8=-111)
            Creature.eatRight(c,o,Int(var_10))
            return var_9
        end
        function func_8(var_6::Int16=-28670,var_7::Int8=-36)
            Creature.stepRight(o)
            o.mem[var_6] = var_6
            Creature.eatUp(c,o,Int(var_7))
            Creature.eatDown(c,o,Int(var_7))
            return var_6
        end
        local var_1::Int8 = 119
        function func_4(var_2::Int64=7723196059316421323,var_3::Bool=true)
            var_2 = var_2 * var_2
            if var_3 == var_3
            end
            var_3 = var_3 | var_3
            Creature.stepDown(o)
            return var_2
        end
        function func_13(var_12::String="MTC7XcIE")
            return var_12
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,329,1,111604,0x0000003e,Dict{Int16,Int16}(),[52,183],19),RpcApi.SimpleOrganism(0x0000000000004136,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_7(var_5::Int16=4444,var_6::Bool=true)
            local var_8::Int64 = 171829713795169641
            var_6 = var_6 & var_6
            Creature.stepLeft(o)
            var_5 = var_5 * var_5
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_4::Int16 = -14134
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            Creature.stepRight(o)
            var_4 = var_4 * var_4
            Creature.stepLeft(o)
            Creature.stepRight(o)
            Creature.stepUp(o)
            o.mem[var_4] = var_4
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepDown(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,110903,0x0000005c,Dict{Int16,Int16}(),[56,156],24),RpcApi.SimpleOrganism(0x0000000000004169,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_7::Int16 = -2042
        local var_6::String = "NGDcicFq"
        function func_3(var_2::Int64=-235958945428180621)
            local var_5::Int16 = -25067
            local var_4::Int16 = -14134
            var_5 = var_5 / var_5
            var_5 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_5
                end
            Creature.stepDown(o)
            Creature.stepLeft(o)
            var_4 = if haskey(o.mem,var_5)
                    o.mem[var_5]
                else
                    var_4
                end
            var_2 = var_2 / var_2
            Creature.stepUp(o)
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            local var_8::Int8 = -45
            local var_9::Int8 = -103
            Creature.eatRight(c,o,Int(var_8))
        end
        Creature.stepLeft(o)
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.stepUp(o)
        return true
    end),[100,300,30,0,1,1,1],7,188,1,110706,0x00000067,Dict{Int16,Int16}(),[182,165],27),RpcApi.SimpleOrganism(0x0000000000001579,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_6(var_4::Int64=7888333340625545254,var_5::Int8=9)
            local var_9::Int8 = 30
            local var_8::Bool = true
            local var_7::Int8 = -1
            Creature.stepDown(o)
            Creature.stepUp(o)
            var_8 = var_8 & var_8
            Creature.eatRight(c,o,Int(var_5))
            return var_4
        end
        local var_3::Int8 = 118
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatUp(c,o,Int(var_1))
        var_1 = var_3 / var_1
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,1000,1,110180,0x0000003e,Dict{Int16,Int16}(),[151,4],17),RpcApi.SimpleOrganism(0x0000000000003ddf,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_4(var_2::Bool=true,var_3::Int64=420931049847432977)
            Creature.stepLeft(o)
            Creature.stepDown(o)
            Creature.stepLeft(o)
            var_3 = var_3 * var_3
            if var_3 >= var_3
                Creature.stepUp(o)
            end
            var_3 = var_3 - var_3
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepDown(o)
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepDown(o)
        Creature.stepLeft(o)
        return true
    end),[100,300,30,0,1,1,1],6,1000,1,108633,0x00000039,Dict{Int16,Int16}(),[141,14],21),RpcApi.SimpleOrganism(0x00000000000028e9,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_3(var_2::Int64=-235958945428180621)
            local var_6::Bool = true
            Creature.stepUp(o)
            var_2 = var_2 / var_2
            Creature.stepRight(o)
            Creature.stepUp(o)
            o.mem[var_4] = var_4
            if var_2 <= var_2
                local var_5::String = "r5GLiC36"
            end
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepUp(o)
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,107854,0x00000038,Dict{Int16,Int16}(),[109,20],22),RpcApi.SimpleOrganism(0x0000000000003ebb,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_7::String = "C2vYsAN5"
        function func_3(var_2::Int64=-235958945428180621)
            local var_6::Int64 = 2108313727829142897
            local var_4::Int16 = -14134
            Creature.stepLeft(o)
            var_4 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_4
                end
            var_4 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_4
                end
            Creature.stepLeft(o)
            Creature.stepLeft(o)
            var_5 = var_5 / var_6
            var_4 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_4
                end
            o.mem[var_4] = var_4
            var_4 = var_4 / var_4
            var_4 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_4
                end
            var_6 = var_6 / var_6
            return var_2
        end
        local var_1::Int8 = 119
        Creature.stepUp(o)
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],23,290,1,103062,0x00000080,Dict{Int16,Int16}(),[114,64],26),RpcApi.SimpleOrganism(0x0000000000003d93,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_3(var_2::Int64=-235958945428180621)
            local var_6::Bool = true
            Creature.stepUp(o)
            Creature.stepUp(o)
            var_2 = var_2 / var_2
            Creature.stepRight(o)
            o.mem[var_4] = var_4
            if var_2 <= var_2
                local var_5::String = "r5GLiC36"
                Creature.stepUp(o)
            end
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepRight(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepUp(o)
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepLeft(o)
            Creature.stepUp(o)
        end
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,96791,0x0000003e,Dict{Int16,Int16}(),[28,135],25),RpcApi.SimpleOrganism(0x0000000000003d64,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::Int64=-6376931539806068851,var_10::Int64=-527738452351850021)
            return var_9
        end
        function func_7(var_5::Int16=-14433,var_6::Bool=true)
            local var_8::String = "Mi2ZEs3w"
            Creature.stepUp(o)
            Creature.stepLeft(o)
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_13::Int64 = 4823497939271164860
            local var_4::Int16 = -14134
            local var_12::Int64 = -8561306439954911482
            var_13 = var_13 * var_2
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        if var_1 > var_1
            local var_14::Int64 = 3798861155764565572
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,96633,0x0000004c,Dict{Int16,Int16}(),[135,119],21),RpcApi.SimpleOrganism(0x0000000000003e27,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::Int64=-6376931539806068851,var_10::Int64=-527738452351850021)
            return var_9
        end
        function func_7(var_5::Int16=-14433,var_6::Bool=true)
            local var_8::String = "Mi2ZEs3w"
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_13::Int64 = 4823497939271164860
            local var_4::Int16 = -14134
            local var_12::Int64 = -8561306439954911482
            var_13 = var_13 * var_2
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        if var_1 > var_1
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,96371,0x00000047,Dict{Int16,Int16}(),[133,199],18),RpcApi.SimpleOrganism(0x00000000000039c6,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_8::String = "GFihWb3t"
        function func_7(var_5::Int16=-13455,var_6::Int64=446029297656619057)
            Creature.stepLeft(o)
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            Creature.stepUp(o)
            var_2 = var_2 + var_2
            Creature.stepLeft(o)
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatUp(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepDown(o)
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,227,1,95696,0x00000057,Dict{Int16,Int16}(),[17,75],17),RpcApi.SimpleOrganism(0x0000000000004127,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::String="vKfNYGka",var_10::Int8=-111)
            return var_9
        end
        function func_8(var_6::Int16=-28670,var_7::Int8=-36)
            for i::Int8 = 1:div(var_7,_LOOP_STEPS_DIVIDER)
            end
            var_6 = var_6 * var_6
            o.mem[var_6] = var_6
            Creature.eatUp(c,o,Int(var_7))
            return var_6
        end
        local var_1::Int8 = 119
        function func_4(var_2::Int64=7723196059316421323,var_3::Bool=true)
            local var_5::Int64 = 128453109842673326
            Creature.stepRight(o)
            Creature.stepLeft(o)
            if var_3 == var_3
            end
            Creature.stepDown(o)
            Creature.stepDown(o)
            return var_2
        end
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,1000,1,93771,0x00000034,Dict{Int16,Int16}(),[54,43],20),RpcApi.SimpleOrganism(0x0000000000003f3b,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_7::Int16=30371)
            o.mem[var_7] = var_7
            Creature.stepLeft(o)
            Creature.stepRight(o)
            Creature.stepLeft(o)
            return var_7
        end
        local var_1::Int8 = 119
        local var_9::Int64 = 1719515173810751484
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepDown(o)
        Creature.eatDown(c,o,Int(var_1))
        var_1 = var_1 / var_1
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],6,1000,1,92353,0x00000036,Dict{Int16,Int16}(),[118,59],18),RpcApi.SimpleOrganism(0x0000000000003ddb,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_1::Int8 = 119
        function func_4(var_2::Int64=-7602132272464706182,var_3::Int16=32402)
            var_3 = if haskey(o.mem,var_3)
                    o.mem[var_3]
                else
                    var_3
                end
            Creature.stepLeft(o)
            o.mem[var_3] = var_3
            var_3 = if haskey(o.mem,var_3)
                    o.mem[var_3]
                else
                    var_3
                end
            var_2 = Int(Creature.getEnergy(o,Int(var_3),Int(var_3)))
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_3),Int(var_3)))
            Creature.stepLeft(o)
            return var_2
        end
        local var_5::Bool = false
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        return true
    end),[100,300,30,0,1,1,1],0,1000,1,91297,0x00000050,Dict{Int16,Int16}(),[108,123],19),RpcApi.SimpleOrganism(0x0000000000003fe5,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_1::Int8 = 119
        function func_3(var_2::Bool=false)
            Creature.stepLeft(o)
            var_2 = var_2 * var_2
            return var_2
        end
        Creature.stepLeft(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.stepLeft(o)
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],4,1000,1,90032,0x00000055,Dict{Int16,Int16}(),[251,70],16),RpcApi.SimpleOrganism(0x0000000000003fb8,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_13::Bool = true
        function func_11(var_9::Bool=true,var_10::String="QMB4uuuH")
            local var_20::Int16 = -2574
            var_20 = var_20 / var_20
            var_10 = var_10[if length(var_10) > length(var_10)
                            1
                        else
                            length(var_10)
                        end:if length(var_10) > length(var_10) > 0
                            0
                        else
                            end
                        end]
            var_10 = var_10[1:if length(var_10) > length(var_10)
                            0
                        else
                            length(var_10) - length(var_10)
                        end]
            Creature.stepLeft(o)
            Creature.stepRight(o)
            Creature.stepLeft(o)
            Creature.stepDown(o)
            Creature.stepRight(o)
            if var_9 > var_9
                local var_22::Int64 = -4737339713700331708
                Creature.stepUp(o)
            end
            Creature.stepRight(o)
            return var_9
        end
        local var_6::Int64 = -1114054366428914299
        local var_1::Int8 = 119
        function func_4(var_2::Int64=-7602132272464706182,var_3::Int16=32402)
            local var_21::String = "oSdt7aUi"
            local var_18::Int16 = -12575
            var_2 = Int(Creature.getEnergy(o,Int(var_3),Int(var_3)))
            var_18 = var_18 * var_18
            Creature.stepDown(o)
            o.mem[var_3] = var_3
            Creature.stepDown(o)
            Creature.stepDown(o)
            var_2 = var_2 / var_2
            Creature.stepRight(o)
            Creature.stepLeft(o)
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_3),Int(var_3)))
            var_18 = if haskey(o.mem,var_3)
                    o.mem[var_3]
                else
                    var_18
                end
            var_21 = var_21 * var_21
            Creature.stepUp(o)
            Creature.stepUp(o)
            var_2 = var_2 / var_2
            Creature.stepUp(o)
            return var_2
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepRight(o)
        end
        if var_13 == var_13
            Creature.stepDown(o)
            Creature.stepLeft(o)
        end
        Creature.stepDown(o)
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepRight(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        if var_1 < var_1
            local var_15::Bool = false
            Creature.stepUp(o)
        end
        var_1 = var_1 / var_1
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            local var_17::Int16 = 18765
            var_17 = if haskey(o.mem,var_17)
                    o.mem[var_17]
                else
                    var_17
                end
        end
        Creature.stepDown(o)
        var_6 = var_6 + var_6
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepLeft(o)
            Creature.stepLeft(o)
        end
        var_13 = Bool(abs(var_13 - var_13))
        return true
    end),[100,300,30,0,1,1,1],0,289,3,88788,0x00000145,Dict{Int16,Int16}(),[65,102],69),RpcApi.SimpleOrganism(0x00000000000040d4,:(function (c::Config.ConfigData,o::Creature.Organism)
        local var_7::Int8 = -48
        local var_5::Int16 = 23602
        function func_4(var_2::Bool=true,var_3::Int64=420931049847432977)
            Creature.stepUp(o)
            var_3 = var_3 / var_3
            Creature.stepDown(o)
            var_3 = var_3 / var_3
            Creature.stepLeft(o)
            return var_2
        end
        local var_1::Int8 = 119
        o.mem[var_5] = var_5
        Creature.eatRight(c,o,Int(var_1))
        if var_5 === var_5
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        o.mem[var_5] = var_5
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepRight(o)
        return true
    end),[100,300,30,0,1,1,1],6,1000,1,87358,0x00000036,Dict(23602=>23602),[194,194],22),RpcApi.SimpleOrganism(0x00000000000036bf,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_3(var_2::Int16=-20728)
            local var_4::Int8 = 59
            Creature.eatDown(c,o,Int(var_4))
            var_2 = if haskey(o.mem,var_2)
                    o.mem[var_2]
                else
                    var_2
                end
            o.mem[var_2] = var_2
            Creature.eatDown(c,o,Int(var_4))
            var_2 = if haskey(o.mem,var_2)
                    o.mem[var_2]
                else
                    var_2
                end
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        var_1 = var_1 * var_1
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            Creature.stepLeft(o)
        end
        Creature.eatUp(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],0,332,1,84311,0x0000005c,Dict{Int16,Int16}(),[58,3],19),RpcApi.SimpleOrganism(0x0000000000003f81,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_6::String="5cOffzeY",var_7::Int8=-15)
            return var_6
        end
        local var_1::Int8 = 119
        function func_3(var_2::Bool=false)
            local var_5::Int16 = -7254
            Creature.stepRight(o)
            var_5 = var_5 / var_5
            Creature.stepDown(o)
            var_2 = var_2 | var_2
            var_5 = if haskey(o.mem,var_5)
                    o.mem[var_5]
                else
                    var_5
                end
            Creature.stepUp(o)
            return var_2
        end
        Creature.stepLeft(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        var_1 = var_1 + var_1
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
        end
        Creature.stepLeft(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.stepLeft(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepLeft(o)
        Creature.stepDown(o)
        return true
    end),[100,300,30,0,1,1,1],32,1000,1,83917,0x0000006d,Dict{Int16,Int16}(),[57,156],24),RpcApi.SimpleOrganism(0x0000000000004145,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_7(var_5::Int16=4444,var_6::Bool=true)
            local var_8::Int64 = 171829713795169641
            var_6 = var_6 & var_6
            Creature.stepLeft(o)
            var_5 = var_5 * var_5
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_4::Int16 = -14134
            local var_9::Int64 = -59930535438688302
            Creature.stepRight(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            Creature.stepRight(o)
            Creature.stepUp(o)
            o.mem[var_4] = var_4
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,83477,0x0000005d,Dict{Int16,Int16}(),[56,157],24),RpcApi.SimpleOrganism(0x0000000000004143,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_8(var_7::Int8=-51)
            Creature.eatRight(c,o,Int(var_7))
            return var_7
        end
        local var_1::Int8 = 119
        function func_4(var_2::Int64=-7602132272464706182,var_3::Int16=32402)
            var_3 = var_3 + var_3
            var_2 = var_2 - var_2
            Creature.stepUp(o)
            var_2 = var_2 - var_2
            if var_3 >= var_3
            end
            if var_2 != var_2
            end
            Creature.stepDown(o)
            Creature.stepDown(o)
            var_2 = Int(Creature.getEnergy(o,Int(var_3),Int(var_3)))
            return var_2
        end
        local var_9::String = "jMd1joqO"
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepRight(o)
        Creature.eatDown(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],13,927,1,81739,0x0000004c,Dict{Int16,Int16}(),[155,95],19),RpcApi.SimpleOrganism(0x0000000000004072,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_11(var_9::Int64=-6376931539806068851,var_10::Int64=-527738452351850021)
            return var_9
        end
        function func_7(var_5::Int16=-14433,var_6::Bool=true)
            local var_8::String = "Mi2ZEs3w"
            Creature.stepLeft(o)
            return var_5
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_13::Int64 = 4823497939271164860
            local var_4::Int16 = -14134
            local var_12::Int64 = -8561306439954911482
            var_13 = var_12 * var_12
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        if var_1 > var_1
            local var_14::Int64 = 3798861155764565572
        end
        Creature.eatRight(c,o,Int(var_1))
        Creature.stepUp(o)
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatLeft(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],7,1000,1,79290,0x0000004e,Dict{Int16,Int16}(),[136,33],20),RpcApi.SimpleOrganism(0x000000000000407d,:(function (c::Config.ConfigData,o::Creature.Organism)
        function func_10(var_9::String="eeX7YJuC")
            var_9 = var_9[1:if length(var_9) > length(var_9)
                            0
                        else
                            length(var_9) - length(var_9)
                        end]
            return var_9
        end
        function func_8(var_7::Int64=4047437063232144020)
            local var_13::Int8 = -14
            for i::Int8 = 1:div(var_13,_LOOP_STEPS_DIVIDER)
            end
            Creature.eatDown(c,o,Int(var_13))
            Creature.stepLeft(o)
            Creature.eatRight(c,o,Int(var_13))
            return var_7
        end
        function func_3(var_2::Int64=-235958945428180621)
            local var_11::Int16 = 7778
            local var_6::Int64 = 2108313727829142897
            local var_5::Int64 = 3293689547575976404
            var_2 = var_2 / var_2
            var_2 = var_2 / var_2
            Creature.stepUp(o)
            var_11 = if haskey(o.mem,var_11)
                    o.mem[var_11]
                else
                    var_11
                end
            var_2 = var_2 * var_5
            Creature.stepUp(o)
            var_5 = var_5 / var_6
            var_4 = if haskey(o.mem,var_4)
                    o.mem[var_4]
                else
                    var_4
                end
            Creature.stepRight(o)
            var_11 = var_11 / var_11
            var_4 = var_4 / var_4
            if var_5 != var_5
                local var_14::Int64 = -2118914073180382021
                Creature.stepLeft(o)
            end
            var_2 = Int(Creature.getEnergy(o,Int(var_4),Int(var_4)))
            return var_2
        end
        local var_1::Int8 = 119
        Creature.eatDown(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatUp(c,o,Int(var_1))
        Creature.stepLeft(o)
        Creature.eatDown(c,o,Int(var_1))
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            local var_16::Int64 = 8156470862365741584
        end
        Creature.stepDown(o)
        Creature.eatDown(c,o,Int(var_1))
        Creature.stepUp(o)
        for i::Int8 = 1:div(var_1,_LOOP_STEPS_DIVIDER)
            local var_15::Int64 = -6084800611521842062
        end
        Creature.eatUp(c,o,Int(var_1))
        Creature.eatRight(c,o,Int(var_1))
        return true
    end),[100,300,30,0,1,1,1],23,442,14,79035,0x00000094,Dict{Int16,Int16}(),[251,69],43)]

max_stars_repo_name: tmptrash/jevo

max_issues_repo_name: tmptrash/jevo

max_forks_repo_name: tmptrash/jevo


text:
#=
Associated YT tutorial: https://youtu.be/gmjzbpSVY1A
=#

using Random

Random.seed!(0)

# Define the spiral_data function to get some data.
function spiral_data(points::Int, classes::Int)
    X = zeros(Float64, points * classes, 2)
    y = zeros(Int64, points * classes)
    for class_number = 1:classes
        ix = points*(class_number-1)+1:points*class_number
        r = range(0, 1, length=points)
        t = range((class_number-1)*4, class_number*4, length=points) .+ randn(points)*0.2
        # The @. macro makes all calculations element-wise.
        @. X[ix, :] = [r*sin(t*2.5) r*cos(t*2.5)]
        @. y[ix] = class_number
    end
    return X, y
end
(X, y) = spiral_data(100, 3)

# Julia doesn't have classes, so instead we make a struct.
mutable struct Layer_Dense
    weights::Array{Float64, 2}
    biases::Vector{Float64}
end

# Make a constructor for this struct.
function Layer_Dense(n_inputs, n_neurons)
    weights = 0.10 * randn(n_inputs, n_neurons)
    biases = zeros(n_neurons)

    return Layer_Dense(weights, biases)
end

# And define the forward function for this struct.
function forward(layer::Layer_Dense, inputs)
    return inputs * layer.weights .+ layer.biases'
end

# The rectified linear unit.
function activation_ReLU(inputs)
    # Using the dot to take the element-wise maximum.
    return max.(0., inputs)
end

# Now do the example.
layer1 = Layer_Dense(2, 5)

output1 = forward(layer1, X)
activation_output1 = activation_ReLU(output1)

println(activation_output1)


text:
# Test ResNet

using LinearAlgebra, InvertibleNetworks, Test

# Input
nx = 28
ny = 28
n_in = 4
n_hidden = 8
batchsize = 2
nblocks = 4

# Input
X = glorot_uniform(nx, ny, n_in, batchsize)
X0 = glorot_uniform(nx, ny, n_in, batchsize)
dX = X - X0

# ResNet
RN0 = ResNet(n_in, n_hidden, nblocks; k=3, p=1, s=1, norm=nothing, n_out=nothing)
θ0 = deepcopy(get_params(RN0))
RN = ResNet(n_in, n_hidden, nblocks; k=3, p=1, s=1, norm=nothing, n_out=nothing)
θ = deepcopy(get_params(RN))

# Observed data
Y = RN.forward(X)

RN_dummy = ResNet(n_in, n_hidden, nblocks; k=3, p=1, s=1, norm=nothing, n_out=nothing)
function loss(θ, X, Y; RN=RN_dummy)
    set_params!(RN, θ)
    Y_ = RN.forward(X)
    ΔY = Y_ - Y
    f = .5f0*norm(ΔY)^2
    ΔX = RN.backward(ΔY, X)
    return f, ΔX, deepcopy(get_grads(RN))
end

###################################################################################################
# Gradient tests

# Gradient test w.r.t. input
f0, ΔX = loss(θ, X0, Y)[1:2]
h = 0.1f0
maxiter = 5
err1 = zeros(Float32, maxiter)
err2 = zeros(Float32, maxiter)

print("\nGradient test\n")
for j=1:maxiter
    f = loss(θ, X0 + h*dX, Y)[1]
    err1[j] = abs(f - f0)
    err2[j] = abs(f - f0 - h*dot(dX, ΔX))
    print(err1[j], "; ", err2[j], "\n")
    global h = h/2f0
end

@test isapprox(err1[end] / (err1[1]/2^(maxiter-1)), 1f0; atol=1f1)
@test isapprox(err2[end] / (err2[1]/4^(maxiter-1)), 1f0; atol=1f1)

# Gradient test for weights
dθ = θ-θ0; dθ .*= norm.(dθ)./(norm.(θ0).+1f-10)

f0, ΔX, Δθ = loss(θ, X0, Y)
h = 0.1f0
maxiter = 5
err3 = zeros(Float32, maxiter)
err4 = zeros(Float32, maxiter)

print("\nGradient test convolutions\n")
for j=1:maxiter
    f = loss(θ+h*dθ, X0, Y)[1]
    err3[j] = abs(f - f0)
    err4[j] = abs(f - f0 - h*dot(dθ, Δθ))
    print(err3[j], "; ", err4[j], "\n")
    global h = h/2f0
end

@test isapprox(err3[end] / (err3[1]/2^(maxiter-1)), 1f0; atol=1f1)
@test isapprox(err4[end] / (err4[1]/4^(maxiter-1)), 1f0; atol=1f1)


text:
module AWG
using Libdl # for NI dlls
include("../INSTR_TYPES.jl")
include("../lib_locations.jl")
include("../visa/VISA.jl")
#
## include all AWG files
include("./Agilent/AWG_Agilent_common.jl")
include("./Agilent/Agilent33500B.jl")
include("./Agilent/Agilent33250A.jl")
# NI
include("NIFgen/NIFgen.jl")
include("NIFgen/NIFgen_Misc.jl")
end #endmodule


text:
function mark_frame_perimetr!(r::Robot)
    num_vert = moves!(r, Sud)
    num_hor = moves!(r, West)
    #УТВ: Робот - в Юго-Западном углу

    for side in (Nord, Ost, Sud, West)
        putmarkers!(r, side)
    end
    #УТВ: По всему периметру стоят маркеры

    moves!(r, Nord, num_vert)
    moves!(r, Ost, num_hor)
    #УТВ: Робот - в исходном положении
end

function moves!(r::Robot,side::HorizonSide)
    num_steps=0
    while isborder(r,side)==false
        move!(r,side)
        num_steps+=1
    end
    return num_steps
end

function moves!(r::Robot,side::HorizonSide,num_steps::Int)
    for _ in 1:num_steps # символ "_" заменяет фактически не используемую переменную
        move!(r,side)
    end
end

function putmarkers!(r::Robot, side::HorizonSide)
    while isborder(r,side)==false
        move!(r,side)
        putmarker!(r)
    end
end


text:
@testset "NonOverlappingOrthotopes2ConditionallyNonOverlappingOrthotopes: $(fct_type), orthotopes $(n_ortho), dimension $(dim), $(T)" for fct_type in ["vector of variables", "vector affine function"], n_ortho in [2, 3], dim in [2, 3], T in [Int, Float64]
    mock = MOIU.MockOptimizer(ConditionallyNonOverlappingOrthotopesModel{T}())
    model = COIB.NonOverlappingOrthotopes2ConditionallyNonOverlappingOrthotopes{T}(mock)

    @test MOI.supports_constraint(model, MOI.VariableIndex, MOI.ZeroOne)
    @test MOI.supports_constraint(
        model,
        MOI.VectorAffineFunction{T},
        CP.NonOverlappingOrthotopes{CP.CONDITIONAL_NONVERLAPPING_ORTHOTOPES},
    )
    @test MOIB.supports_bridging_constraint(
        model,
        MOI.VectorAffineFunction{T},
        CP.NonOverlappingOrthotopes{CP.UNCONDITIONAL_NONVERLAPPING_ORTHOTOPES},
    )

    n_vars_position = n_ortho * dim
    n_vars_size = n_ortho * dim
    if T == Int
        x_pos, _ = MOI.add_constrained_variables(model, [MOI.Integer() for _ in 1:n_vars_position])
        x_sze, _ = MOI.add_constrained_variables(model, [MOI.Integer() for _ in 1:n_vars_size])
        x_end, _ = MOI.add_constrained_variables(model, [MOI.Integer() for _ in 1:n_vars_position])
    elseif T == Float64
        x_pos = MOI.add_variables(model, n_vars_position)
        x_sze = MOI.add_variables(model, n_vars_size)
        x_end = MOI.add_variables(model, n_vars_position)
    end
    x_ortho = [
        [
            x_pos[(1 + (i - 1) * dim) : (i * dim)],
            x_sze[(1 + (i - 1) * dim) : (i * dim)],
            x_end[(1 + (i - 1) * dim) : (i * dim)],
        ]
        for i in 1:n_ortho
    ]
    x = vcat([vcat(x_ortho[i]...) for i in 1:n_ortho]...)

    # Test the construction of x before going on (no loops to ease checking
    # for correctness).
    @test length(x_pos) == dim * n_ortho
    @test length(x_sze) == dim * n_ortho
    @test length(x_end) == dim * n_ortho
    @test length(x) == 3 * dim * n_ortho
    if n_ortho >= 1
        @test x[(0 * dim + 1):(1 * dim)] == x_pos[(0 * dim + 1):(1 * dim)]
        @test x[(1 * dim + 1):(2 * dim)] == x_sze[(0 * dim + 1):(1 * dim)]
        @test x[(2 * dim + 1):(3 * dim)] == x_end[(0 * dim + 1):(1 * dim)]
    end
    if n_ortho >= 2
        @test x[(3 * dim + 1):(4 * dim)] == x_pos[(1 * dim + 1):(2 * dim)]
        @test x[(4 * dim + 1):(5 * dim)] == x_sze[(1 * dim + 1):(2 * dim)]
        @test x[(5 * dim + 1):(6 * dim)] == x_end[(1 * dim + 1):(2 * dim)]
    end
    if n_ortho >= 3
        @test x[(6 * dim + 1):(7 * dim)] == x_pos[(2 * dim + 1):(3 * dim)]
        @test x[(7 * dim + 1):(8 * dim)] == x_sze[(2 * dim + 1):(3 * dim)]
        @test x[(8 * dim + 1):(9 * dim)] == x_end[(2 * dim + 1):(3 * dim)]
    end
    if n_ortho >= 4
        @assert false
    end

    fct = if fct_type == "vector of variables"
        MOI.VectorOfVariables(x)
    elseif fct_type == "vector affine function"
        MOIU.vectorize(x)
    else
        @assert false
    end

    c = MOI.add_constraint(model, fct, CP.NonOverlappingOrthotopes(n_ortho, dim))

    for i in 1:(3 * dim *  n_ortho)
        @test MOI.is_valid(model, x[i])
    end
    @test MOI.is_valid(model, c)

    bridge = MOIBC.bridges(model)[MOI.ConstraintIndex{MOI.VectorOfVariables, CP.NonOverlappingOrthotopes}(-1)]

    @testset "Bridge properties" begin
        @test MOIBC.concrete_bridge_type(typeof(bridge), MOI.VectorOfVariables, CP.NonOverlappingOrthotopes{CP.UNCONDITIONAL_NONVERLAPPING_ORTHOTOPES}) == typeof(bridge)
        @test MOIB.added_constrained_variable_types(typeof(bridge)) == [(MOI.EqualTo{T},)]
        @test MOIB.added_constraint_types(typeof(bridge)) == [
            (MOI.VectorAffineFunction{T}, CP.NonOverlappingOrthotopes{CP.CONDITIONAL_NONVERLAPPING_ORTHOTOPES}),
        ]

        @test MOI.get(bridge, MOI.NumberOfVariables()) == 1
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VariableIndex, MOI.EqualTo{T}}()) == 1
        @test MOI.get(bridge, MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, CP.NonOverlappingOrthotopes{CP.CONDITIONAL_NONVERLAPPING_ORTHOTOPES}}()) == 1

        @test MOI.get(bridge, MOI.ListOfVariableIndices()) == [bridge.var]
        @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VariableIndex, MOI.EqualTo{T}}()) == [bridge.var_con]
        @test MOI.get(bridge, MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, CP.NonOverlappingOrthotopes{CP.CONDITIONAL_NONVERLAPPING_ORTHOTOPES}}()) == [bridge.con]
    end

    @testset "New variable" begin
        @test MOI.is_valid(model, bridge.var)
        @test MOI.is_valid(model, bridge.var_con)
        @test MOI.get(model, MOI.ConstraintSet(), bridge.var_con) == MOI.EqualTo(one(T))
        @test MOI.get(model, MOI.ConstraintFunction(), bridge.var_con) == bridge.var
    end

    @testset "New constraint" begin
        @test MOI.is_valid(model, bridge.con)
        @test MOI.get(model, MOI.ConstraintSet(), bridge.con) == CP.NonOverlappingOrthotopes{CP.CONDITIONAL_NONVERLAPPING_ORTHOTOPES}(n_ortho, dim)
        f = MOI.get(model, MOI.ConstraintFunction(), bridge.con)

        for i in 1:length(f.terms)
            t = f.terms[i]
            @test t.output_index === i
            @test t.scalar_term.coefficient === one(T)
        end

        f_vars = [t.scalar_term.variable for t in f.terms]
        @test length(f_vars) == 3 * dim * n_ortho + n_ortho

        if n_ortho >= 1
            @test f_vars[(0 * dim + 1):(1 * dim)] == x_pos[(0 * dim + 1):(1 * dim)]
            @test f_vars[(1 * dim + 1):(2 * dim)] == x_sze[(0 * dim + 1):(1 * dim)]
            @test f_vars[(2 * dim + 1):(3 * dim)] == x_end[(0 * dim + 1):(1 * dim)]
            @test f_vars[3 * dim + 1] == bridge.var
        end
        if n_ortho >= 2
            @test f_vars[(3 * dim + 2):(4 * dim + 1)] == x_pos[(1 * dim + 1):(2 * dim)]
            @test f_vars[(4 * dim + 2):(5 * dim + 1)] == x_sze[(1 * dim + 1):(2 * dim)]
            @test f_vars[(5 * dim + 2):(6 * dim + 1)] == x_end[(1 * dim + 1):(2 * dim)]
            @test f_vars[6 * dim + 2] == bridge.var
        end
        if n_ortho >= 3
            @test f_vars[(6 * dim + 3):(7 * dim + 2)] == x_pos[(2 * dim + 1):(3 * dim)]
            @test f_vars[(7 * dim + 3):(8 * dim + 2)] == x_sze[(2 * dim + 1):(3 * dim)]
            @test f_vars[(8 * dim + 3):(9 * dim + 2)] == x_end[(2 * dim + 1):(3 * dim)]
            @test f_vars[9 * dim + 3] == bridge.var
        end
        if n_ortho >= 4
            @assert false
        end
    end
end


text:
"""
Return the branch index (index of child) based
on splitting the feature on value given in the test t.

Returns -1 if branch is not found.
"""
function branch_for_instance(t::NumericBinaryTest, X::Vector{Float64})
    if t.attr_idx < 1 || t.attr_idx > length(X)
        return -1
    end
    test_value = X[t.attr_idx]
    return test_value <= t.split_value ? 1 : 2
end

abstract type AbstractNode end

struct FoundNode
    node::AbstractNode
    parent::Union{AbstractNode, Nothing}
    parent_branch::Int64
    depth::Int64
end

function FoundNode(node, parent, parent_branch)
    return FoundNode(node, parent, parent_branch, -1)
end

mutable struct LearningNodeNB{T<:AbstractSplitCritereon} <: AbstractNode
    stats::Dict{Int64, Float64}
    classifier::NaiveBayes
    last_split_attempt::Int64
    split_critereon::T
end

function LearningNodeNB()
    return LearningNodeNB(Dict{Int64, Float64}(), NaiveBayes(), 0, GiniSplitCritereon())
end

function total_weight(n::AbstractNode)
    return sum(values(n.stats))
end

function filter_instance_to_leaf(n::LearningNodeNB, X::Vector{Float64}, parent, parent_branch::Int64)
    return FoundNode(n, parent, parent_branch)
end

function learn_one!(n::LearningNodeNB, X::Vector{Float64}, y::Int64, weight::Float64=1.0)
    update_stats(n, y, weight)
    partial_fit!(n.classifier, X, y, weight)
end

function predict_one(n::LearningNodeNB, X::Vector{Float64})
    return predict_proba(n.classifier, X)
end

function update_stats(n::LearningNodeNB, y::Int64, weight::Float64=1.0)
    if !haskey(n.stats, y)
        n.stats[y] = 0.0
    end
    n.stats[y] += weight
end

function get_best_split_suggestions(n::LearningNodeNB)
    best_suggestions = Vector{AttributeSplitSuggestion}()
    for (attr_idx, observer) in pairs(n.classifier.attribute_observers)
        best_suggestion = get_best_split_suggestion(observer, n.classifier.observed_class_distribution, attr_idx)
        push!(best_suggestions, best_suggestion)
    end
    return best_suggestions
end

struct SplitNode{T<:AbstractSplitCritereon} <: AbstractNode
    stats::Dict{Int64, Float64}
    children::Dict{Int64, AbstractNode}
    split_test::AbstractConditionalTest
    split_critereon::T
end

function set_child(parent::SplitNode, child::AbstractNode, branch_idx)
    parent.children[branch_idx] = child
end

function filter_instance_to_leaf(n::SplitNode, X::Vector{Float64}, parent, parent_branch::Int64)
    child_branch = branch_for_instance(n.split_test, X)
    if child_branch == -1
        return FoundNode(n, parent, parent_branch)
    end
    child = n.children[child_branch]
    return filter_instance_to_leaf(child, X, n, child_branch)
end

mutable struct HoeffdingTree
    grace_period::Float64
    split_confidence::Float64
    tie_threshhold::Float64
    tree_root::AbstractNode
    decision_node_count::Int64
    active_leaf_node_count::Int64
    train_weight_seen::Float64
end

function HoeffdingTree()
    return HoeffdingTree(200.0, 0.0000001, 0.05, LearningNodeNB(), 1, 1, 0.0)
end

function partial_fit!(c::HoeffdingTree, X::Vector{Float64}, y::Int64)
    found_node  = filter_instance_to_leaf(c.tree_root, X, nothing, -1)
    leaf = found_node.node
    handle_leaf(leaf, found_node.parent, found_node.parent_branch, c, X, y)
end

function handle_leaf(learning_node::LearningNodeNB, parent::Union{AbstractNode, Nothing}, parent_branch::Int64, c::HoeffdingTree, X::Vector{Float64}, y::Int64)
    learn_one!(learning_node, X, y)
    weight_seen = total_weight(learning_node)
    seen_since_last_split = weight_seen - learning_node.last_split_attempt
    if seen_since_last_split >= c.grace_period
        attempt_to_split(c, learning_node, parent, parent_branch)
        learning_node.last_split_attempt = floor(Int, weight_seen)
    end
end

function predict(c::HoeffdingTree, X::Vector{Float64})
    votes = predict_proba(c, X)
    return argmax_votes(votes)
end

function predict_proba(c::HoeffdingTree, X::Vector{Float64})
    found_node  = filter_instance_to_leaf(c.tree_root, X, nothing, -1)
    leaf = found_node.node
    return predict_one(leaf, X)
end

function hoeffding_bound(range, confidence, n)
    return sqrt((range^2 * log(1.0/confidence)) / (2.0*n))
end

function attempt_to_split(c::HoeffdingTree, n::LearningNodeNB, parent::Union{SplitNode, Nothing}, parent_idx::Int64)
    best_split_suggestions = get_best_split_suggestions(n)
    sort!(best_split_suggestions, by = x -> x.merit)
    should_split = false
    if length(best_split_suggestions) < 2
        should_split = length(best_split_suggestions) > 1
    else
        hoeffding_value = hoeffding_bound(get_range_of_merit(n.split_critereon), c.split_confidence, total_weight(n))
        best_suggestion = best_split_suggestions[end]
        second_best_suggestion = best_split_suggestions[end-1]
        if (best_suggestion.merit - second_best_suggestion.merit > hoeffding_value) || (hoeffding_value < c.tie_threshhold)
            should_split = true
        end
    end
    if should_split
        split_decision = best_split_suggestions[end]
        split_children = Dict{Int64, AbstractNode}()
        for (i, child_distribution) in enumerate(split_decision.resulting_class_distribution)
            new_child = LearningNodeNB(child_distribution, NaiveBayes(), 0, n.split_critereon)
            split_children[i] = new_child
        end
        new_split = SplitNode(n.stats, split_children, split_decision.split_test, n.split_critereon)
        if !isa(parent, Nothing)
            set_child(parent, new_split, parent_idx)
        else
            c.tree_root = new_split
        end
    end

end