# class for the labyrinth from enum import Enum import itertools from functools import reduce from wall import Wall directions = {"north": (-1, 0), "east": (0, 1), "west": (0, -1), "south": (1, 0)} directions_inv = {v:k for k, v in directions.items()} def describeThings(things): # always starts with there is/there are if len(things) == 0: return "there is nothing" elif len(things) == 1: return "there is "+str(things[0]) else: return "there are "+(", ".join([x.toString() for x in things[:-1]]))+" and "+things[-1].toString() class Labyrinth: class Field: def __init__(self,column,row,labyrinth): # please only read from these members self.column = column self.row = row self.labyrinth = labyrinth # the following usually change self.things = [] # list of things/players on this field def _removeThing(self,thing): self.things.remove(thing) def _addThing(self,thing): assert thing not in self.things self.things.append(thing) def neighbor(self,dy,dx): return self.labyrinth.getTileAt(self.row + dy, self.column + dx) def isWalkable(self): for thing in self.things: if thing.blocksMove(): return False return True # constructor for a completely read labyrinth def __init__(self,game=None,width=None,height=None,stream=None): self.game = game if width is not None: # empty labyrinth of given size assert height is not None assert stream is None self.tiles = [[Labyrinth.Field(col,row,self) for col in range(width)] for row in range(height)] elif height is not None: assert False # width None, height not elif stream is not None: self.tiles = self._readlab(stream) # function for reading a labyrinth from a file def _readlab (self,stream): tiles = [] for rownum,line in enumerate(stream): row = [] for colnum,char in enumerate(line[:-1]): field = Labyrinth.Field(colnum,rownum,self) if char in [' ','0','_']: pass elif char in ['W','w','#','1']: wall = Wall() wall.field = field field._addThing(wall) else: raise Exception("Unexpected character '{0}'".format(char)) row.append(field) tiles.append(row) return tiles def writelab(self): result = "" for row in self.tiles: for field in row: if not field.things: result += "." elif len(field.things) == 1: result += repr(field.things[0]) else: result += "+" result += "\n" return result def getWidth(self): return len(self.tiles[0]) def getHeight(self): return len(self.tiles) def outOfBounds(self,row,column): return row < 0 or column < 0 or row >= self.getHeight() or column >= self.getWidth() def getTileAt(self,row,column): if self.outOfBounds(row,column): return None else: return self.tiles[row][column] def getTiles(self): return reduce(lambda x,y:x+y, self.tiles) def getFreeTiles (self): return list(filter(Labyrinth.Field.isWalkable, self.getTiles())) def getDescription (self, player): descr = "" local = list(filter(lambda thing: thing != player, player.field.things)) if local: descr += "Next to you, "+describeThings(local)+"\n" for direction, (dy,dx) in directions.items(): field = player.field.neighbor(dx=dx, dy=dy) if field is None: continue if field.things: descr += "In the "+direction+", "+describeThings(field.things)+"\n" return descr def moveThing (self, thing, newField, beforeFinish=None): if thing.onMove(newField) == False: return False assert thing.field.labyrinth == self assert newField.labyrinth == self thing.field._removeThing(thing) oldField = thing.field thing.field = newField newField._addThing(thing) if beforeFinish is None: self.game.showAdmins() else: beforeFinish() thing.afterMove(oldField) return True def createThing (self, thing, field): thing.field = field field._addThing(thing) if thing.onMove(field): thing.afterMove(field) self.game.showAdmins() def removeThing(self, thing): field = thing.field field.things.remove(thing) self.game.showAdmins()