Skip to content

Commit

Permalink
Add Result struct
Browse files Browse the repository at this point in the history
  • Loading branch information
James Zingel committed Oct 14, 2023
1 parent 4d76807 commit 575d58c
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 60 deletions.
76 changes: 26 additions & 50 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function place(board::Board, piece::Piece, x::Integer, y::Integer)::Tuple{Bool,
return false, newboard(board)
end

function compute(i::Integer, j::Integer, board::Board, perms::Vector{Vector{Piece{T}}}, stats::Dict, callbacks) where {T<:Integer}
function compute(i::Integer, j::Integer, board::Board, perms::Vector{Vector{Piece{T}}}, res::Result, callbacks)::Result where {T<:Integer}
# TODO: Don't calculate the second permutations for duplicated pieces
# i=x, j=y
while j <= size(board, 1)
Expand All @@ -113,60 +113,50 @@ function compute(i::Integer, j::Integer, board::Board, perms::Vector{Vector{Piec
for piece in piece_potentials
poss, b = place(board, piece, i, j)

stats["total_placements"] += 1
stats["successful_placements"] += poss
callbacks[1](poss, stats) # callback for if the piece is placed
res.total_placements += 1
res.successful_placements += poss
callbacks[1](poss, res) # callback for if the piece is placed

if poss
callbacks[2](b) # callback for if the piece is possible
remaining = copy(perms)
deleteat!(remaining, p_inx)

if length(remaining) <= stats["best_fit"] # we're the best so far
if length(remaining) < stats["best_fit"] # its a new best
stats["best_fit"] = length(remaining)
stats["best_times"] = 0
if length(remaining) <= res.best_fit # we're the best so far
if length(remaining) < res.best_fit # its a new best
res.best_fit = length(remaining)
res.best_times = 0
end
stats["best_times"] += 1
callbacks[3](b, stats, remaining) # callback for if the piece is the best
res.best_times += 1
callbacks[3](b, res, remaining) # callback for if the piece is the best
end

if length(remaining) == 0 # We're done!
@debug "Found solution" num=stats["best_times"] b
push!(stats["solutions"], b)
@debug "Found solution" num=res.best_times b
push!(res.solutions, b)
else
compute(i, j, b, remaining, stats, callbacks)
compute(i, j, b, remaining, res, callbacks)
# next loop will increment i,j for us
end
end
end
end
# Unable to place any pieces. So this sim sucks
stats["dead_ends"] += 1
return stats
res.dead_ends += 1
return res
end
i += 1
end
j += 1
i = 1
end
return stats
return res
end

solve(prob::Problem) = solve(prob, [(x,y)->nothing, (x)->nothing, (x,y,z)->nothing])
function solve(problem::Problem, f)
function solve(problem::Problem, f)::Result
perms = create_permutations(problem.pieces)
stats = Dict( # environment variables passed through
"total_placements" => 0,
"successful_placements" => 0,
"dead_ends" => 0,
"best_fit" => 1000, # best number of pieces fitted in the board
"best_times" => 0, # number of times the best fit was achieved
"solutions" => [],
"wait" => false, # if we wait after each placement for the enter key
"tic" => now(), # start time
)
return compute(1, 1, problem.board, perms, stats, f)
return compute(1, 1, problem.board, perms, newresult(), f)
end

function distribute(problem::Problem)::Vector{Problem}
Expand All @@ -193,40 +183,26 @@ function distribute(problem::Problem)::Vector{Problem}
return subproblems
end

function solveparallel(prob::Problem)
function solveparallel(prob::Problem)::Result
# Solve the problem using multithreading
if nthreads() < 2
@warn "Multithreading arguement badly set. Only using 1 thread. Start julia with --threads=n to add more threads"
@warn "Multithreading arguement badly set. Only using 1 thread. Start julia with --threads=n to add more threads or --threads=auto to set automatically."
end

original = Dict( # environment variables passed through
"total_placements" => 0,
"successful_placements" => 0,
"dead_ends" => 0,
"best_fit" => 1000, # best number of pieces fitted in the board
"best_times" => 0, # number of times the best fit was achieved
"solutions" => [],
"wait" => false, # if we wait after each placement for the enter key
"tic" => now(), # start time
)

subprobs = distribute(prob)
nprobs = length(subprobs)
results = fill(Dict(), nprobs) # stats for each subproblem to mutate
results = fill(newresult(), nprobs) # stats for each subproblem to mutate

@info "Multithreading with $(nthreads()) threads chugging through $(length(subprobs)) subproblems"

@threads for i = 1:nprobs
subprob = subprobs[i]
results[i] = compute(1, 1, subprob.board, create_permutations(subprob.pieces), deepcopy(original), [(x,y)->nothing, (x)->nothing, (x,y,z)->nothing])
print("Worker $(threadid()) has finished subproblem $i finding $(length(results[i]["solutions"])) results.\n")
results[i] = compute(1, 1, subprob.board, create_permutations(subprob.pieces), newresult(), [(x,y)->nothing, (x)->nothing, (x,y,z)->nothing])
print("Worker $(threadid()) has finished subproblem $i finding $(length(results[i].solutions)) results.\n")
end

# merge results
for v in ["total_placements", "successful_placements", "dead_ends", "best_times"]
original[v] = sum([sp[v] for sp in results])
end
original["solutions"] = vcat([sp["solutions"] for sp in results]...)
@info "Found $(length(original["solutions"])) solutions."
return original
merged = merge(results)
@info "Found $(length(merged.solutions)) solutions."
return merged
end
16 changes: 8 additions & 8 deletions src/run.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ function clear_lines(num)
print("\r\u1b[K") # clear that last line
end

function update_screen(b, stats, remain)
function update_screen(b, res, remain)
clear_lines(size(b.shape, 1)+1)
print("Placement success rate of ", BOLD("$(stats["successful_placements"])/$(stats["total_placements"])"),
" with ", BOLD("$(stats["dead_ends"])"), " dead ends in ", BOLD("$(round(now()-stats["tic"], Minute))"), ".") # no newline as print(b) adds one at the beginning
print("Placement success rate of ", BOLD("$(res.successful_placements)/$(res.total_placements)"),
" with ", BOLD("$(res.dead_ends)"), " dead ends in ", BOLD("$(round(now()-res.tic, Minute))"), ".") # no newline as print(b) adds one at the beginning
println(b)
if length(remain) == 0
print("Found ", BOLD("$(length(stats["solutions"]))"), " solutions.")
print("Found ", BOLD("$(length(res.solutions))"), " solutions.")
else
print("Fitted ", BOLD("$(12-length(remain))/12"), " pieces into the board ", BOLD("$(stats["best_times"])"), " times.")
print("Fitted ", BOLD("$(12-length(remain))/12"), " pieces into the board ", BOLD("$(res.best_times)"), " times.")
end
end

Expand All @@ -46,11 +46,11 @@ function live(prob::Problem)
println(BOLD("Lonpos Solver v1.1"))
println(prob.board)

solutions = solve(prob, callbacks)
result = solve(prob, callbacks)
clear_lines(size(prob.board.shape, 1))

println("Done! Found $(length(solutions)) solutions")
println(solutions)
println("Done! Found $(length(result.solutions)) solutions")
println(result.solutions)
end


25 changes: 25 additions & 0 deletions src/structs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using Crayons
using TOML
using Dates

# update to static in the future
mutable struct Piece{T<:Integer}
Expand All @@ -18,6 +19,17 @@ struct Problem{T<:Integer}
board::Board{T}
end

# Contain all the solutions and stats. This is passed between branches
mutable struct Result{T<:Integer}
total_placements::T
successful_placements::T
dead_ends::T
best_fit::T # best number of pieces fitted in the board
best_times::T # number of times the best fit was achieved
solutions:: Vector{Board}
tic:: DateTime # problem start time
end

warned = false

function string_map_to_matrix(map::String)::Matrix{Int64}
Expand Down Expand Up @@ -52,6 +64,19 @@ newboard(shp::Matrix{T}) where {T<:Integer} = Board(shp)
newboard(b::Board) = Board(copy(b.shape)) # is a copy
newboard(map::String) = newboard(string_map_to_matrix(map) * INVALID_BOARD)

newresult() = Result(0, 0, 0, 1000, 0, Board[], now())

function merge(results::Vector{Result{T}})::Result{T} where {T<:Integer}
# Merge all the results into one
combined = newresult()
combined.total_placements = sum([r.total_placements for r in results])
combined.successful_placements = sum([r.successful_placements for r in results])
combined.dead_ends = sum([r.dead_ends for r in results])
combined.best_times = sum([r.best_times for r in results])
combined.solutions = vcat([r.solutions for r in results]...)
return combined
end

function consistent(prob::Problem)::Bool
boardgaps = prod(size(prob.board.shape)) - (sum(prob.board.shape)÷13)
piecegaps = sum([sum(ifelse.(p.shape .!= 0, 1, 0)) for p in prob.pieces])
Expand Down
4 changes: 2 additions & 2 deletions test/end2end.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ subproblems = Lonpos.distribute(prob)


# END TO END
sols = solve(prob)
res = solve(prob)

@test length(sols) == 2
@test length(res.solutions) == 2

0 comments on commit 575d58c

Please sign in to comment.