labyrinth.py 4.02 KB
Newer Older
Constantin's avatar
Constantin committed
1 2 3
# class for the labyrinth

from enum import Enum
Constantin's avatar
changes  
Constantin committed
4
from functools import reduce
Ralf's avatar
Ralf committed
5
from player import Player
Constantin's avatar
Constantin committed
6

Constantin's avatar
Constantin committed
7 8
directions = {"north": (-1, 0), "east": (0, 1), "west": (0, -1), "south": (1, 0)}

Constantin's avatar
Constantin committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
class Labyrinth:
	class GroundType(Enum):
		FLOOR = 1
		WALL  = 2
		
	class Field:
		def __init__(self,groundtype,column,row,labyrinth):
			# please only read from these members
			self.groundtype = groundtype
			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 self.things.count(thing) == 0
			assert self.groundtype != Labyrinth.GroundType.WALL
			self.things.append(thing)
Constantin's avatar
Constantin committed
29
		def neighbor(self,dy,dx):
Constantin's avatar
Constantin committed
30
			return self.labyrinth.getTileAt(self.row + dy, self.column + dx)
Constantin's avatar
Constantin committed
31 32 33
		def isWalkable(self):
			# this may be more complicated in the future
			return self.groundtype == Labyrinth.GroundType.FLOOR
Constantin's avatar
Constantin committed
34
		def __str__(self):
Constantin's avatar
Constantin committed
35 36 37 38 39 40 41
			# always starts with there is/there are
			if self.groundtype == Labyrinth.GroundType.WALL:
				return "there is a wall"
			else:
				if len(self.things) == 0:
					return "there is nothing"
				elif len(self.things) == 1:
Constantin's avatar
Constantin committed
42
					return "there is "+str(self.things[0])
Constantin's avatar
Constantin committed
43 44 45 46 47 48 49 50
				else:
					return "there are "+(", ".join([x.toString() for x in self.things[:-1]]))+" and "+self.things[-1].toString()
	
	# constructor for a completely read labyrinth
	def __init__(self,width=None,height=None,stream=None):
		if width is not None: # empty labyrinth of given size
			assert height is not None
			assert stream is None
Constantin's avatar
Constantin committed
51
			self.tiles = [[Labyrinth.Field(Labyrinth.GroundType.FLOOR,col,row,self) for col in range(width)] for row in range(height)]
Constantin's avatar
Constantin committed
52 53 54 55 56 57 58 59 60 61 62
		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):
Constantin's avatar
Constantin committed
63 64
				if char in [' ','0','_']:
					row.append(Labyrinth.Field(Labyrinth.GroundType.FLOOR,colnum,rownum,self))
Constantin's avatar
Constantin committed
65
				elif char in ['W','w','#','1']:
Constantin's avatar
Constantin committed
66
					row.append(Labyrinth.Field(Labyrinth.GroundType.WALL,colnum,rownum,self))
Constantin's avatar
Constantin committed
67 68 69
			tiles.append(row)
		return tiles
	
Ralf's avatar
Ralf committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
	def writelab(self, stream):
		for row in self.tiles:
			for field in row:
				if field.groundtype == Labyrinth.GroundType.FLOOR:
					print("#", file=stream)
				else:
					if not field.things:
						print(".", file=stream)
					elif len(field.things) == 1:
						if isinstance(field.things[0], Player):
							print("P", file=stream)
						else:
							print("?", file=stram)
					else:
						print("+", file=stream)
			print("\n", file=stream)
	
Constantin's avatar
Constantin committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	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]
	
Constantin's avatar
changes  
Constantin committed
102 103 104 105
	def getTiles(self):
		return reduce(lambda x,y:x+y, self.tiles)
	
	def getFreeTiles (self):
Constantin's avatar
Constantin committed
106
		return list(filter(lambda f: f.groundtype == Labyrinth.GroundType.FLOOR and len(f.things) == 0 ,self.getTiles()))
Constantin's avatar
changes  
Constantin committed
107
	
Constantin's avatar
Constantin committed
108 109 110 111 112 113
	def getDescription (self, field):
		descr = ""
		for name,field in zip(["North","South","West","East"], [self.getTileAt(field.row+row,field.column+column) for row,column in [(-1,0),(1,0),(0,-1),(0,1)]]):
			descr += "In the "+name+" "+("there is a wall" if field is None else str(field))+".\n"
		return descr
	
Constantin's avatar
changes  
Constantin committed
114 115 116 117
	def moveThing (self, thing, newField):
		if thing.onMove(newField) == False:
			return False
		assert thing.field.labyrinth == self
Constantin's avatar
Constantin committed
118
		assert newField.labyrinth == self
Constantin's avatar
changes  
Constantin committed
119
		thing.field._removeThing(thing)
Constantin's avatar
Constantin committed
120
		oldField = thing.field
Constantin's avatar
changes  
Constantin committed
121 122
		thing.field = newField
		newField._addThing(thing)
Constantin's avatar
Constantin committed
123
		thing.afterMove(oldField)
Constantin's avatar
changes  
Constantin committed
124 125 126 127
		return True
	
	def createThing (self, thing, field):
		thing.field = field
Constantin's avatar
Constantin committed
128
		field._addThing(thing)
Constantin's avatar
Constantin committed
129 130
		thing.onMove(field)
		thing.afterMove(field)