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)