OVERRIDE_MSG = "This should be overridden in concrete implemention of AbstractState"

last_reward(::AbstractState)            = @assert false OVERRIDE_MSG
# @todo change to get_next_state
next_state(::AbstractState, ::Integer)  = @assert false OVERRIDE_MSG
states(::AbstractState)                 = @assert false OVERRIDE_MSG
dynamics(::AbstractState, ::Integer)    = @assert false OVERRIDE_MSG

# @todo make this into a generic string
hex_str(::AbstractState)                = @assert false OVERRIDE_MSG
key(::AbstractState)                    = @assert false OVERRIDE_MSG
to_play(::AbstractState)                = @assert false OVERRIDE_MSG
make_image(::AbstractState)             = @assert false OVERRIDE_MSG
gridsvg(::AbstractState)                = @assert false OVERRIDE_MSG
targetsvg(::AbstractState, ::Target)    = @assert false OVERRIDE_MSG

# @todo should be in AbstractBoardGame
function cartesian_coords_to_action(
    ::Type{<:AbstractState}, 
    x::Int, 
    y::Int)
    @assert false OVERRIDE_MSG
end

# @todo should be in AbstractBoardGame
# @todo rename to get_cell_states
calc_cell_states(::AbstractState)       = @assert false OVERRIDE_MSG

# @todo using the decode{T} pattern
encode(s::AbstractEnvState) = base64encode(serialize, s)
function decode(encoded::String)
    encoded |> base64decode |> IOBuffer |> deserialize
end

function init(T::Type{<:AbstractState}, actions::Vector{<:Integer})
    reduce(next_state, actions; init=T())
end

Base.copy(T::Type{<:AbstractState}, state::AbstractState) = T(key(state))

function Base.show(io::IO, s::AbstractState)
    encoded = encode(s)
    if length(encoded) >= 40
        encoded = join([first(encoded, 20), "...", last(encoded, 20)])
    end
    print(io, "$(string(AbstractState)){\"$encoded\"}")
end