diff --git a/julia/CubeShapeCounting.jl b/julia/CubeShapeCounting.jl index 04f216d..aa50bbc 100644 --- a/julia/CubeShapeCounting.jl +++ b/julia/CubeShapeCounting.jl @@ -1,6 +1,117 @@ +include("Shape.jl") include("ImmutableOrientedShape.jl") +include("Powerset.jl") +include("TupleMisc.jl") +include("plot.jl") +using XXhash +using Serialization +using DataStructures +using ArgParse -function main(i::Int64) - D = Dict{ImmutableOrientedShape}() - S = Stack{Int64}(); +function main() + s = ArgParseSettings() + @add_arg_table s begin + "-g" + help = "generator" + arg_type = Int + "-l" + help = "list" + action = :store_true + "-p" + help = "plot n_cubes i_shape" + nargs = '+' + arg_type = Int + end + + parsed_args = parse_args(s) + + generate = get(parsed_args, "g", nothing) + if generate !== nothing && generate > 0 + scanForShapes(generate) + end + if get(parsed_args, "l", false) + listShapes() + end + plot = get(parsed_args, "p", nothing) + if length(plot) == 1 + plotShapes(plot) + elseif length(plot) == 2 + plotShape(plot) + end end + +function scanForShapes(MaxSize::Int64) + D = Dict{UInt, ImmutableOrientedShape}() + S = Stack{Shape}(); + cube = getCube() + immutableCube = getImmutableOrientedShape(cube) + D[immutableCube.hash] = immutableCube + push!(S, cube) + + while !isempty(S) + cube = pop!(S) + growableSpaces = collect(getPossibleNeighbors(cube)) + acceptable_growth = MaxSize - length(cube.cubes) + for i ∈ 1:acceptable_growth + possibleGrowth = Powerset.getPowerSubSet(length(growableSpaces), i) + for j ∈ axes(possibleGrowth, 1) + cubesToAdd = growableSpaces[possibleGrowth[j, :]] + newShape = deepcopy(cube) + for c ∈ cubesToAdd + push!(newShape, c) + end + collision = checkForCollision(newShape, D) + if !collision + push!(S, newShape) + immutableNewShape = getImmutableOrientedShape(newShape) + D[immutableNewShape.hash] = immutableNewShape + end + end + end + end + sanitizedData = sanitize(D, MaxSize) + serialize("results.bin", sanitizedData) +end + +function listShapes() + T = deserialize("results.bin") + print("size: ") + println(T[1]) + for V ∈ T[2] + for v ∈ V + println(v) + end + end +end + +function checkForCollision(S::Shape, D::Dict{UInt, ImmutableOrientedShape}) + for i ∈ 1:24 + hash = hashList(S.orderedLists[i]) + value = get(D, hash, nothing) + if value !== nothing && hash == value.hash + return true + end + end + return false +end + +function hashList(L::Vector{Tuple{Int64, Int64, Int64}}) + diffList = Vector{Tuple{Int64, Int64, Int64}}(undef, length(L)-1) + for i ∈ eachindex(diffList) + diffList[i] = L[i+1] - L[i] + end + return xxh3_64(diffList) +end + +function sanitize(D::Dict{UInt, ImmutableOrientedShape}, size::Int64) + data = Vector{Vector{Vector{Tuple{Int64, Int64, Int64}}}}(undef, size) + for i ∈ eachindex(data) + data[i] = Vector{Vector{Tuple{Int64, Int64, Int64}}}(undef, 0) + end + for (K, V) ∈ D + push!(data[length(V.cubes)], V.cubes) + end + return (size, data) +end + +main() diff --git a/julia/ImmutableOrientedShape.jl b/julia/ImmutableOrientedShape.jl index 089b7bb..4f67eea 100644 --- a/julia/ImmutableOrientedShape.jl +++ b/julia/ImmutableOrientedShape.jl @@ -1,11 +1,10 @@ -include("Shape.jl") using XXhash struct ImmutableOrientedShape - cubes::Vector{Tuple{Int8, Int8, Int8}} + cubes::Vector{Tuple{Int64, Int64, Int64}} hash::UInt end function getImmutableOrientedShape(S::Shape) - shape = ImmutableOrientedShape(S.orderedLists[1], xxh3_64(S.orderedLists[1])) + shape = ImmutableOrientedShape(S.orderedLists[1], hashList(S.orderedLists[1])) end diff --git a/julia/Plot.jl b/julia/Plot.jl new file mode 100644 index 0000000..c436e79 --- /dev/null +++ b/julia/Plot.jl @@ -0,0 +1,134 @@ +using PyCall +using Serialization + +py""" +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +def plotShapePY(v): + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + plotSingleShape(ax, v) + + plt.show() + +def plotShapesPY(vs): + n = len(vs) + if n == 1: + plotShapePY(vs[0]) + else: + fig = plt.figure() + print(n) + n_rows = int(np.ceil(0.6*n)) + n_cols = int(np.ceil(n/n_rows)) + print(n_rows) + print(n_cols) + for i in range(len(vs)): + v = vs[i] + ax = fig.add_subplot(n_cols, n_rows, i+1, projection='3d') + plotSingleShape(ax, v) + plt.show() + +def plotSingleShape(ax, v): + l = len(v) + + for i in range(l): + X1, Y1, Z1 = horizontalPlane(v[i][0] , v[i][1] , v[i][2] ) + X2, Y2, Z2 = horizontalPlane(v[i][0] , v[i][1] , v[i][2]+1) + X3, Y3, Z3 = verticalPlaneX( v[i][0] , v[i][1] , v[i][2] ) + X4, Y4, Z4 = verticalPlaneX( v[i][0] , v[i][1]+1, v[i][2] ) + X5, Y5, Z5 = verticalPlaneY( v[i][0] , v[i][1] , v[i][2] ) + X6, Y6, Z6 = verticalPlaneY( v[i][0]+1, v[i][1] , v[i][2] ) + + ax.plot_surface(X1, Y1, Z1, alpha=0.8, color='orange') + ax.plot_surface(X2, Y2, Z2, alpha=0.8, color='orange') + ax.plot_surface(X3, Y3, Z3, alpha=0.8, color='green' ) + ax.plot_surface(X4, Y4, Z4, alpha=0.8, color='green' ) + ax.plot_surface(X5, Y5, Z5, alpha=0.8, color='cyan' ) + ax.plot_surface(X6, Y6, Z6, alpha=0.8, color='cyan' ) + + x_min = np.inf + x_max = -np.inf + y_min = np.inf + y_max = -np.inf + z_min = np.inf + z_max = -np.inf + for i in range(l): + x_min = np.min([x_min, v[i][0]]) + x_max = np.max([x_max, v[i][0]]) + y_min = np.min([y_min, v[i][1]]) + y_max = np.max([y_max, v[i][1]]) + z_min = np.min([z_min, v[i][2]]) + z_max = np.max([z_max, v[i][2]]) + + biggest_length = max([x_max-x_min, y_max-y_min, z_max-z_min]) + x_diff = (biggest_length - (x_max - x_min)) / 2 + x_lim_min = x_min - x_diff + x_lim_max = x_max + x_diff + + y_diff = (biggest_length - (y_max - y_min)) / 2 + y_lim_min = y_min - y_diff + y_lim_max = y_max + y_diff + + z_diff = (biggest_length - (z_max - z_min)) / 2 + z_lim_min = z_min - z_diff + z_lim_max = z_max + z_diff + + ax.scatter([x_lim_min, x_lim_max+1], [y_lim_min, y_lim_max+1], [z_lim_min, z_lim_max+1], alpha=0) + +def getPoints(a): + x = a[0] + y = a[1] + z = a[2] + p = [ + [x, y, z ], + [x+1, y, z ], + [x+1, y+1, z ], + [x, y+1, z ], + [x, y, z+1], + [x+1, y, z+1], + [x+1, y+1, z+1], + [x, y+1, z+1] + ] + return p + +def horizontalPlane(x, y, z): + one = np.ones(4).reshape(2, 2) + r1 = [x, x + 1] + r2 = [y, y + 1] + X, Y = np.meshgrid(r1, r2) + Z = one*z + return X, Y, Z + +def verticalPlaneX(x, y, z): + one = np.ones(4).reshape(2, 2) + r1 = [x, x + 1] + r2 = [z, z + 1] + X, Z = np.meshgrid(r1, r2) + Y = one*y + return X, Y, Z + + +def verticalPlaneY(x, y, z): + one = np.ones(4).reshape(2, 2) + r1 = [y, y + 1] + r2 = [z, z + 1] + Y, Z = np.meshgrid(r1, r2) + X = one*x + return X, Y, Z +""" + +function plotShape(t::Vector{Int64}) + T = deserialize("results.bin") + v = T[2][t[1]][t[2]] + + py"plotShapePY"(v) +end + +function plotShapes(t::Vector{Int64}) + T = deserialize("results.bin") + v = T[2][t[1]] + + py"plotShapesPY"(v) +end diff --git a/julia/Powerset.jl b/julia/Powerset.jl index c7770cb..202067f 100644 --- a/julia/Powerset.jl +++ b/julia/Powerset.jl @@ -4,6 +4,8 @@ global _calculatedPowerSets = Matrix{Int64}(undef, 1, 0) global _grownUntil = 0 function getPowerSet(setSize::Integer) + global _calculatedPowerSets + global _grownUntil wantedPowerSetSize = 2^setSize if setSize <= _grownUntil return _calculatedPowerSets[1:wantedPowerSetSize, 1:setSize] @@ -29,13 +31,19 @@ end function getPowerSubSet(setSize::Integer, subSetSize::Integer) PowerSet = getPowerSet(setSize) - setSizes = dropdim(sum(Powerset, dims = 2), dims = 2) + setSizes = dropdims(sum(PowerSet, dims = 2), dims = 2) powerSubSetSize = binomial(setSize, subSetSize) - powerSubSets = Matrix{Int64}(undef, powerSubSetSize, setSize) + powerSubSets = Matrix{Int64}(undef, powerSubSetSize, subSetSize) j = 1 - for i ∈ 1:setSize + for i ∈ axes(PowerSet, 1) if setSizes[i] == subSetSize - powerSubSets[j, :] = PowerSet[i, :] + l = 1 + for k ∈ axes(PowerSet, 2) + if PowerSet[i, k] == 1 + powerSubSets[j, l] = k + l += 1 + end + end j += 1 end end diff --git a/julia/Rotations.jl b/julia/Rotations.jl new file mode 100644 index 0000000..1ae3fe4 --- /dev/null +++ b/julia/Rotations.jl @@ -0,0 +1,59 @@ + +const global _rot01(x) = (x[1], x[2], x[3]) +const global _rot02(x) = (x[2], x[3], x[1]) +const global _rot03(x) = (x[3], x[1], x[2]) + +const global _rot04(x) = (x[1], x[2], -x[3]) +const global _rot05(x) = (x[2], x[3], -x[1]) +const global _rot06(x) = (x[3], x[1], -x[2]) + +const global _rot07(x) = (x[1], -x[2], x[3]) +const global _rot08(x) = (x[2], -x[3], x[1]) +const global _rot09(x) = (x[3], -x[1], x[2]) + +const global _rot10(x) = (x[1], -x[2], -x[3]) +const global _rot11(x) = (x[2], -x[3], -x[1]) +const global _rot12(x) = (x[3], -x[1], -x[2]) + +const global _rot13(x) = (-x[1], x[2], x[3]) +const global _rot14(x) = (-x[2], x[3], x[1]) +const global _rot15(x) = (-x[3], x[1], x[2]) + +const global _rot16(x) = (-x[1], x[2], -x[3]) +const global _rot17(x) = (-x[2], x[3], -x[1]) +const global _rot18(x) = (-x[3], x[1], -x[2]) + +const global _rot19(x) = (-x[1], -x[2], x[3]) +const global _rot20(x) = (-x[2], -x[3], x[1]) +const global _rot21(x) = (-x[3], -x[1], x[2]) + +const global _rot22(x) = (-x[1], -x[2], -x[3]) +const global _rot23(x) = (-x[2], -x[3], -x[1]) +const global _rot24(x) = (-x[3], -x[1], -x[2]) + +const global Rotations = [ + _rot01, + _rot02, + _rot03, + _rot04, + _rot05, + _rot06, + _rot07, + _rot08, + _rot09, + _rot10, + _rot11, + _rot12, + _rot13, + _rot14, + _rot15, + _rot16, + _rot17, + _rot18, + _rot19, + _rot20, + _rot21, + _rot22, + _rot23, + _rot24 +] diff --git a/julia/Shape.jl b/julia/Shape.jl index 71bbcde..6c378a5 100644 --- a/julia/Shape.jl +++ b/julia/Shape.jl @@ -1,28 +1,40 @@ -include("SortingModifiers.jl") +include("Rotations.jl") struct Shape - cubes::Set{Tuple{Int8, Int8, Int8}} - recentCubes::Set{Tuple{Int8, Int8, Int8}} - orderedLists::Vector{Vector{Tuple{Int8, Int8, Int8}}} + cubes::Set{Tuple{Int64, Int64, Int64}} + recentCubes::Set{Tuple{Int64, Int64, Int64}} + orderedLists::Vector{Vector{Tuple{Int64, Int64, Int64}}} end -function trypush!(S::Shape, t::Tuple{Int8, Int8, Int8}) - if t ∈ S.cubes - return false - else - push!(S.cubes, t) - for i ∈ 1:24 - index = searchsortedfirst(S.orderedLists[i], t, by=SortingModifiers[i]) - insert!(S.orderedLists, index, t) - end - return true +function Base.:push!(S::Shape, t::Tuple{Int64, Int64, Int64}) + push!(S.cubes, t) + for i ∈ 1:24 + t_rot = Rotations[i](t) + index = searchsortedfirst(S.orderedLists[i], t_rot) + insert!(S.orderedLists[i], index, t_rot) end end +function getPossibleNeighbors(S::Shape) + possibleSpots = Set{Tuple{Int64, Int64, Int64}}() + for p ∈ S.cubes + push!(possibleSpots, + p + (1, 0, 0), + p + (0, 1, 0), + p + (0, 0, 1), + p - (1, 0, 0), + p - (0, 1, 0), + p - (0, 0, 1) + ) + end + spots = setdiff(possibleSpots, S.cubes) + return spots +end + function getCube() return Shape( - {(0, 0, 0)}, - {(0, 0, 0)}, + Set([(0, 0, 0)]), + Set([(0, 0, 0)]), [ [(0, 0, 0)], [(0, 0, 0)], diff --git a/julia/SortingModifiers.jl b/julia/SortingModifiers.jl deleted file mode 100644 index abfa778..0000000 --- a/julia/SortingModifiers.jl +++ /dev/null @@ -1,59 +0,0 @@ - -const global _comp01(x) = (x[1], x[2], x[3]) -const global _comp02(x) = (x[2], x[3], x[1]) -const global _comp03(x) = (x[3], x[1], x[2]) - -const global _comp04(x) = (x[1], x[2], -x[3]) -const global _comp05(x) = (x[2], x[3], -x[1]) -const global _comp06(x) = (x[3], x[1], -x[2]) - -const global _comp07(x) = (x[1], -x[2], x[3]) -const global _comp08(x) = (x[2], -x[3], x[1]) -const global _comp09(x) = (x[3], -x[1], x[2]) - -const global _comp10(x) = (x[1], -x[2], -x[3]) -const global _comp11(x) = (x[2], -x[3], -x[1]) -const global _comp12(x) = (x[3], -x[1], -x[2]) - -const global _comp13(x) = (-x[1], x[2], x[3]) -const global _comp14(x) = (-x[2], x[3], x[1]) -const global _comp15(x) = (-x[3], x[1], x[2]) - -const global _comp16(x) = (-x[1], x[2], -x[3]) -const global _comp17(x) = (-x[2], x[3], -x[1]) -const global _comp18(x) = (-x[3], x[1], -x[2]) - -const global _comp19(x) = (-x[1], -x[2], x[3]) -const global _comp20(x) = (-x[2], -x[3], x[1]) -const global _comp21(x) = (-x[3], -x[1], x[2]) - -const global _comp22(x) = (-x[1], -x[2], -x[3]) -const global _comp23(x) = (-x[2], -x[3], -x[1]) -const global _comp24(x) = (-x[3], -x[1], -x[2]) - -const global SortingModifiers = [ - _comp01, - _comp02, - _comp03, - _comp04, - _comp05, - _comp06, - _comp07, - _comp08, - _comp09, - _comp10, - _comp11, - _comp12, - _comp13, - _comp14, - _comp15, - _comp16, - _comp17, - _comp18, - _comp19, - _comp20, - _comp21, - _comp22, - _comp23, - _comp24 -] diff --git a/julia/TupleMisc.jl b/julia/TupleMisc.jl new file mode 100644 index 0000000..70922d9 --- /dev/null +++ b/julia/TupleMisc.jl @@ -0,0 +1,3 @@ + +Base.:+(X::Tuple{Integer, Integer, Integer}, Y::Tuple{Integer, Integer, Integer}) = (X[1]+Y[1], X[2]+Y[2], X[3]+Y[3]) +Base.:-(X::Tuple{Integer, Integer, Integer}, Y::Tuple{Integer, Integer, Integer}) = (X[1]-Y[1], X[2]-Y[2], X[3]-Y[3]) diff --git a/julia/results.bin b/julia/results.bin new file mode 100644 index 0000000..49e5e0d Binary files /dev/null and b/julia/results.bin differ