import math, sys, time import traceback from browser import document as doc from browser import timer, window, alert, html, console from javascript import JSConstructor sys.__console__ = False MEDIA_PATH="/media/teaching/alg110006/maze/" class Output: def __init__(self,element): self.el = element self.el.value = '' self.style = {} self.Class = '' self._content = [] def write(self,data): lines = [html.SPAN(ln,Class=self.Class,style=self.style) for ln in data.split('\n')] for ln in lines[:-1]: br = html.BR() self._content.append(ln) self._content.append(br) self.el <= ln self.el <= br self._content.append(lines[-1]) self.el <= lines[-1] def clear(self,*args): for el in self._content: self.el.remove(el) del el self._content = [] class Canvas: def __init__(self,element): self._elt = element self._ctx = element.getContext("2d") self._scale = 1 def set_scale(self,sc): self._scale = sc def draw_square(self,pos,width): self.fillRect(pos[0]*self._scale,pos[1]*self._scale,width*self._scale,width*self._scale) def draw_circle(self,pos,radius): self.beginPath() self.arc(pos[0]*self._scale, pos[1]*self._scale, radius*self._scale, 0, 2*math.pi) self.fill() self.closePath() def __getattr__(self, name): return self._ctx.__getattribute__(name) def __setattr__(self,name,value): if name[0] == "_": object.__setattr__(self,name,value) else: self._ctx.__setattr__(name,value) class Console: _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands for supporting Python development. See www.python.org for more information. """ _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com All Rights Reserved. Copyright (c) 2001-2013 Python Software Foundation. All Rights Reserved. Copyright (c) 2000 BeOpen.com. All Rights Reserved. Copyright (c) 1995-2001 Corporation for National Research Initiatives. All Rights Reserved. Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved. """ _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ def __init__(self,elem): self._elem = elem self.credits.__repr__ = lambda:Console._credits self.copyright.__repr__ = lambda:Console._copyright self.license.__repr__ = lambda:Console._license self._redirected = False self._oldstdout = None self._oldstderr = None self.history = [] self.current = 0 self._status = "main" # or "block" if typing inside a block # execution namespace self.editor_ns = { 'credits':self.credits, 'copyright':self.copyright, 'license':self.license, '__name__':'__console__', } self._elem.bind('keypress', self.myKeyPress) self._elem.bind('keydown', self.myKeyDown) self._elem.bind('click', self.cursorToEnd) v = sys.implementation.version self._elem.value = "Brython %s.%s.%s on %s %s\n%s\n>>> " % ( v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion,'Type "copyright()", "credits()" or "license()" for more information.') self._elem.focus() self.cursorToEnd() def _redirectOut(self): if self._redirected: sys.__console__ = False sys.stdout = self._oldstdout sys.stderr = self._oldstderr self._redirected = False else: sys.__console__ = True self._oldstdout = sys.stdout self._oldstderr = sys.stderr sys.stdout = self sys.stderr = self self._redirected = True def credits(self): self.write(self._credits) def copyright(self): self.write(self._copyright) def license(self): self.write(self._license) def write(self,data): self._elem.value += str(data) def cursorToEnd(self, *args): pos = len(self._elem.value) self._elem.setSelectionRange(pos, pos) self._elem.scrollTop = self._elem.scrollHeight def get_col(self,area): # returns the column num of cursor sel = self._elem.selectionStart lines = self._elem.value.split('\n') for line in lines[:-1]: sel -= len(line) + 1 return sel def myKeyPress(self, event): if event.keyCode == 9: # tab key event.preventDefault() self._elem.value += " " elif event.keyCode == 13: # return src = self._elem.value if self._status == "main": self.currentLine = src[src.rfind('>>>') + 4:] elif self._status == "3string": self.currentLine = src[src.rfind('>>>') + 4:] self.currentLine = self.currentLine.replace('\n... ', '\n') else: self.currentLine = src[src.rfind('...') + 4:] if self._status == 'main' and not self.currentLine.strip(): self._elem.value += '\n>>> ' event.preventDefault() return self._elem.value += '\n' self.history.append(self.currentLine) self.current = len(self.history) if self._status == "main" or self._status == "3string": try: self._redirectOut() _ = self.editor_ns['_'] = eval(self.currentLine, self.editor_ns) if _ is not None: self.write(repr(_)+'\n') self._elem.value += '>>> ' self._status = "main" except IndentationError: self._elem.value += '... ' self._status = "block" except SyntaxError as msg: if str(msg) == 'invalid syntax : triple string end not found' or \ str(msg).startswith('Unbalanced bracket'): self._elem.value += '... ' self._status = "3string" elif str(msg) == 'eval() argument must be an expression': try: self._redirectOut() exec(self.currentLine, self.editor_ns) except: traceback.print_exc(self) finally: self._redirectOut() self._elem.value += '>>> ' self._status = "main" elif str(msg) == 'decorator expects function': self._elem.value += '... ' self._status = "block" else: traceback.print_exc(self) self._elem.value += '>>> ' self._status = "main" except: traceback.print_exc(self) self._elem.value += '>>> ' self._status = "main" finally: self._redirectOut() elif self.currentLine == "": # end of block block = src[src.rfind('>>>') + 4:].splitlines() block = [block[0]] + [b[4:] for b in block[1:]] block_src = '\n'.join(block) # status must be set before executing code in globals() self._status = "main" try: self._redirectOut() _ = exec(block_src,self.editor_ns) #exec(block_src,self.editor_ns) if _ is not None: print(repr(_)) except: traceback.print_exc(self) finally: self._redirectOut() self._elem.value += '>>> ' else: self._elem.value += '... ' self.cursorToEnd() event.preventDefault() def myKeyDown(self, event): if event.keyCode == 37: # left arrow sel = self.get_col(self._elem) if sel < 5: event.preventDefault() event.stopPropagation() elif event.keyCode == 36: # line start pos = self._elem.selectionStart col = self.get_col(self._elem) self._elem.setSelectionRange(pos - col + 4, pos - col + 4) event.preventDefault() elif event.keyCode == 38: # up if self.current > 0: pos = self._elem.selectionStart col = self.get_col(self._elem) # remove self.current line self._elem.value = self._elem.value[:pos - col + 4] self.current -= 1 self._elem.value += self.history[self.current] event.preventDefault() elif event.keyCode == 40: # down if self.current < len(self.history) - 1: pos = self._elem.selectionStart col = self.get_col(self._elem) # remove self.current line self._elem.value = self._elem.value[:pos - col + 4] self.current += 1 self._elem.value += self.history[self.current] event.preventDefault() elif event.keyCode == 8: # backspace src = self._elem.value lstart = src.rfind('\n') if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6): event.preventDefault() event.stopPropagation() class Memory: def __init__(self): pass def __str__(self): ret = '{'+"\n".join([str(a)+":"+str(self.__getattribute__(a)) for a in dir(self) if a[0]!='_']) #for a in dir(self): #val = self.__getattribute__(a) #ret = ret+a+":"+str(val)+",\n" return ret +"}" class Maze: SQ_WIDTH = 10 def __init__(self,map_elem = None): self.map = [[]] self._start = (0,0) self._end = (0,0) self._map_elem = map_elem def add_element(self,element): self._map_elem <= element def remove_element(self,element): self._map_elem.remove(element) def canvas(self): return self._canvas def randomize(self, width): pass def width(self): return max([len(row) for row in self.map]) def height(self): return len(self.map) def erase(self): for pos in self: del self.map[pos]['elem'] def reload(self,*args): for pos in self: self[pos]['data'] = "" self[pos]['elem'].style.backgroundColor='' def load(self,map): self.erase() self.map=[] for row in map: map_row = [] for col in row: map_row.append({'block':col,'data':""}) self.map.append(map_row) for pos in self: self[pos]['elem'] = html.DIV(' ', style={ 'height':str(Maze.SQ_WIDTH)+'px','width':str(Maze.SQ_WIDTH)+'px', 'top':pos[0]*Maze.SQ_WIDTH,'left':pos[1]*Maze.SQ_WIDTH }, Class='maze_block block_'+self[pos]["block"]) if self._map_elem: self._map_elem <= self[pos]['elem'] b = self[pos]["block"] if b == "S": self._start = pos elif b == "E": self._end = pos self._map_elem.style.height = str(self.height()*Maze.SQ_WIDTH)+'px' self._map_elem.style.width = str(self.width()*Maze.SQ_WIDTH)+'px' def __iter__(self): for row in range(len(self.map)): for col in range(len(self.map[row])): yield (row,col) def __getitem__(self,pos): if pos[0] < len(self.map) and pos[1]< len(self.map[pos[0]]) and pos[0]>=0 and pos[1] >= 0: return self.map[pos[0]][pos[1]] return None def __setitem__(self,pos,value): self.map[pos[0]][pos[1]]["data"]=value def fillSquare(self,pos,color): self[pos]['elem'].style.backgroundColor=color def getSquareFill(self,pos): return self[pos]['elem'].style.backgroundColor def start(self): return self._start def end(self): return self._end class Agent: MAX_HEALTH = 10 SPEED = 1 def __init__(self): self.memory=Memory() self.maze=None self._health = Agent.MAX_HEALTH self._canvas = None self._scene = None self._distance = 0 self._fuel = 0 self._agent_element = html.DIV(' ', style = { 'height':str(Maze.SQ_WIDTH)+'px','width':str(Maze.SQ_WIDTH)+'px', }, Class='agent agent_'+str(self._health) ) self._pos = (0,0) @property def pos(self): return self._pos @pos.setter def pos(self,val): dist = abs(val[0]-self._pos[0]) + abs(val[1]-self._pos[1]) if dist > 1: raise Exception("Agent cannot move: Target too far (can move only one square at a time)") if dist > 0 and self._fuel <= 0 and not sys.__console__: raise Exception("Agent cannot move: Out of fuel.") target = self.maze[val] if not target or target["block"] == 'W': if sys.__console__: self.hurt() raise Exception("Agent cannot move: Target square not empty.") self._fuel = self._fuel - 1 self._pos = val self._distance = self._distance + 1 self._agent_element.style.top = Maze.SQ_WIDTH*self._pos[0] self._agent_element.style.left = Maze.SQ_WIDTH*self._pos[1] def go(self,direction): tpos = self.dirpos(direction) self.pos = tpos if self._scene: animation = self._scene.turnCamera(direction,Agent.SPEED*0.3) animation.onAnimationEnd = lambda:self._scene.moveCamera(self.pos,Agent.SPEED*0.6) @property def health(self): return self._health def health_color(self): blue=0 green=255 red=255 if self.health >= Agent.MAX_HEALTH/2: red=255-(self.health-Agent.MAX_HEALTH/2)*(255/(Agent.MAX_HEALTH/2)) if self.health <= Agent.MAX_HEALTH/2: green=255-(Agent.MAX_HEALTH/2-self.health)*(255/(Agent.MAX_HEALTH/2)) return (int(red),int(green),int(blue)) def heal(self,points = None): if points: self._health = self._health + points else: self._health = Agent.MAX_HEALTH def hurt(self): self._health = self._health - 1 @property def distance(self): return self._distance def dirpos(self,direction=None): dX,dY = 0,0 if direction == 'N' or direction == '-S': dY = -1 elif direction == 'S' or direction == '-N': dY = 1 elif direction == 'E' or direction == '-W': dX = 1 elif direction == 'W' or direction == '-E': dX = -1 return (self.pos[0]+dY,self.pos[1]+dX) def is_wall(self,direction): target = self.maze[self.dirpos(direction)] if not target or target["block"] == 'W': return True return False def write(self,text): self.maze[self.pos]["data"] = text def read(self,direction=None): return self.maze[self.dirpos(direction)]["data"] def set_color(self,color): self.maze.fillSquare(self.pos,color) def get_color(self): return self.maze.getSquareFill(self.pos) def forget(self): self.memory = {} def at_exit(self): return self.pos == self.maze.end() def enter_maze(self,maze): self.maze = maze self._pos = maze.start() self.pos = maze.start() self._distance = 0 self.maze.add_element(self._agent_element) def enter_scene(self,scene): self._scene = scene self._scene.resetCamera() def erase(self): self.maze.remove_element(self._agent_element) del self._agent_element class Babylon: def __init__(self, BABYLON, canvas): if not BABYLON.Engine.isSupported(): raise Exception("Babylon not Supported") self.lib = BABYLON self.Engine = JSConstructor(self.lib.Engine) self.Scene = JSConstructor(self.lib.Scene) self.FreeCamera = JSConstructor(self.lib.FreeCamera) self.Vector3 = JSConstructor(self.lib.Vector3) self.Color3 = JSConstructor(self.lib.Color3) self.StandardMaterial = JSConstructor(self.lib.StandardMaterial) self.Texture = JSConstructor(self.lib.Texture) self.CubeTexture = JSConstructor(self.lib.CubeTexture) self.PointLight = JSConstructor(self.lib.PointLight) self.Mesh = JSConstructor(self.lib.Mesh) self.Animation = JSConstructor(self.lib.Animation) self.engine = self.Engine(canvas,True) window.bind('resize',self._resizeHandler) def _resizeHandler(self,*args): self.engine.resize() class MazeScene: BLOCK_SIZE = 8 CAMERA_VERTPOS = 5 CAMERA_VERTROT = 0 def __init__(self, maze, canvas): self._intervals = [] self._babylon = Babylon(window.BABYLON, canvas) self._maze = maze self._scene_objects = {} self._cache = {} self._scene = self._createScene() self._createMaze() #self._scene.activeCamera.attachControl(canvas) self.camera = self._scene.activeCamera self._scene.activeCamera.position = self.mazeToScenePos(self._maze.start(),self.CAMERA_VERTPOS) self._scene.activeCamera.rotation.x=self.CAMERA_VERTROT self._babylon.engine.runRenderLoop(self.renderLoop) def set_interval(self,handler, interval_msec): i = { 'handler':handler, 'next_run':time.time()+interval_msec*0.001, 'interval':interval_msec*0.001, } self._intervals.append(i) return i def clear_interval(self, interval): self._intervals.remove(interval) def resetCamera(self): self.camera.position=self.mazeToScenePos(self._maze.start(),self.CAMERA_VERTPOS) self._scene.activeCamera.rotation.x=self.CAMERA_VERTROT def _createBlock(self,id): if 'Cube' in self._cache: return self._cache['Cube'].clone(id) else: cubeWallMaterial = self._babylon.StandardMaterial("cubeWalls", self._scene) cubeWallMaterial.emissiveTexture = self._babylon.Texture(MEDIA_PATH+"textures/masonry-wall-texture.jpg", self._scene) cubeWallMaterial.bumpTexture = self._babylon.Texture(MEDIA_PATH+"textures/masonry-wall-bump-map.jpg", self._scene) cubeWallMaterial.specularTexture = self._babylon.Texture(MEDIA_PATH+"textures/masonry-wall-normal-map.jpg", self._scene) material1 = self._babylon.StandardMaterial("mat1", self._scene) material1.diffuseColor = self._babylon.Color3(1, 1, 0) mainCube = self._babylon.lib.Mesh.CreateBox("cachedCube", self.BLOCK_SIZE, self._scene) mainCube.position = self.mazeToScenePos((-5,-5),0) #mainCube.material = material1 mainCube.material = cubeWallMaterial self._cache['Cube'] = mainCube return self._createBlock(id) def mazeToScenePos(self,maze_pos,vertical=None): if not vertical: vertical = self.BLOCK_SIZE/2 row,col = maze_pos mWidth = self._maze.width() mHeight = self._maze.height() return self._babylon.Vector3(self.BLOCK_SIZE / 2 + (row - (mWidth / 2)) * self.BLOCK_SIZE, vertical, self.BLOCK_SIZE / 2 + (col - (mHeight / 2)) * self.BLOCK_SIZE) def moveCamera(self,maze_pos,time=0.7): camera = self._scene_objects['camera'] return self._babylon.lib.Animation.CreateAndStartAnimation("rotateCamAnimation", camera, "position", 30, int(30*time), camera.position, self.mazeToScenePos(maze_pos,self.CAMERA_VERTPOS), self._babylon.lib.PyAnimation.L_TYPE.CONST) def turnCamera(self,direction,time=0.3): if direction == 'S' or direction == '-N': angle = math.pi/2 elif direction == 'N' or direction == '-S': angle = -math.pi/2 elif direction == 'E' or direction == '-W': angle = 0 elif direction == 'W' or direction == '-E': angle = math.pi camera = self._scene_objects['camera'] return self._babylon.lib.Animation.CreateAndStartAnimation("rotateCamAnimation", camera, "rotation.y", 30, int(30*time), camera.rotation.y, angle, self._babylon.lib.PyAnimation.L_TYPE.CONST) def overView(self): camera = self._scene_objects['camera'] self._babylon.lib.Animation.CreateAndStartAnimation("rotateCamAnimation", camera, "rotation.x", 30, 300, camera.rotation.x, (math.pi/2-0.1), self._babylon.lib.PyAnimation.L_TYPE.CONST) self._babylon.lib.Animation.CreateAndStartAnimation("backoffCamAnimation", camera, "position", 30, 300, camera.position, self.mazeToScenePos((self._maze.height()/2,self._maze.width()/2),180), self._babylon.lib.PyAnimation.L_TYPE.CONST) def _createMaze(self): for pos in self._maze: if self._maze[pos]['block'] == 'W': id = 'wall.'+str(pos) cube = self._createBlock(id) cube.position = self.mazeToScenePos(pos) self._scene_objects[id] = cube def _createScene(self): mWidth = self._maze.width() mHeight = self._maze.height() # Scene scene = self._babylon.Scene(self._babylon.engine) scene.gravity = self._babylon.Vector3(0, -0.8, 0) # Camera freeCamera = self._babylon.FreeCamera("free", self.mazeToScenePos(self._maze.start(),5), scene) freeCamera.minZ = 1 freeCamera.applyGravity = True freeCamera.ellipsoid = self._babylon.Vector3(1, 1, 1) self._scene_objects['camera'] = freeCamera freeCamera.position=self._babylon.Vector3(4,10,3) freeCamera.rotation=self._babylon.Vector3(0,-2,0) # Ground groundMaterial = self._babylon.StandardMaterial("groundMat", scene) groundMaterial.emissiveTexture = self._babylon.Texture(MEDIA_PATH+"textures/arroway.de_tiles-35_d100.jpg", scene) groundMaterial.emissiveTexture.uScale = mWidth groundMaterial.emissiveTexture.vScale = mHeight groundMaterial.bumpTexture = self._babylon.Texture(MEDIA_PATH+"textures/arroway.de_tiles-35_b010.jpg", scene) groundMaterial.bumpTexture.uScale = mWidth groundMaterial.bumpTexture.vScale = mHeight groundMaterial.specularTexture = self._babylon.Texture(MEDIA_PATH+"textures/arroway.de_tiles-35_s100-g100-r100.jpg", scene) groundMaterial.specularTexture.uScale = mWidth groundMaterial.specularTexture.vScale = mHeight ground = self._babylon.lib.Mesh.CreateGround("ground",(mHeight + 2) * self.BLOCK_SIZE, (mWidth + 2) * self.BLOCK_SIZE, 1, scene, False) ground.material = groundMaterial ground.position.x=0 ground.position.z=0 self._scene_objects['ground'] = ground # Skybox skybox = self._babylon.lib.Mesh.CreateBox("skyBox", 800.0, scene) skyboxMaterial = self._babylon.StandardMaterial("skyBox", scene) skyboxMaterial.backFaceCulling = False skyboxMaterial.reflectionTexture = self._babylon.CubeTexture(MEDIA_PATH+"textures/mountains/sky", scene) skyboxMaterial.reflectionTexture.coordinatesMode = self._babylon.lib.consts.Texture.SKYBOX_MODE skyboxMaterial.diffuseColor = self._babylon.Color3(0, 0, 0) skyboxMaterial.specularColor = self._babylon.Color3(0, 0, 0) skybox.material = skyboxMaterial #At Last, add some lights to our scene light0 = self._babylon.PointLight("pointlight0", self._babylon.Vector3(28, 78, 385), scene) light0.diffuse = self._babylon.Color3(0.5137254901960784, 0.2117647058823529, 0.0941176470588235) light0.intensity = 0.2 self._scene_objects['light0'] = light0 light1 = self._babylon.PointLight("pointlight1", self._babylon.Vector3(382, 96, 4), scene) light1.diffuse = self._babylon.Color3(1, 0.7333333333333333, 0.3568627450980392) light1.intensity = 0.3 self._scene_objects['light1'] = light1 return scene def renderLoop(self,*args): self._babylon.engine.resize() self._scene.render() t = time.time() for i in self._intervals: if t > i["next_run"]: i["handler"]() i["next_run"] = t + i["interval"] class App: initial_arena = [ ['W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'], ['W','S','W','W',' ',' ',' ',' ',' ','W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','E',''], ['W',' ',' ','W',' ','W','W','W',' ','W',' ','W',' ','W',' ','W',' ','W','W','W','W','W'], ['W','W',' ','W',' ',' ','W','W',' ','W',' ','W',' ','W',' ','W',' ',' ',' ','W',' ','W'], ['W','W',' ','W','W',' ','W','W',' ','W',' ','W',' ','W',' ','W','W','W',' ','W',' ','W'], ['W',' ',' ',' ','W',' ',' ','W',' ','W',' ','W',' ','W',' ',' ',' ','W',' ','W',' ','W'], ['W',' ','W',' ','W','W',' ','W',' ','W',' ','W',' ','W','W','W',' ','W',' ','W',' ','W'], ['W',' ','W',' ',' ','W',' ','W',' ','W',' ','W',' ','W',' ',' ',' ','W',' ','W',' ','W'], ['W',' ','W','W',' ','W',' ','W',' ','W',' ','W',' ','W',' ','W','W','W',' ','W',' ','W'], ['W',' ',' ','W',' ','W',' ','W',' ','W',' ','W',' ','W',' ',' ','W',' ',' ','W',' ','W'], ['W','W',' ','W',' ','W',' ','W',' ','W',' ','W',' ','W','W',' ','W',' ','W','W',' ','W'], ['W',' ',' ','W',' ',' ',' ','W',' ',' ',' ',' ',' ',' ',' ',' ','W',' ',' ',' ',' ','W'], ['W',' ','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W',' ','W'], ['W',' ',' ',' ',' ',' ',' ','W','W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W',' ','W'], ['W',' ',' ',' ','W','W',' ','W','W',' ','W','W',' ','W','W','W',' ','W',' ','W',' ','W'], ['W',' ',' ',' ','W',' ',' ',' ',' ',' ','W','W',' ','W',' ',' ',' ','W',' ','W',' ','W'], ['W',' ',' ',' ','W',' ','W','W','W','W','W','W',' ','W',' ','W','W','W',' ','W',' ','W'], ['W',' ',' ',' ','W',' ','W','W','W','W','W','W',' ','W',' ','W','W',' ',' ','W',' ','W'], ['W',' ',' ',' ','W',' ',' ',' ',' ',' ',' ','W',' ','W',' ',' ',' ',' ','W','W',' ','W'], ['W',' ',' ',' ','W','W','W','W','W','W',' ','W',' ','W','W','W','W','W','W','W',' ','W'], ['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'], ['W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'], ] def error(self,*args): print(*args,file=sys.stderr) def write(self,data): self.logArea.value = self.logArea.value+data def __init__(self): self.maze = Maze(doc["mazeMapDiv"]) self.maze.load(App.initial_arena) self.agent = None self.agent_status = "No Agent" self.agent_function = None self.agent_init_function = None self.editor_elt = doc["editor"] self.doc = window.cm.getDoc() self.console = Console(doc["console"]) self.console.editor_ns['maze']=self.maze try: self.scene = MazeScene(self.maze,doc["canvas"]) except Exception as Ex: self.scene = None print("Canvas not supported:", Ex) doc["canvas"].style.display="none" doc["babylon-nosupport"].style.display="block" self.console.editor_ns['scene']=self.scene self._interval = None self.log = Output(doc["logArea"]) self.err = Output(doc["logArea"]) self.err.Class = 'log error' self.log.Class = 'log normal' sys.stdout = self.log sys.stderr = self.err self.infoElements = { 'agent': { 'pos':doc["agent_pos"], 'health':doc["agent_health"], 'distance':doc["agent_distance"], 'status':doc["agent_status"] }, } self._bind_buttons() self._updateUI() self.newAgent() if self.scene: self.scene.set_interval(self._updateUI,300) else: timer.set_interval(self._updateUI, 300) def _bind_buttons(self): doc["stepBtn"].bind("click",self.step) doc["stepBtn-I"].bind("click",self.step) doc["runBtn"].bind("click",self.run) doc["runBtn-I"].bind("click",self.run) doc["stopBtn"].bind("click",self.stop) doc["stopBtn-I"].bind("click",self.stop) doc["restartBtn"].bind("click",self.newAgent) doc["clearLogBtn"].bind("click",self.log.clear) doc["clearLogBtn"].bind("click",self.err.clear) def _updateUI(self,*args): self._updateInfo() def newAgent(self,ev=None): if self.agent: self.agent.erase() del self.console.editor_ns['agent'] try: self.compile() except Exception as ex: self.error("Chyba ve vaší strategii (v pythonovském kódu):", ex) self.agent = None return False self.agent = Agent() self.maze.reload() self.agent.enter_maze(self.maze) if self.scene: self.agent.enter_scene(self.scene) if self.agent_init_function: try: self.agent_init_function(self.agent) self.console.editor_ns['agent'] = self.agent self.agent_status='Searching' self._updateUI() except Exception as ex: self.error("Chyba při inicializaci agenta, agent umřel:", ex) self.agent.erase() self.agent = None self.agent_status='None' return False return True def step(self,ev=None): if not self.agent: if not self.newAgent(): return False try: self.agent._fuel = 1 self.agent_function(self.agent) except Exception as ex: self.agent.hurt() self.error("Chyba při provádění tahu, agent se zranil:", ex) self._updateUI() def compile(self,ev=None): locals = { 'finish':self.agentFinish, 'fail':self.agentFail } globals = { } exec(self.doc.getValue(),globals,locals) self.agent_function = locals['tah'] self.agent_init_function = locals.get('init',None) def _doStep(self): if not self.agent: self.stop() return if self.agent.health <= 0: self.error("Agent umřel.") self.stop() self.agent.erase() self.agent = None self.agent_status='None' self.step() def _updateInfo(self): self.infoElements['agent']['status'].html = self.agent_status if self.agent: self.infoElements['agent']['pos'].html = toHtml(self.agent.pos) self.infoElements['agent']['health'].style = { "width":str(self.agent.health*4)+'px', "backgroundColor":"rgb"+str(self.agent.health_color()) } self.infoElements['agent']['distance'].html = toHtml(self.agent.distance) def run(self,ev=None): if not self.agent: if not self.newAgent(): return doc["runBtn"].Class='btn disabled btn-success' doc["stopBtn"].Class='btn enabled btn-warning' if self.scene: self._interval=self.scene.set_interval(self._doStep,Agent.SPEED*1000) else: self._interval=timer.set_interval(self._doStep, Agent.SPEED*1000) def stop(self,ev=None): if self._interval: doc["runBtn"].Class='btn enabled btn-success' doc["stopBtn"].Class='btn disabled btn-warning' if self.scene: self.scene.clear_interval(self._interval) else: timer.clear_interval(self._interval) def agentFinish(self): if self.agent and self.agent.pos == self.maze.end(): self.stop() self.agent_status='Found Exit' self._updateUI() self.scene.overView() alert("Well done") elif self.agent: self.agent_status='Died' self._updateUI() alert("Not well done") self.agent.erase() self.agent = None self.error("Agent died.") self.stop() self._updateUI() def agentFail(self): alert("Agent východ nenašel a hru vzdal.") self.agent_status='Failed' self.stop() self._updateUI() def toHtml(var): return str(var).replace('\n','
').replace('>','>').replace('<','<').replace(' ',' ') def init(): app = App() init()