# Written by Naughty Nathan (nathan@naughtynathan.co.uk) # over the May Bank Holiday 2010 # # This python script is a little labyrinth game based on the # logic rules of Picross/Hanjie. # The basic idea is to figure out the unique path from the # (S)tart marker to the (E)nd. # # The path MUST go through EVERY square in the grid once and cannot # cross over or go back on itself. The numbers on the top and left # edges of the puzzle board denote how many wall sections exist # somewhere along the respective grid line. For example: # # 2 1 4 |----------| # # means that along this horizontal grid-line there are 3 sections # of wall. The first is 2 grid units long, the second is a single # unit and finally the third wall section is 4 units long. Every # section of wall must be separated by a gap of at least one grid # square. There may of course be gaps before and after any wall # sections. # Full instructions and tips can be found online at: # # http://www.naughtynathan.co.uk/?cat=11 # # Feel free to visit and leave comments, feedback, future requests, etc. # You will also be able to find Labyrinth updates and new puzzle packs. # as well as other Maya/MEL/Python stuff... import maya.cmds as mc import maya.mel as mel import random __version__ = 1.0 __author__ = 'Naughty Nathan' # globals: gameBoard = '' mainDataGroup = '' helper = '' xSize,ySize = 0,0 wallThickness = 0.225 pathThickness = 0.125 # shaders: boardSG = 'boardSG' wallsSG = 'wallsSG' blackSG = 'blackSG' redSG = 'redSG' ctx = 'labyrinthCtx' currentPuzzle = 0 winCheck = [[],[],[],0] # hPathData, vPathData, IdList, wallCount doReset = 0 # this is the default "built-in" list of puzzles, but we can override this with puzzle-packs (coming in v2.0) labyrinthPuzzles = [ ['Puzzle 1 - 8x8', (181, 203, 132, 15, 177, 222, 141), (237, 146, 37, 155, 204, 255, 153), (0, 1), (2, 4)], ['Puzzle 2 - 8x8', (255, 153, 231, 16, 231, 153, 255), (153, 102, 153, 36, 153, 102, 153), (4, 3), (3, 3)], ['Puzzle 3 - 8x8', (177, 104, 151, 239, 247, 41, 159), (135, 99, 133, 59, 132, 201, 231), (1, 2), (6, 4)], ['Puzzle 4 - 8x8', (153, 195, 129, 96, 189, 66, 189), (249, 159, 111, 148, 111, 135, 249), (3, 4), (3, 3)], ['Puzzle 5 - 8x8', (183, 109, 128, 120, 177, 27, 141), (157, 252, 57, 135, 204, 225, 255), (1, 1), (3, 0)], ['Puzzle 6 - 8x8', (225, 240, 129, 127, 128, 205, 153), (183, 239, 151, 47, 241, 236, 145), (6, 1), (3, 7)], ['Puzzle 7 - 8x8', (255, 129, 198, 233, 214, 137, 7), (189, 66, 189, 198, 253, 230, 193), (7, 7), (3, 4)], ['Puzzle 8 - 8x8', (141, 211, 161, 64, 161, 211, 141), (153, 189, 126, 189, 219, 231, 153), (1, 1), (6, 1)], ['Puzzle 9 - 8x8', (153, 252, 161, 16, 137, 123, 177), (159, 255, 217, 140, 49, 155, 249), (5, 2), (2, 6)], ['Puzzle 10 - 8x8', (131, 199, 239, 220, 143, 7, 129), (152, 129, 195, 231, 255, 243, 225), (0, 0), (6, 1)], ['Puzzle 11 - 8x8', (129, 244, 203, 253, 227, 63, 129), (135, 219, 165, 211, 141, 195, 225), (5, 4), (1, 3)], ['Puzzle 12 - 8x8', (199, 233, 211, 141, 99, 159, 231), (129, 30, 165, 219, 183, 73, 176), (3, 5), (0, 7)], ['Puzzle 13 - 8x8', (145, 227, 249, 255, 255, 207, 159), (129, 15, 131, 6, 225, 195, 129), (0, 3), (2, 2)], ['Puzzle 14 - 8x8', (249, 135, 204, 231, 51, 225, 159), (141, 99, 161, 126, 141, 198, 177), (4, 2), (4, 3)], ['Puzzle 15 - 8x8', (54, 153, 207, 231, 3, 231, 153), (131, 196, 179, 121, 188, 199, 179), (0, 7), (0, 0)], ['Puzzle 16 - 8x8', (231, 249, 243, 129, 255, 243, 249), (129, 222, 237, 19, 137, 16, 129), (3, 3), (3, 6)], ['puzzle 17 - 8x8', (225, 216, 129, 198, 33, 255, 240), (223, 167, 223, 185, 31, 140, 49), (7, 0), (7, 7)] , ['Puzzle 18 - 8x8', (141, 198, 129, 254, 1, 230, 153), (255, 129, 180, 111, 183, 207, 177), (3, 2), (6, 2)], ['Puzzle 19 - 8x8 (Tricky!)', (159, 57, 242, 229, 203, 148, 233), (233, 214, 173, 88, 177, 99, 135), (2, 0), (7, 4)], ['Puzzle 20 - 16x16', (33153, 55491, 60419, 55303, 45833, 28563, 34605, 56283, 58941, 61851, 58893, 64536, 63885, 61470, 57729), (63489, 40947, 49199, 32863, 64703, 65407, 65273, 2332, 65039, 61471, 59315, 52801, 33231, 147, 32865), (2, 2), (5, 12)], ['Bonus Puzzle!', (33, 3, 33, 48, 33, 3, 39, 63, 15, 33, 60, 51, 33, 51, 60, 33, 12, 33, 48, 39, 15, 63, 33, 48, 33, 3, 33, 48, 33), (965725209, 1070052927, 1066170495, 1071625983, 640877414), (0, 4), (0, 5)] ] # this function creates (or deletes) an edge element, either a wall or a path def createElement(edge,path): vtxPair = mc.ls(mc.polyListComponentConversion(edge,fe=1,tv=1),fl=1) P1 = mc.pointPosition(vtxPair[0],w=1) P2 = mc.pointPosition(vtxPair[1],w=1) # here we do something a bit mischevious: make variables that look like 0 and 1 l = (P1[0] == P2[0]) # is the element vertical? O = not l # get the edge component index, we use this to ID the element itemId = int(edge.split('[')[-1].split(']')[0]) # wall defaults: object = 'labyrinthWall_%d' % itemId SG = wallsSG yh = 0.5 group = mainDataGroup+'|walls' hSize = (1+wallThickness,wallThickness) vSize = (wallThickness,1+wallThickness) if path: # path defaults: object = 'labyrinthPath_%d' % itemId SG = redSG yh = 0.1 group = mainDataGroup+'|path' hSize = (pathThickness,1+pathThickness) vSize = (1+pathThickness,pathThickness) pos = ( (P1[0]+P2[0])/2 , (P1[2]+P2[2])/2 ) elemSize = hSize pathId = int(pos[0]-.5) , int(pos[1]-1) if l: # vertical! elemSize = vSize pathId = int(pos[0]-1) , int(pos[1]-.5) # winCheck is the variable we use to check if the puzzle is complete. # at the start winCheck is full of the game stats, and each time we # insert a new wall or path we SUBTRACT it's ID value from winCheck. # When winCheck is empty (all zero) the puzzle is completed correctly. # obviously, if wall or path elements are ERASED we ADD the IDs back on. global winCheck # However, because the wall locations don't actually exist anywhere in # the data (only the path locations are stored) we have to use a cheap # wall-counting method to keep track of the walls. # if user is holding a keyMod (ctrl?) then DELETE the wall/path element if mc.getModifiers(): if mc.objExists(object): mc.delete(object) # also, tag the path back into the arrayId winCheck[2][itemId] = 0 if path: winCheck[O][pathId[O]] += 2**pathId[l] else: winCheck[3] += 1 elif not mc.objExists(object) and not winCheck[2][itemId]: cube = mc.polyCube(n=object,w=elemSize[0],h=yh,d=elemSize[1])[0] winCheck[2][itemId] = 1 if path: winCheck[O][pathId[O]] -= 2**pathId[l] else: winCheck[3] -= 1 mc.sets(cube, e=1, forceElement=SG) if not path: mc.sets(cube+'.f[1]', e=1, forceElement=blackSG) mc.move(pos[0],0,pos[1],cube,a=1,rpr=1) mc.parent(cube, group) def createPath(idA,idB,horiz): # if the path is horizontal just count between faces faces = [] if horiz: faces = [gameBoard+'.f[%d]' % fId for fId in range(idA,idB+1)] else: faces = [gameBoard+'.f[%d]' % fId for fId in range(idA,idB+1,xSize)] if len(faces) > 1: internalEdges = mc.ls(mc.polyListComponentConversion(faces,ff=1,te=1,internal=1),fl=1) # maya is a fucking disgrace, -internal ALWAYS includes BORDER edges.. FFS. shareEdges = [edge for edge in internalEdges if len(mc.ls(mc.polyListComponentConversion(edge,fe=1,tf=1),fl=1))==2] for edge in shareEdges: createElement(edge,1) def isPath(point): remX = point[0] % 1 remY = point[2] % 1 # if the starting hit points are within the center of a face (between 0.2 and 0.8) # we're plotting a path, not a wall. return remX > .2 and remX < .8 and remY > .2 and remY < .8 # this is fired when the dragger button is released def ctxAction(): # if the reset button was pressed during the drag, reset now on mouse release: if doReset: init(reset=1) return if not mc.objExists(helper): return mc.delete(helper) S = mc.draggerContext(ctx, q=1, ap=1) E = mc.draggerContext(ctx, q=1, dp=1) if isPath(S): # path: # first, quantize the coords (trunc and clamp) Sx = min(max(0,int(S[0])),xSize) Sy = min(max(0,int(S[2])),ySize) Ex = min(max(0,int(E[0])),xSize) Ey = min(max(0,int(E[2])),ySize) # if the path isn't ortho if (Sx!=Ex) and (Sy!=Ey): return # convert the hit pos into faceID faceIds = [ Sx+(Sy*xSize) , Ex+(Ey*xSize) ] faceIds.sort() horiz = (Sy==Ey) createPath(faceIds[0],faceIds[1],horiz) else: # wall: # first, quantize the coords (round and clamp) Sx = min(max(0,round(S[0])),xSize) Sy = min(max(0,round(S[2])),ySize) Ex = min(max(0,round(E[0])),xSize) Ey = min(max(0,round(E[2])),ySize) # if the path isn't ortho if (Sx!=Ex) and (Sy!=Ey): return # starting OR ending on the board edge is valid, but BOTH is not if (Sx==Ex==0) or (Sx==Ex==xSize): return if (Sy==Ey==0) or (Sy==Ey==ySize): return # convert the hit pos into vtxID vtxIdA = ((xSize+1)*Sy)+Sx vtxIdB = ((xSize+1)*Ey)+Ex edges = mc.polySelect(gameBoard, ass=1, sep=(vtxIdA,vtxIdB)) if edges: for edge in edges: createElement(edge,0) # wall mc.select(cl=1) mc.refresh(cv=1) # finally we'll check if the puzzle is complete? # however, this only checks the path, and ideally we want to check the walls too! if not sum(winCheck[0]) and not sum(winCheck[1]) and not winCheck[3]: winEvent() # dragging the ctx updates the helper line def ctxDrag(): ap = mc.draggerContext(ctx, q=1, ap=1) dp = mc.draggerContext(ctx, q=1, dp=1) if isPath(ap): dp = int(dp[0])+0.5 , 0.06 , int(dp[2])+0.5 # clamp the end point to the board bounds dp = min(max(.5,dp[0]),xSize-.5) , 0.06 , min(max(.5,dp[2]),ySize-.5) else: dp = round(dp[0]) , 0.06 , round(dp[2]) # clamp the end point to the board bounds dp = min(max(0,dp[0]),xSize) , 0.06 , min(max(0,dp[2]),ySize) if not mc.objExists(helper): return mc.move(dp[0],dp[1],dp[2],helper+'.cv[1]',a=1) mc.refresh(cv=1) # clicking the ctx creates the helper line def ctxClick(): global helper ap = mc.draggerContext(ctx, q=1, ap=1) color = 6 # wall helper = blue # if we're clicking on the UI controls: if ap[0]>-0.05 and ap[0]<0.2 and ap[2]>ySize+0.05 and ap[2]xSize: return if ap[2] < 0 or ap[2]>ySize: return # create the helper curve to show where the user is dragging helper = mc.curve(n='labyrinthHelper',d=1,p=(ap,(ap[0]+.01,ap[1]+.01,ap[2]+.01))) if not isPath(ap): mc.parent(mc.offsetCurve(helper,d=0.02,ugn=1,nr=(0,-1,0))[0],helper) mc.parent(mc.offsetCurve(helper,d=-.02,ugn=1,nr=(0,-1,0))[0],helper) mc.setAttr(helper+'.overrideEnabled',1) mc.setAttr(helper+'.overrideColor',color) mc.select(cl=1) def makeMaterial(name,colour,c=0,trans=0): mat = name+'Mat' SG = name+'SG' if not mc.objExists(SG): material = mc.shadingNode('lambert', asShader=1, n=mat) SG = mc.sets(renderable=1, noSurfaceShader=1, empty=1, name=SG) mc.connectAttr((material+'.outColor'),(SG+'.surfaceShader'),f=1) mc.setAttr((material+'.color'),colour[0]*c,colour[1]*c,colour[2]*c) mc.setAttr((material+'.transparency'),trans,trans,trans) mc.setAttr((material+'.incandescence'),colour[0],colour[1],colour[2]) return SG # this function makes 2D filled text from Maya text curves def makeText(text): obj = mc.textCurves(ch=0, t=text)[0] for crvGrp in mc.listRelatives(obj): curves = mc.listRelatives(crvGrp) textFill = mc.planarSrf(crvGrp, ch=0, o=1, po=0)[0] mc.displaySmoothness(textFill, pointsShaded=16) mc.sets(textFill, e=1, forceElement=blackSG) mc.parent(textFill, crvGrp) mc.delete(curves) # move the text object pivot to the center mc.xform(obj,cp=1) mc.scale(.15, .15, .15, obj) mc.rotate(-90, 0, 0, obj, os=1, r=1) return obj # this function generates the 2D numbers along the top and left sides of the puzzle board def createEdgeNumbers(array,vertical): row = 1 numName = ('colNumbers','rowNumbers') numbers = mc.group(empty=1,name=numName[vertical]) itemRange = ySize if vertical: itemRange = xSize global winCheck for item in array: item = (item<<1)+1 # shift left and add a true to start of array (bit 0) count = 0 posOffset = 0 # we work backwards through the array (bot-up / r-l) for i in range(itemRange,-1,-1): if not item&(2**i): count += 1 else: if count or (not i and not posOffset): numStr = ('%d' % count) text = makeText(numStr) if vertical: mc.move(posOffset-0.75, 0, row, text, a=1, rpr=1) else: mc.move(row, 0, posOffset-0.75, text, a=1, rpr=1) posOffset -= 0.8 winCheck[3] += count count = 0 mc.parent(text, numbers) row += 1 mc.parent(numbers, mainDataGroup) def winEvent(): back = mc.polyPlane(w=xSize+1,h=xSize/2.0,sx=5,sy=3)[0] mc.displaySmoothness(back,polygonObject=3) mc.move(xSize/2.0, .45, ySize/2.0, back, a=1, rpr=1) bMat = makeMaterial('white',(1,1,1),trans=0.333) mc.sets(back, e=1, forceElement=bMat) congrat = makeText('Congratulations!!') tagLine = makeText('You have solved the Labyrinth!') win = makeMaterial('win',(1,.25,0)) mc.sets(congrat, e=1, forceElement=win) mc.sets(tagLine, e=1, forceElement=blackSG) mc.move(xSize/2.0, .5, (ySize/2.0)-xSize/10.0, congrat, a=1, rpr=1) mc.move(xSize/2.0, .5, (ySize/2.0)+xSize/8.0, tagLine, a=1, rpr=1) cScale = xSize/5.0 mc.scale(cScale, cScale, cScale, congrat,r=1) mc.scale(cScale/2, cScale/2, cScale/2, tagLine,r=1) mc.parent(back,congrat,tagLine,mainDataGroup) mc.select(cl=1) mc.setToolTo('selectSuperContext') def init(reset=0): # unfortunately, Maya is stupid. I'm forcing cm so it all works right mc.currentUnit(linear='cm') # initialise all the relevent variables for this board: name,hori,vert,sCoord,eCoord = labyrinthPuzzles[currentPuzzle] global xSize global ySize xSize,ySize = len(hori)+1,len(vert)+1 global winCheck IdArray = [0]*((xSize*(xSize+1)) + (ySize*(ySize+1))) winCheck = [list(hori),list(vert),IdArray,0] global doReset doReset = 0 # create the shaders we need global boardSG global wallsSG global blackSG global redSG boardSG = makeMaterial('board',(.5,.5,.45),c=1) wallsSG = makeMaterial('walls',(.25,.25,.25),c=1) blackSG = makeMaterial('black',(0,0,0)) redSG = makeMaterial('red',(1,.25,.25)) # first we hide all the existing DAG objects, in case the user has stuff in his scene mc.hide(all=1) # now we create the groups global mainDataGroup if mc.objExists('labyrinthData'): mc.delete('labyrinthData') mainDataGroup = mc.group(empty=1, name='labyrinthData') mc.createNode('transform',name='walls',p=mainDataGroup,ss=1) mc.createNode('transform',name='path',p=mainDataGroup,ss=1) # create the layer if mc.objExists('labyrinthLayer'): mc.delete('labyrinthLayer') layer = mc.createDisplayLayer(mainDataGroup,n='labyrinthLayer',nr=1) mc.setAttr(layer+'.displayType', 2) # create the board: global gameBoard gameBoard = mc.polyPlane(n='board',w=xSize, h=ySize, sx=xSize, sy=ySize, ax=(0,-1,0),ch=0)[0] gameLines = mc.polyPlane(n='gridLines',w=xSize, h=ySize, sx=xSize, sy=ySize, ax=(0,1,0),ch=0)[0] gameBase = mc.polyPlane(n='bottom',w=xSize, h=ySize, sx=1, sy=1, ax=(0,-1,0),ch=0)[0] # we need to create the board inverted so the verts/faces are in the correct alignment # so we therefore need to invert the Y so it faces upwards (in case backCulling is on) mc.setAttr(gameBoard+'.sy',-1) mc.setAttr(gameLines+'.template',1) mc.sets(gameBoard, e=1, forceElement=boardSG) mc.sets(gameBase, e=1, forceElement=wallsSG) mc.move(xSize/2.0, -.01, ySize/2.0, gameBoard, r=1,rpr=1) mc.move(xSize/2.0, 0, ySize/2.0, gameLines, r=1,rpr=1) mc.move(xSize/2.0, -.25, ySize/2.0, gameBase, r=1,rpr=1) mc.parent(gameBoard, gameLines, gameBase, mainDataGroup) wallOff = wallThickness/2.0 # create the outer "walls" (add a tiny thickness to avoid z-fighting) tFrame = mc.polyCube(w=xSize+wallThickness, h=0.6, d=wallThickness, ch=0, n='frame_top')[0] mc.move(xSize/2.0, -0.05, 0-wallOff, tFrame, a=1, rpr=1) tlCorner = mc.polyCylinder(r=wallOff, h=0.6, sx=16, ch=0, n='frame_tlCorner')[0] trCorner = mc.polyCylinder(r=wallOff, h=0.6, sx=16, ch=0, n='frame_trCorner')[0] mc.move(-wallOff, -0.05, 0-wallOff, tlCorner, a=1, rpr=1) mc.move(xSize+wallOff, -0.05, 0-wallOff, trCorner, a=1, rpr=1) lFrame = mc.polyCube(w=wallThickness, h=0.6, d=ySize+wallThickness+wallOff, ch=0, n='frame_left')[0] mc.move(-wallOff, -0.05, (ySize/2.0)+(wallOff/2.0), lFrame, a=1, rpr=1) rFrame = mc.polyCube(w=wallThickness, h=0.6, d=ySize+wallThickness+wallOff, ch=0, n='frame_right')[0] mc.move(xSize+wallOff, -0.05, (ySize/2.0)+(wallOff/2.0), rFrame, a=1, rpr=1) bFrame = mc.polyCube(w=xSize, h=0.6, d=wallThickness*2, ch=0, n='frame_bottom')[0] mc.move(xSize/2.0, -0.05, ySize+wallThickness, bFrame, a=1, rpr=1) blCorner = mc.polyCylinder(r=wallThickness, h=0.6, sx=16, ch=0, n='frame_blCorner')[0] brCorner = mc.polyCylinder(r=wallThickness, h=0.6, sx=16, ch=0, n='frame_brCorner')[0] mc.move(0, -0.05, ySize+wallThickness, blCorner, a=1, rpr=1) mc.move(xSize, -0.05, ySize+wallThickness, brCorner, a=1, rpr=1) mc.sets(tFrame,bFrame,lFrame,rFrame,tlCorner,trCorner,blCorner,brCorner, e=1, forceElement=blackSG) mc.sets(tFrame+'.f[0]',bFrame+'.f[2]',lFrame+'.f[4]',rFrame+'.f[5]', e=1, forceElement=wallsSG) footer = makeText('www.naughtynathan.co.uk') mc.move(xSize-1.5, .26, ySize+wallThickness, footer, a=1, rpr=1) mc.scale(.35,.35,.35,footer,r=1) mc.sets(footer, e=1, forceElement=boardSG) resetButton = mc.polySphere(r=wallOff,sx=16,sy=8,ch=0,n='resetButton') mc.move(wallOff, .24, ySize+0.2, resetButton, a=1, rpr=1) mc.sets(resetButton, e=1, forceElement=redSG) mc.parent(tFrame,bFrame,lFrame,rFrame,tlCorner,trCorner,blCorner,brCorner,footer,resetButton, mainDataGroup) # now we have the barrier arrays, determine the edge numbers createEdgeNumbers(hori,0) createEdgeNumbers(vert,1) # now put the S and E in sPos = sCoord # S text = makeText('S') disc = mc.polyCylinder(r=.375,h=0.1,sa=32,ch=0)[0] mc.sets(disc, e=1, forceElement=redSG) mc.move(sPos[0]+.5, 0.05, sPos[1]+.5, disc, a=1, rpr=1) mc.move(sPos[0]+.5, 0.11, sPos[1]+.5, text, a=1, rpr=1) mc.parent(text,disc,mainDataGroup) ePos = eCoord # E text = makeText('E') disc = mc.polyCylinder(r=.375,h=0.1,sa=32,ch=0)[0] mc.sets(disc, e=1, forceElement=redSG) mc.move(ePos[0]+.5, 0.05, ePos[1]+.5, disc, a=1, rpr=1) mc.move(ePos[0]+.5, 0.11, ePos[1]+.5, text, a=1, rpr=1) mc.parent(text,disc, mainDataGroup) if not reset: mc.select(mainDataGroup) mel.eval('fitPanel -selected') # set the display up nicely: viewport = mc.getPanel(wf=1) if not mc.getPanel(typeOf=viewport) == 'modelPanel': viewport = 'modelPanel4' mc.modelEditor(viewport, e=1, grid=0, da='smoothShaded', dl='default', dtx=0, lw=1, ns=1, pm=1, nc=1, ha=0, # sel=0, wos=0, udm=0, ao=0, xr=0, bfc=0, cov='doubleSided') mc.select(cl=1) # set up and activate the dragger tool if mc.draggerContext(ctx, q=1, ex=1): mc.deleteUI(ctx) mc.draggerContext(ctx, cursor='crossHair', i1='sketchPlane.xpm', undoMode='sequence', pr='plane', space='world', rc=ctxAction, dc=ctxDrag, pc=ctxClick) mc.setToolTo(ctx) def buildPuzzleMenu(): items = mc.optionMenu('labyrinthPuzzleMenu',q=1,ill=1) if items: mc.deleteUI(items) for item in labyrinthPuzzles: mc.menuItem(p='labyrinthPuzzleMenu',l='%- 32s' % item[0]) def menuChange(puzzleName='unused'): global currentPuzzle index = mc.optionMenu('labyrinthPuzzleMenu',q=1,sl=1)-1 currentPuzzle = index def ui(): win = 'labyrinthWindow' if mc.window(win,ex=1): mc.deleteUI(win) #mc.windowPref(win,remove=1) mc.window(win,wh=(120,125),title='Labyrinth!',tlb=1) mc.columnLayout(adj=1) # mc.rowLayout(nc=2,adj=1,cw2=(1,28)) mc.text(l='Puzzle set:',al='left') # mc.symbolButton(i='fileOpen.xpm',h=20) # mc.setParent('..') mc.textField(en=0,text='Internal') mc.optionMenu('labyrinthPuzzleMenu',cc=menuChange) mc.setParent('..',m=1) mc.button(l='Initialise!', h=30, bgc=(.8,.9,.7), c=init,ann='Press to Start or Reset this Labyrinth Puzzle!') mc.setParent('..') buildPuzzleMenu() mc.showWindow(win)