labyrinth.py 4.21 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
Constantin's avatar
Constantin committed
5

Constantin's avatar
Constantin committed
6
directions = {"north": (-1, 0), "east": (0, 1), "west": (0, -1), "south": (1, 0)}
Constantin's avatar
SOKOBAN  
Constantin committed
7
directions_inv = {v:k for k, v in directions.items()}
Constantin's avatar
Constantin committed
8

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
				else:
					return "there are "+(", ".join([x.toString() for x in self.things[:-1]]))+" and "+self.things[-1].toString()
	
	# constructor for a completely read labyrinth
Ralf's avatar
Ralf committed
47 48
	def __init__(self,game=None,width=None,height=None,stream=None):
		self.game = game
Constantin's avatar
Constantin committed
49 50 51
		if width is not None: # empty labyrinth of given size
			assert height is not None
			assert stream is None
Constantin's avatar
Constantin committed
52
			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
53 54 55 56 57 58 59 60 61 62 63
		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
64 65
				if char in [' ','0','_']:
					row.append(Labyrinth.Field(Labyrinth.GroundType.FLOOR,colnum,rownum,self))
Constantin's avatar
Constantin committed
66
				elif char in ['W','w','#','1']:
Constantin's avatar
Constantin committed
67
					row.append(Labyrinth.Field(Labyrinth.GroundType.WALL,colnum,rownum,self))
Constantin's avatar
Constantin committed
68 69 70
			tiles.append(row)
		return tiles
	
Ralf's avatar
Ralf committed
71 72
	def writelab(self):
		result = ""
Ralf's avatar
Ralf committed
73 74
		for row in self.tiles:
			for field in row:
Ralf's avatar
Ralf committed
75
				if field.groundtype == Labyrinth.GroundType.WALL:
Ralf's avatar
Ralf committed
76
					result += "#"
Ralf's avatar
Ralf committed
77 78
				else:
					if not field.things:
Ralf's avatar
Ralf committed
79
						result += "."
Ralf's avatar
Ralf committed
80
					elif len(field.things) == 1:
Ralf's avatar
Ralf committed
81
						result += field.things[0].asChar()
Ralf's avatar
Ralf committed
82
					else:
Ralf's avatar
Ralf committed
83 84 85
						result += "+"
			result += "\n"
		return result
Ralf's avatar
Ralf committed
86
	
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
	
Ralf's avatar
Ralf committed
114
	def moveThing (self, thing, newField, beforeFinish=None):
Constantin's avatar
changes  
Constantin committed
115 116 117
		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)
Ralf's avatar
Ralf committed
123 124 125 126
		if beforeFinish is None:
			self.game.showAdmins()
		else:
			beforeFinish()
Constantin's avatar
Constantin committed
127
		thing.afterMove(oldField)
Constantin's avatar
changes  
Constantin committed
128 129 130 131
		return True
	
	def createThing (self, thing, field):
		thing.field = field
Constantin's avatar
Constantin committed
132
		field._addThing(thing)
Constantin's avatar
Constantin committed
133
		thing.onMove(field)
Ralf's avatar
Ralf committed
134 135 136 137 138 139 140
		thing.afterMove(field)
		self.game.showAdmins()
	
	def removeThing(self, thing):
		field = thing.field
		field.things.remove(thing)
		self.game.showAdmins()