# author: sohrab # last editied: 140906 # # a 3d extension of ghost diagrams (c) pfh # # (0,0,0) defined as the back-top-left corner of the space # x runs to the right, z forward and y downward # # blocks are created from a string of 6 characters # letters match the same letters of the other case # digits match themselves # # |z # |_____ # / x # y/ # # " " for empty cells # # TODO: # back-tracking: deadends, store space, try another space, check its # not the crappy first space # # # this program is not complete... # from visual import * import random import visual import time __version__='0.000001' class escape (Exception): """just a escape route when a match is found in Asseble loop""" pass class cell: """this is the shape""" def __init__(self, shape): #*instance variable self.shape = (shape!=None and len(shape)==6) and shape or " " self.visual = None #*constants #number of faces self.faceNo = 6 #rotation self.RIGHT = 0 self.UP = 1 self.CLOCKWISE = 2 def rotate (self, direction, shape = None): """returns the orientation if the shape is rotated in 'direction'""" original = (shape != None and len(shape)==self.faceNo) and shape or self.shape #change of index for each rotation (self.rotate) rotation = {0:(1,2,3,0,4,5), 1:(5,1,4,3,0,2), 2:(0,4,2,5,3,1) } #right, up, clockwise s = '' #return value if rotation.has_key(direction): for x in rotation[direction]: s += original[x] return s def orientations(self): """returns tuple of all the possible cells created by rotating the original cell""" #different orientaions of a shape (self.orientation) #-1 is just to make tuples since one member tuple is not really a tuple faceRotations = ( (), (self.RIGHT,-1), (self.RIGHT, self.RIGHT), #face0, face1, face2 (self.RIGHT, self.RIGHT, self.RIGHT), #face3 (self.UP,-1), (self.UP,self.UP,self.UP)) #face4,face5 rotationsPerFace = ( (), (self.CLOCKWISE,-1), (self.CLOCKWISE, self.CLOCKWISE), (self.CLOCKWISE, self.CLOCKWISE, self.CLOCKWISE)) answer = [] #return variable for x in faceRotations: #for every face for y in rotationsPerFace: #for clockwise rotations on every face new = self.shape for a in x: new = self.rotate(shape=new, direction=a) for b in y: new = self.rotate(shape=new, direction=b) if len(new) == self.faceNo and new not in answer: answer.append(cell(new)) return tuple(answer) class draw: """these are the static modules to create the 3d space""" def __init__(self, dimensions, sph=False): scene.title = 'ghost-space' scene.center = len(dimensions)==3 and \ (dimensions[0]/2, dimensions[1]/2, dimensions[2]/2) or (5,5,5) self.sph = sph #color assignment self.colors = {} con1 = ('a', 'b', 'c', 'd', 'e', 'f', '1', '2', '3', '4', '5', '6') con2 = ('A', 'B', 'C', 'D', 'E', 'F', '1', '2', '3', '4', '5', '6') for x in range(12): self.colors[con1[x]]=(random.random(), random.random(), random.random()) c = self.colors[con1[x]] self.colors[con2[x]]=(c[0]+.01, c[1], c[2]) def add(self, object, coordinate): """creates a 3d object of the argument""" axes = [ (0,0,.5), #face0 (.5,0,0), #face1 (0,0,-.5), #face2 (-.5,0,0), #face3 (0,.5,0), #face4 (0,-.5,0)] #face5 f = frame(pos=(coordinate[0], ass.dimensions[1]-coordinate[1], coordinate[2])) f.visible = 0 if self.sph == True: sphere(frame=f, radius=0.25, color=color.white) else: box(frame=f, length=0.25, width=0.25, height=0.25, color=color.white) for a in range(6): if object.shape[a]!=' ': cylinder(frame=f, radius=0.1, color=self.colors[object.shape[a]], axis=axes[a]) f.visible = 1 object.visual = f def remove(self, object): """removes the object from 3d scene""" for x in object.visual.objects: x.visible = 0 object.visual.visible = 0 class assembler: """used to assemble 'cell' objects and visually represents them using 'draw' class""" def __init__(self, cells, dimensions=(5,5,5), closed=False, step=False, rand=False, sph=False): self.dimensions = dimensions self.cells = cells self.step = step self.closed = closed self.sph = sph self.rand = rand #hash-table for the space containing cell objects # { (x,y,z):cell,... } self.space={} #matching faces self.matches = { 'a':'A', 'A':'a', 'b':'B', 'B':'b', 'c':'C', 'C':'c', 'd':'D', 'D':'d', 'e':'E', 'E':'e', 'f':'F', 'F':'f', '1':'1', '2':'2', '3':'3', '4':'4', '5':'5', '6':'6', ' ':' ' } #index of matching face e.g. face0 of this cube matches face2 of cube at the front self.matchingFaces = { 0:2, 1:3, 2:0, 3:1, 4:5, 5:4 } #Queue of coordinates to be check to be checked self.checkQueue = [] #for backtracking, stores the coordinates in the order self.doneList = [] def neighbours(self, coordinate): """returns a tuple of all the neighbouring cells of coordinate, putting 'None' for the ones that are outta bounds""" if len(coordinate)==3: neigh=[] neigh.append((coordinate[0],coordinate[1],coordinate[2]+1)) neigh.append((coordinate[0]+1,coordinate[1],coordinate[2])) neigh.append((coordinate[0],coordinate[1],coordinate[2]-1)) neigh.append((coordinate[0]-1,coordinate[1],coordinate[2])) neigh.append((coordinate[0],coordinate[1]+1,coordinate[2])) neigh.append((coordinate[0],coordinate[1]-1,coordinate[2])) temp=[] for (a,b,c) in neigh: #checking for out-of-bound and already filled coordinates if -10 and current not in lastDeleted): self.add(n, drawer) toDelete = lastDeleted = None raise escape #found a match else: #all possibilities exhasted #start over: lastDeleted = [] amount = len(self.space) if amount > 0: for x in range(random.randint(1,amount)): toDelete = self.doneList.pop() lastDeleted.append( (toDelete, self.space[toDelete].shape)) self.remove(toDelete, drawer) lastDeleted = lastDeleted[-6:] # last six shapes deleted except escape: pass def validShape(shape): """validates the shape""" if len(shape)!=cell('').faceNo: return False for a in shape: if not assembler((" "," 1 ")).matches.has_key(a): return False return True """ try 6-letter shapes and one-letter option code: option codes: s: step through the process c: closed contruction - does everything but that r: randomised choices for each cell d: cutomised dimensions h: uses spheres instead of boxes """ if __name__=='__main__': print 'enter shapes and options:' a = raw_input() if len(a)==6: a + ',x' #stops tuple to disintegrate single shapes into letters arg = a.split(',') sh = [] step = False closed = False dim = (5,5,5) rand = False sph = False for n in arg: if validShape(n): sh.append(cell(n)) elif (len(n) == 1): scene.fullscreen = (n == 'f') and 1 or 0 if n == 's': step = True if n == 'h': sph = True if n == 'c': closed = True if n == 'r': rand = True if n == 'd': d = raw_input('dimensions x,y,z: ') dsplit = d.split(',') if len(dsplit) == 3 and int(dsplit[0])>0 and int(dsplit[1])>0 and int(dsplit[2])>0: dim = (int(dsplit[0]), int(dsplit[1]), int(dsplit[2])) ass = assembler(cells=sh, step=step, dimensions=dim, closed=closed, rand=rand, sph=sph) ass.execute() print 'done... press any key to exit' a = raw_input()