diff --git a/cube_puzzle.txt b/cube_puzzle.txt new file mode 100644 index 0000000..90806c5 --- /dev/null +++ b/cube_puzzle.txt @@ -0,0 +1,25 @@ +[[[1, 1, 1, 1, 22], + [6, 6, 6, 6, 22], + [2, 6, 9, 22, 22], + [9, 9, 9, 9, 22], + [10, 15, 15, 15, 15]], + [[2, 11, 1, 19, 20], + [2, 11, 13, 19, 21], + [2, 11, 11, 19, 23], + [2, 11, 17, 19, 24], + [10, 14, 18, 15, 25]], + [[3, 3, 3, 3, 20], + [4, 13, 13, 19, 21], + [8, 8, 8, 8, 23], + [10, 14, 17, 17, 24], + [10, 14, 18, 18, 25]], + [[4, 12, 3, 20, 20], + [4, 12, 13, 21, 21], + [4, 12, 8, 23, 23], + [4, 12, 17, 24, 24], + [10, 14, 18, 25, 25]], + [[5, 5, 5, 5, 20], + [7, 5, 13, 16, 21], + [7, 12, 16, 16, 23], + [7, 7, 17, 16, 24], + [7, 14, 18, 16, 25]]] diff --git a/matplottest.py b/matplottest.py new file mode 100644 index 0000000..3c571de --- /dev/null +++ b/matplottest.py @@ -0,0 +1,17 @@ +import matplotlib.pyplot as plt +import numpy as np + +x = np.linspace(0, 6*np.pi, 100) +y = np.sin(x) + +# You probably won't need this if you're embedding things in a tkinter plot... +plt.ion() + +fig = plt.figure() +ax = fig.add_subplot(111) +line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma + +for phase in np.linspace(0, 10*np.pi, 500): + line1.set_ydata(np.sin(x + phase)) + fig.canvas.draw() + fig.canvas.flush_events() diff --git a/solve.py b/solve.py new file mode 100644 index 0000000..1e0e797 --- /dev/null +++ b/solve.py @@ -0,0 +1,129 @@ +import pprint +import operator +import numpy as np +import math +from copy import copy, deepcopy + +piece=[[0,0,0],[0,1,0],[0,2,0],[0,3,0],[1,2,0]] +sizeofcube=5 + +def init_cube(size=sizeofcube): + return [[[0 for x in range(0,size)] for y in range(0,size)] for z in range(0,size)] + +def move_start_position(piece,index): + return [np.subtract(x, piece[index]) for x in piece] + +def draw_cube(cube): + from mpl_toolkits.mplot3d import Axes3D + import matplotlib.pyplot as plt + fig = plt.figure() + ax = fig.gca(projection='3d') + ax.set_aspect('equal') + ax.set_xlabel('x', fontsize=10) + ax.set_ylabel('y', fontsize=10) + ax.set_zlabel('z', fontsize=10) + + ma=np.array(cube) + ax.voxels(ma, edgecolor="k") + plt.show() + +def put_piece_in_cube(piece,cube,position,index): + cursors = [np.add(position,p) for p in piece] + in_cube = [ max(c) < len(cube) and min(c) >= 0 for c in cursors] + if all(in_cube): + for cursor in cursors: + try: + if cube[cursor[0]][cursor[1]][cursor[2]]!=0: + return False + except: + return False + for cursor in cursors: + cube[cursor[0]][cursor[1]][cursor[2]]=index + return True + else: + return False + +def rotate_vector(vector,axis,angle): + x,y,z=vector + angle=math.radians(angle) + if axis == "z": + return (int(round((x*math.cos(angle)) - (y*math.sin(angle)))),int(round((x*math.sin(angle)) + (y*math.cos(angle)))),z) + if axis == "y": + return (int(round(x*math.cos(angle) + z*math.sin(angle))),y,int(round(-x*math.sin(angle) + z*math.cos(angle)))) + if axis == "x": + return (x,int(round(y*math.cos(angle) - z*math.sin(angle))),int(round(y*math.sin(angle) + z*math.cos(angle)))) + +def rotate_piece(piece,axis,angle): + return [rotate_vector(x, axis, angle) for x in piece] + +def shift_piece(piece,anchor_index): + anchor=piece[anchor_index] + return [np.subtract(p,anchor) for p in piece] + +def generate_rotations(piece): + all_rotations=set() + for i in range(0,4): + for j in range(0,4): + for k in range(0,4): + for p in range(0,5): + rotated_piece=rotate_piece(rotate_piece(rotate_piece(shift_piece(piece,p),"x",k*90),"y",j*90),"z",i*90) + all_rotations.add(tuple(rotated_piece)) + return frozenset(all_rotations) + +def find_empty_spot(cube): + for z in range(0,sizeofcube): + for y in range(0,sizeofcube): + for x in range(0,sizeofcube): + if cube[x][y][z]==0: + return (x,y,z) + return None + +def solve(cube,index): + #make copy of cube + global maxindex + if index > maxindex: + print(index) + maxindex=index + + backup=deepcopy(cube) + # draw_cube(backup) + #make copy of available pieces + global all_rotations + pieces=set(all_rotations.copy()) + + # print("{}:find empty spot#########################".format(index)) + empty_pos=find_empty_spot(backup) + + if empty_pos==None: + pprint.pprint(cube) + draw_cube(cube) + return True + else: + (x,y,z)=empty_pos + # print("{}:empty_spot at ({},{},{})".format(index,x,y,z)) + #found empty space > trying to fill it + while len(pieces)>0: + #use copy of cube without my parts + local_cube=deepcopy(backup) + piece=pieces.pop() + if put_piece_in_cube(piece, local_cube, (x,y,z), index): + # print("{}:found fitting piece {} ({} left)".format(index,piece,len(pieces))) + if solve(local_cube, index+1): + return True + else: + # print("{}:removing ({},{},{}):{}".format(index,x,y,z,len(pieces))) + pass + #nothing fits return fail + return False + + +maxindex=0 + + +def main(): + global all_rotations + all_rotations=generate_rotations(piece) + solve(init_cube(),1) + +if __name__ == '__main__': + main() diff --git a/solve2.py b/solve2.py new file mode 100644 index 0000000..7c5f75e --- /dev/null +++ b/solve2.py @@ -0,0 +1,161 @@ +import pprint +import operator +import numpy as np +import math +from copy import copy, deepcopy +import profile + +piece=[[0,0,0],[0,1,0],[0,2,0],[0,3,0],[1,2,0]] +sizeofcube=5 + +def init_cube(size=sizeofcube): + return [[[0 for x in range(0,size)] for y in range(0,size)] for z in range(0,size)] + +def move_start_position(piece,index): + return [np.subtract(x, piece[index]) for x in piece] + +def draw_cube(cube): + from mpl_toolkits.mplot3d import Axes3D + import matplotlib.pyplot as plt + fig = plt.figure() + ax = fig.gca(projection='3d') + ax.set_aspect('equal') + ax.set_xlabel('x', fontsize=10) + ax.set_ylabel('y', fontsize=10) + ax.set_zlabel('z', fontsize=10) + + ma=np.array(cube) + ax.voxels(ma, edgecolor="k") + plt.show() + +def set_cube_vals(cursors,cube,value): + for cursor in cursors: + cube[cursor[0]][cursor[1]][cursor[2]]=value + +def is_valid(piece,position): + global sizeofcube + upper_x=sizeofcube-position[0] + upper_y=sizeofcube-position[1] + upper_z=sizeofcube-position[2] + for (x,y,z) in piece: + if x<-position[0] or x>upper_x: + return False + if y<-position[1] or y>upper_y: + return False + if z<-position[2] or z>upper_z: + return False + return True + +def put_piece_in_cube(piece,cube,position,index): + if is_valid(piece,position): + # cursors = [np.add(position,p) for p in piece] + # for cursor in cursors: + cursors=[] + for (x,y,z) in piece: + cursor=[(x+position[0]),(y+position[1]),(z+position[2])] + cursors.append(cursor) + try: + if cube[cursor[0]][cursor[1]][cursor[2]]!=0: + return False + except: + return False + set_cube_vals(cursors, cube, index) + return True + else: + return False + +def remove_piece_in_cube(piece,cube,position): + cursors = [np.add(position,p) for p in piece] + set_cube_vals(cursors, cube, 0) + +def rotate_vector(vector,axis,angle): + x,y,z=vector + angle=math.radians(angle) + if axis == "z": + return (int(round((x*math.cos(angle)) - (y*math.sin(angle)))),int(round((x*math.sin(angle)) + (y*math.cos(angle)))),z) + if axis == "y": + return (int(round(x*math.cos(angle) + z*math.sin(angle))),y,int(round(-x*math.sin(angle) + z*math.cos(angle)))) + if axis == "x": + return (x,int(round(y*math.cos(angle) - z*math.sin(angle))),int(round(y*math.sin(angle) + z*math.cos(angle)))) + +def rotate_piece(piece,axis,angle): + return [rotate_vector(x, axis, angle) for x in piece] + +def shift_piece(piece,anchor_index): + anchor=piece[anchor_index] + return [np.subtract(p,anchor) for p in piece] + +def generate_rotations(piece): + all_rotations=set() + for i in range(0,4): + for j in range(0,4): + for k in range(0,4): + for p in range(0,len(piece)): + rotated_piece=rotate_piece(rotate_piece(rotate_piece(shift_piece(piece,p),"x",k*90),"y",j*90),"z",i*90) + all_rotations.add(tuple(rotated_piece)) + return frozenset(all_rotations) + +def find_empty_spot(cube): + for z in range(0,sizeofcube): + for y in range(0,sizeofcube): + for x in range(0,sizeofcube): + if cube[x][y][z]==0: + return (x,y,z) + return None + +def printstats(): + global stat_counter + global stats + stat_counter=stat_counter+1 + if stat_counter%10000==0: + print(stat_counter) + for x in stats: + print("{}:{}".format(x,stats[x])) + if x>5: + break + + +def solve(cube,index): + #make copy of cube + printstats() + + global all_rotations + pieces=set(all_rotations.copy()) + + # print("{}:find empty spot#########################".format(index)) + empty_pos=find_empty_spot(cube) + + if empty_pos==None: + pprint.pprint(cube) + draw_cube(cube) + return True + else: + (x,y,z)=empty_pos + while len(pieces)>0: + #use copy of cube without my parts + piece=pieces.pop() + if put_piece_in_cube(piece, cube, (x,y,z), index): + # print("{}:found fitting piece {} ({} left)".format(index,piece,len(pieces))) + stats[index]=len(pieces) + if solve(cube, index+1): + return True + else: + remove_piece_in_cube(piece, cube, (x,y,z)) + #nothing fits return fail + return False + + +maxindex=0 +stat_counter=0 +stats=dict() +last_stats=dict() +def main(): + + global all_rotations + all_rotations=generate_rotations(piece) + print(len(all_rotations)) + solve(init_cube(),1) + +if __name__ == '__main__': + # profile.run('main()') + main()