#! /usr/bin/python # $:(file-if (really realpython)) from __future__ import print_function from visual import * from random import random from time import sleep import os ## for _exit() def wait(): print("Call to wait() should never happen.") class extemporaneous(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) browser = 0 # $:(file-endif) everything above this line gets left out of the browser version if type(browser) == 'undefined': browser = 1 class bogus: def __init__(self): 0000 if browser: KeyboardInterrupt = bogus.constructor # kludge: placeholder def jsSetpos(newpos): this.clear() for ii in newpos: this.append(ii) if browser: curve.prototype.setpos = jsSetpos # works in glowscript else: curve.setpos = lambda self, newpos : setattr(self, 'pos', newpos) # works from cmdline def sortme_py(stuff): return sorted(stuff, key=manhattan) # python sort aka sorted def sortme_js(_stuff): stuff = _stuff[:] # because js does a sort in place stuff.sort(mh2) # native js sort return stuff if browser: args = parseQueryString() print_options(readonly=0) random = Math.random def conlog(): console.log.apply(console, arguments) def extemporaneous(): rslt = {} for obj in arguments: for key in dir(obj): rslt[key] = obj[key] return rslt sortme = sortme_js else: args = extemporaneous(watch=1, debug=0) conlog = print sortme = sortme_py # Manhattan metric: def manhattan(myvec): radius = 0 for xyz in myvec: radius += abs(xyz) return radius def mh2(a, b): return manhattan(a) - manhattan(b) # look up value if it exists; otherwise return the default def chex(obj, ndx, dflt): if ndx in dir(obj): if browser: return obj[ndx] else: return obj.__dict__[ndx] return dflt # Replacement for ternary construction: return a if q else b # which is not implemented in rapydscript def pick(q, a, b): if q: return a return b def uniform(a, b): return random()*(b-a) + a def choice(seq): # random choice if len(seq) > 0: return seq[int(random()*len(seq))] else: raise IndexError() # unlike the python version, this DOES build a range object, # feel free to reimplement def randrange(*args): return choice(range(*args)) # uses Fisher-Yates algorithm to shuffle an array def shuffle(x, random_f=random): for i in range(len(x)): j = int(random_f() * (i+1)) x[i], x[j] = x[j], x[i] return x # show the args (as specified via URL query string) if 0: for arg in dir(args): val = args[arg] tf = pick(val, 'yes', 'no') print(" arg: ", arg, "-->", val, tf) # some program-wide variables: shared = extemporaneous(keylist=[], Vtoggle=0, oldV=0, zap=0) cell = 0 # will get initialized to an array verbosity = chex(args, 'verbosity', 0) debug = chex(args, 'debug', 0) dots = chex(args, 'dots', 0) watch = chex(args, 'watch', 0) # [dx, dy, edge] # Note that stepping the index 2 (mod 4) gives you the opposite edge. neighbors = [ [1, 0, 1], [0, 1, 0], [-1, 0, 1], [0, -1, 0] ] # the edge here is the edge /along/ which we move # (not the edge we cross, i.e. not like neighbors) northeast = [ [1, 0, 0], [0, 1, 1] ] # like neighbors, but without edge indicator basis = [[1, 0], [0, 1], [-1, 0], [0, -1]] # 0 --> [0, 16, 99, 12, 1] 13 # 0.25 --> [0, 36, 62, 26, 4] 30 # 0.5 --> [0, 41, 54, 27, 6] 33 # 0.75 --> [0, 42, 52, 28, 6] 34 # 1 --> [0, 43, 51, 27, 7] 34 # 0 1 --> [0, 35, 61, 31, 1] 32 # 0.25 1 --> [0, 39, 57, 27, 5] 32 # 0.5 1 --> [0, 44, 48, 30, 6] 36 branching = .3 breadth = 0 # breadth-first search is not particularly helpful; # mostly it just creates lots of shallow appendices plain = vector(1,1,1) # a generic color OKxx = vector(1, 0, 1) fake = -9e99 # when a vector component is required, but meaningless if debug: OKne = color.blue OKsw = color.red else: OKne = OKxx OKsw = OKxx ######### end of static parameters class cheeser: def __init__(self, wedge): self.x = 0 self.y = 0 self.marked = 0 self.wedge = wedge if wedge: self.c = curve(radius=0.1, color=color.yellow, visible=0, pos=[vector(.8, .5, 0), # vertex vector(.2, .4, 0), vector(.2, .6, 0), vector(.8, .5, 0), vector(.2, .46666, 0), vector(.2, .53333, 0), vector(.8, .5, 0) ] ) else: self.c = sphere(radius=0.15, color=color.yellow, visible=0, pos=vector(0.5, 0.5, 0)) #-# end defining __init__ # If the cheese is at the given position, # make it visible def check(self, xx, yy): if xx == self.x and yy == self.y: # It's probably already visible, but we can make sure: cheese.marked = cheese.c.visible = 1 #-# end defining check # cor is a vector specifying the lower-left corner of the # cell in which the cheese is sitting def setpos(self, cor): self.x = cor.x self.y = cor.y if self.wedge: self.c.setpos([vector(.8 + cor.x, .5 + cor.y, 0), vector(.2 + cor.x, .4 + cor.y, 0), vector(.2 + cor.x, .6 + cor.y, 0), vector(.8 + cor.x, .5 + cor.y, 0), vector(.2 + cor.x, .46666 + cor.y, 0), vector(.2 + cor.x, .53333 + cor.y, 0), vector(.8 + cor.x, .5 + cor.y, 0) ]) else: self.c.pos=cor + vector(.5, .5, 0) #-# end defining cheeser cheese = 0 # will become a cheeser object later, after defines are done # This class exists primarily to keep track of global state, # e.g. stepno. # This stands in contrast to per-cell state, which is the celler class. class visitor: def __init__(self): self.stepno = 0 # Check the cell at [x,y]. # Check whether we are allowed to enter it, and if so, do. def check(self, x, y, force=0): x = int(x) y = int(y) #-- print("checking", x, y, force) # not allowed to explore cells that you haven't peeked into: if not force and not cell[y][x].seen: return # not allowed to re-visit cells that have already been visited: if cell[y][x].visited.visible: return self.stepno += 1 cell[y][x].enter(self.stepno) 0-0 # end defining check 0-0 # end defining visitor class edger: def __init__(self): self.c = 0 # the curve will go here self.exists = 0 self.marked = 0 #-# End of define class edger # RS fails for class celler(): as opposed to class celler: class celler: # constructor def __init__(self, xx, yy): self.xx = xx # remember where we sit self.yy = yy self.looped = 0 self.iid = 0 # island ID self.harvey = 0 # 1 ==> stepped on by the wall-banger algorithm # 2 ==> nearest neighbor to harvey # gutter cells on the left and the bottom have no edges at all: if xx > 0 and yy > 0: # otherwise, allocate right-sized array for edges: self.e = ['bot', 'left'] # contents will get overwritten for dx,dy,ed in northeast: xxp = xx + dx yyp = yy + dy if (xx > w and not ed) or (yy > h and ed): # gutter cells on the top have no left, and # gutter cells on the right have no bottom: 0 else: # otherwise allocate left or bottom of this cell self.e[ed] = edger() self.e[ed].c = curve(pos=[vector(xx, yy, 0), vector(xxp, yyp, 0)], radius=0.1, visible=0, color=plain) if xx >= 1 and xx <= w and yy >= 1 and yy <= h: # visited.visible means stepped on during solution phase self.d = sphere(pos=vector(xx+.5, yy+.5, 0.1), radius=0.1, visible=0) if dots: self.visited = sphere(pos=vector(xx+.5, yy+.5, 0), radius=0.1, visible=0) else: self.visited = label(pos=vector(xx+.5, yy+.5, 0), text='', border=0, line=0, box=0, visible=0, opacity=0) # opacity means /background/ opacity #-# end defining __init__ ########## # here self is a cell, i.e. an instance of class celler # if id>0, make this a visible edge # otherwise reset it to the invisible and nonexistent state # # Negative pseudo-edge-number means leave edges alone. # ... which would make this a complete no-op, # except that there's also an effect on self.iid. def mk_edge(self, edge=-1, id=0): if edge >= 0: self.e[edge].exists = id > 0 self.e[edge].marked = 0 self.e[edge].c.visible = debug and id > 0 self.e[edge].c.color = plain if id > 0: self.iid = id def reset(self): # reset a single cell xx = self.xx yy = self.yy self.seen = 0 self.looped = 0 self.iid = 0 self.harvey = 0 # construct the boundary box; boundary has id=1 if (xx == 1 or xx == w+1) and yy >= 1 and yy <= h: self.mk_edge(1, 1) if (yy == 1 or yy == h+1) and xx >= 1 and xx <= w: self.mk_edge(0, 1) # sneaky corner case: cell has no left-edge and no botttom-edge, # but still it is a node on the boundary: if xx == w+1 and yy == h+1: self.mk_edge(-1, 1) # sneaky boundary cella if xx >= 1 and xx <= w and yy >= 1 and yy <= h: # working cells self.visited.visible = 0 self.d.visible = debug self.d.color = vector(1, 0, 1) # mark working-cell edges as non-existent: if xx > 1: self.mk_edge(1, 0) if yy > 1: self.mk_edge(0, 0) #-# end of defining reset # Enter a cell. # Mark it as visited. # Mark visible neighboring cells as seen. # Mark "near" edges of neighboring cells as appopriate. def enter(self, stepno): xx = self.xx yy = self.yy self.visited.visible = 1 if not dots: # update the text self.visited.text = str(stepno) # Mark all four edges of this cell for edge in [ self.e[1], self.e[0], cell[yy][xx+1].e[1], cell[yy+1][xx].e[0] ]: edge.c.color = OKxx edge.marked = edge.c.visible = edge.exists # Did we step on the cheese? cheese.check(xx, yy) self.seen = 1 # Mark top and bottom of horizontally neighboring cells, # if they are open to view: for dx in [-1, 1]: rx = xx + dx ry = yy bigx = max(xx, rx) # See if the neighboring cell is accessible: if not cell[ry][bigx].e[1].exists: if rx >= 1 and rx <= w: #-- print("h marking", rx, ry) cell[ry][rx].seen = 1 # Can we smell the cheese? cheese.check(rx, ry) for box in [cell[ry][rx], cell[ry+1][rx] ]: edge = box.e[0] if not edge.marked: edge.c.color = pick(dx<0, OKsw, OKne) edge.marked = edge.c.visible = edge.exists # Mark left and right of vertically neighboring cells: for dy in [-1, 1]: rx = xx ry = yy + dy bigy = max(yy, ry) # See if the neighboring cell is accessible: if not cell[bigy][rx].e[0].exists: if ry >= 1 and ry <= h: #-- print("v marking", rx, ry) cell[ry][rx].seen = 1 # Can we smell the cheese? cheese.check(rx, ry) for box in [cell[ry][rx], cell[ry][rx+1] ]: edge = box.e[1] if not edge.marked: edge.c.color = pick(dy<0, OKsw, OKne) edge.marked = edge.c.visible = edge.exists #-# end of defining enter #-# End of define class celler def xvms(): print("XVMS", cell[1][1].e[1].exists, cell[1][1].e[1].c.visible, cell[1][1].e[1].marked, cell[1][1].seen, " ", cell[1][2].e[1].exists, cell[1][2].e[1].c.visible, cell[1][2].e[1].marked, cell[1][2].seen, " ", cell[1][3].e[1].exists, cell[1][3].e[1].c.visible, cell[1][3].e[1].marked, cell[1][3].seen ) ## support for 'D' key: ## degree of connectivity: def decon(on): histo = [0] * 5 clr = ([vector(1,0,1), vector(1,0,0), vector(1, 1, 0), vector(0, 1, 0), vector(0, 0, 1), ]) for xx in range(1, 1+w): for yy in range(1, 1+h): count = 0 if not cell[yy][xx].e[0].exists: count += 1 if not cell[yy][xx].e[1].exists: count += 1 if not cell[1+yy][xx].e[0].exists: count += 1 if not cell[yy][1+xx].e[1].exists: count += 1 cell[yy][xx].d.color = clr[count] histo[count] += 1 cell[yy][xx].d.visible = on print("decon:", branching, breadth, "-->", histo, histo[3] + histo[4]) 0-0 # end defining decon cmap = ([vector(1, 0, 1), vector(1, 0, 0), vector(1, .5, 0), vector(1, 1, 0), vector(0, 1, 0), vector(0, 1, 1), vector(0, 0, 1) ]) def wallbang(on): for xx in range(1, 1+w): for yy in range(1, 1+h): har = cell[yy][xx].harvey if har: ndx = (har-1) % len(cmap) cell[yy][xx].d.color = cmap[ndx] cell[yy][xx].d.visible = on ##### support for the 'V' key: def visualize(on): if on: #=== if browser: print("begin V on") cheese.c.visible = 1 total = 0 for y in range(1, 2+h): for x in range(1, 2+w): ##rate## rate(myrate) # gutter cells on the top have no left: if y <= h: vv = cell[y][x].e[1].c.visible = cell[y][x].e[1].exists total += vv # gutter cells on the right have no bottom: if x <= w: vv = cell[y][x].e[0] .c.visible = cell[y][x].e[0] .exists total += vv #=== print("end V on; total visible:", total) else: #=== if browser: print("begin V off") if grcheese: grcheese.visible = 0 grcheese.color = vector(1,0,0) cheese.c.visible = cheese.marked for y in range(1, 2+h): for x in range(1, 2+w): ##rate## rate(myrate) # gutter cells on the top have no left: if y <= h: cell[y][x].e[1].c.visible = cell[y][x].e[1].marked # gutter cells on the right have no bottom: if x <= w: cell[y][x].e[0] .c.visible = cell[y][x].e[0] .marked #xx for testing only, #xx and confusing even then: cell[1][1].e[1].c.visible = 1 #=== if browser: print("end V off") # Asynchronous. Just put event on list to be dealt with by main loop. def keyDown(ev): shared.keylist.append(ev) def key_action(mykey): if mykey == 'E': if browser: if extra_win.style.height == '0px': extra_win.style.height = '400px' else: extra_win.style.height = '0px' # if not browser, just ignore if mykey == 'P': if browser: if py_win.style.height == '0px': py_win.style.height = '300px' else: py_win.style.height = '0px' # if not browser, just ignore elif mykey == 'J': if browser: if machine_code_win.style.height == '0px': machine_code_win.style.height = '300px' else: machine_code_win.style.height = '0px' # if not browser, just ignore elif mykey == 'C': if browser: print_options(clear=True) if printarea.style.height == '0px': printarea.style.height = '300px' elif mykey == 'V': shared.Vtoggle = not shared.Vtoggle elif mykey == 'Z': shared.zap = 1 elif mykey == 'D': decon(1) elif mykey == 'W': wallbang(1) elif mykey == 'N': shared.run = 0 elif mykey == 'c': xvms() else: 00000 # FIXME: key not handled # Synchronous. Called from main loop. def check_keylist(): if len(shared.keylist) == 0: return ev = shared.keylist.pop() ctrl = pick(ev.ctrl, ' ctrl', '') shift = pick(ev.shift, ' shift', '') alt = pick(ev.alt, ' alt', '') if 0 : print("Event type: " + ev.type, " key: '" + ev.key + "'", " ord: " + ord(ev.key), " keyCode: " + ev.keyCode, " charCode: " + ev.charCode, ctrl, alt, shift) ###def pr(x) : print(x, '-->', ev[x]) ###dir(ev).map(pr) # for debugging key_action(ev.key) 0-0 def mouse(ev): if 0 : print('mouse click at: ', ev.pos) if 1: # initial allocation h = 8 w = 16 myrange = 10 if browser: myrange = 6 if browser: display = canvas scene = display(x=0, y=0, width=854, height=480, # position of graphics window center = vector(w/2+1, h/2+1, 0), range=myrange) cheese = cheeser(1) # the actual objective cheese if browser: scene.bind('keydown', keyDown) scene.bind('click', mouse) cell = [0] * (2+h) for yy in range(0, 2+h): cell [yy] = [0] * (2+w) for xx in range(0, 2+w): cell[yy][xx] = celler(xx, yy) grcheese = blcheese = 0 # may get redefined if 0: grcheese = cheeser(0) # a hackish marking, mainly for debugging grcheese.c.visible = 1 if 1: blcheese = cheeser(0) # another marking, mainly for debugging blcheese.setpos(vector(0, 1+h, 0)) blcheese.c.visible = 1 blcheese.c.color = vector(0, 0, 1) visit = visitor() class noder: def __init__(self, x, y): self.pos = [x, y] self.dir = basis[:] shuffle(self.dir) #-# end defining noder.__init__ #-# end declaration of noder class treer: def __init__(self, _iid, x, y): self.iid = _iid self.todo = [] # allow dup connections to boundary, but nothing else: if cell[y][x].iid > 1: if verbosity: print("treer: avoiding dup tree during maze construction", x, y) else: self.todo.append(noder(x,y)) cell[y][x].iid = self.iid # keeps trying until it creates one link # (or runs out of options def growsome(self, count): todo = self.todo while len(todo): if random() < branching: shuffle(todo) node = todo[len(todo)-1] if not len(node.dir): todo.pop() continue [xx, yy] = node.pos [dx, dy] = node.dir.pop() if self.growinto(xx, yy, xx+dx, yy+dy): count -= 1 if count <= 0: return 1 # did all that was requested return 0 # ran out of options #-# end loop over things to do #-# end defining growsome ## args: (oldx, oldy, newx, newy) ## returns 1 if a new link was created def growinto(self, x, y, xx, yy): rslt = 0 if xx <= 0: return 0 if yy <= 0: return 0 if xx >= w+2: return 0 if yy >= h+2: return 0 if cell[yy][xx].iid: return 0 # dest cell already part of some island # step to new cell.... # new cell joins the island: cell[yy][xx].iid = self.iid # find and mark the edge that brought us here: hor = y == yy ver = x == xx if hor: smallx = min(x, xx) smally = yy ed = 0 elif ver: smallx = xx smally = min(y, yy) ed = 1 else: print("SNH neither hor nor ver") return 0 edge = cell[smally][smallx].e[ed] if edge.exists: print("SNH retrace edge", x, y, xx, yy) return 0 # didn't really add anything new edge.exists = 1 edge.c.visible = debug self.todo.append(noder(xx, yy)) return 1 #-# end defining growinto #-# end declaring treer ### create randomly, make sure it can grow one unit def mkroot(forest, id, todo, poslist): done = 0 mylist = list(poslist) while done < todo: if not len(mylist): return which = int(random()*len(mylist)) [xx,yy] = mylist.pop(which) tree = treer(id, xx, yy) tree.growsome(1) if not len(tree.todo): continue forest.append(tree) todo += -1 # Cartesian outer product. # Not an elegant or efficient implementation, but gets the job done. # Note that neither lambda-expressions nor generator-expressions # work in the browser. def outer(xrange, yrange): rslt = [] for xx in xrange: for yy in yrange: rslt.append([xx, yy]) return rslt # Call with innish[ii], ii # The list in innish[ii] is originally unmarked. # We split the list, and mark the cells that need to be marked, # namely the ones that are neighbors of already-marked cells. def innerlayer(todo, ii): x,y = todo[0] thresh = ii marked = [] unmarked = [] for x,y in todo: har = cell[y][x].harvey if har and har <= thresh: print("bogus innerlayer", x, y, thresh, har) markme = 0 for dx,dy,ed in neighbors: xx = x + dx yy = y + dy mx = max(x, xx) my = max(y, yy) # see if the step [dx,dy] is allowed, i.e. no wall: if not cell[my][mx].e[ed].exists: har = cell[yy][xx].harvey # check status of other cell if har > 0 and har <= thresh: markme = 1 break # no need to check additional neighbors if markme: cell[y][x].harvey = thresh+1 # set status of this cell marked.append([x, y]) else: unmarked.append([x, y]) #-# end loop over cells return [marked, unmarked] def zap(): for yy in range(0, 2+h): for xx in range(0, 2+w): cell[yy][xx].reset() def check_zap(): vis_total = 0 ex_total = 0 # loop over working cells only: for yy in range(2, 1+h): for xx in range(2, 1+w): for edge in range(0, 2): if cell[yy][xx].e[edge].exists: ## print? conlog? ("zap must have failed for", xx, yy, edge) ex_total += 1 if cell[yy][xx].e[edge].c.visible: vis_total += 1 print("check_zap working existing:", ex_total, " visible:", vis_total) def newgame(): # re-initialize for new game: #-- conlog("new game") visit.stepno = 0 zap() if 1: floating = [] rooted = [] # put some rooted guys on each edge of the boundary: xrange = range(2, 1+w) yrange = range(2, 1+h) mkroot(rooted, 2, 3, outer(xrange, [1])) mkroot(rooted, 2, 3, outer(xrange, [1+h])) mkroot(rooted, 2, 1, outer([1], yrange)) mkroot(rooted, 2, 1, outer([1+w], yrange)) # some free-floating islands: mkroot(floating, 33, 6, outer(xrange, yrange)) # At this point, the rooted guys have already grown one unit; # now islands get some priority: for ii in range(6): for ff in floating: ff.growsome(1) # now everybody grows to completion: everybody = floating + rooted iix = 0 for ii in range(h*w): iix = ii didsome = 0 for tr in everybody: didsome += tr.growsome(1) if not didsome: break #-# end of big loop for xx in range(1, w+2): for yy in range(1, h+2): hh = 0 if cell[yy][xx].iid <= 2: # This *node* is a harvey node. # That means all four cells surrounding it # will be stepped on by the wall-banger algorithm: # So mark the harvey *cells* as such: cell[yy ][xx ].harvey = 1 cell[yy-1][xx ].harvey = 1 cell[yy ][xx-1].harvey = 1 cell[yy-1][xx-1].harvey = 1 # innish[ii] is marked with harveyness of ii+1 # except fot the last element of innish, which is unmarked innish = [ [], [] ] for xx in range(1, w+1): for yy in range(1, h+1): if cell[yy][xx].harvey: innish[0].append([xx, yy]) # the harvey cells else: innish[1].append([xx, yy]) for ii in range(1, h+w): [marked, unmarked] = innerlayer(innish[ii], ii) if not len(unmarked): break innish[ii] = marked innish.append(unmarked) for ii in range(len(innish)): x = y = cxy = -1 if len(innish[ii]) : x,y = innish[ii][0] cxy = cell[y][x].harvey if 0: print("xxxxxxxxx", ii, len(innish[ii]), [x,y], cxy) cheesable = innish[len(innish) - 1] # keep users honest: cheese /might/ be near the edge: if random() < 2./h + 2./w: cheesable = innish[0] # Don't place the cheese toooo close to the starting point: inlen = len(cheesable) if inlen: # sort in ascending order: cheesable = sortme(cheesable) # keep only the second half: cheesable = cheesable[int(inlen/2):] [rx, ry] = choice(cheesable) # random choice else: # emergency cheese placement # Cheese placement is optimized under the assumption that # the maze has a roughly 2:1 aspect ratio: rx = randrange(w/2, w) # not including w ry = randrange(2, h) # not including h cheese.setpos(vector(rx, ry, fake)) cheese.c.color = choice(([vector(1,1,0), vector(0,1,0), vector(0,0,1)])) cheese.c.visible = debug cheese.marked = 0 # cheese.rotate(angle=0) # causes cheese to disappear if grcheese: grcheese.visible = 1 grcheese.c.color = vector(0,1,0) if 0: ######## for debugging asdf visualize(1) decon(1) visit.check(1, 1, 'force') # give the user a starting point if 0: xvms() visualize(shared.Vtoggle) #-# End defining newgame. shared.run = 0 # set to zero to force a new game stoptrigger = 0 shared.Vtoggle = debug try: while 1111: # loop over all games, and all action within a game rate(30) if not browser: if scene.kb.keys: # check for key event s = scene.kb.getkey() # get keyboard info # This catches ^C on the graphics window # (whereas keyboardinterrupt catches it on the controlling terminal). if len(s) == 1: key_action(s) elif len(s) > 0: if s == 'ctrl+c': os._exit(0) need_new = not shared.run check_keylist() if need_new: newgame() shared.run = 1 continue if shared.zap: zap() #xx check_zap() shared.zap = 0 p = scene.mouse.pos shift = scene.mouse.shift ctrl = scene.mouse.ctrl if (p): p = vector(p) x = floor(p.x) y = floor(p.y) # special exercises: if shift and not browser: oldstop = stoptrigger stoptrigger = x==0 and y==0 if stoptrigger and not oldstop: shared.run = 0 if x==0 and y==h: shared.Vtoggle = 1 if x==0 and y==h-1: visualize(0) if x==1 and y==h+1: sleep(0.5) newgame() visualize(1) decon(1) if x==2 and y==h+1: sleep(0.5) newgame() visualize(1) wallbang(1) if shared.oldV != shared.Vtoggle: visualize(shared.Vtoggle) decon(shared.Vtoggle) shared.oldV = shared.Vtoggle # The main exploration business: if x >= 1 and x <= w and y >= 1 and y <= h and not shift: visit.check(x,y) ## else: ## conlog("******* p was null") 0-0 # end of loop over all games except KeyboardInterrupt as e: print ('...Interrupt...') os._exit(1)