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

from enum import Enum
Ralf's avatar
Ralf committed
4
import itertools
Constantin Berhard's avatar
changes  
Constantin Berhard committed
5
from functools import reduce
Ralf's avatar
Ralf committed
6
from wall import Wall
Constantin Berhard's avatar
Constantin Berhard committed
7

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

Ralf's avatar
Ralf committed
11 12 13 14 15 16 17
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:
Ralf's avatar
Ralf committed
18
		return "there are "+(", ".join([str(x) for x in things[:-1]]))+" and "+str(things[-1])
Ralf's avatar
Ralf committed
19 20


Constantin Berhard's avatar
Constantin Berhard committed
21 22
class Labyrinth:
	class Field:
Ralf's avatar
Ralf committed
23
		def __init__(self,column,row,labyrinth):
Constantin Berhard's avatar
Constantin Berhard committed
24 25 26 27 28 29 30 31 32
			# 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):
Ralf's avatar
Ralf committed
33
			assert thing not in self.things
Constantin Berhard's avatar
Constantin Berhard committed
34
			self.things.append(thing)
35
		def neighbor(self,dy,dx):
Constantin Berhard's avatar
Constantin Berhard committed
36
			return self.labyrinth.getTileAt(self.row + dy, self.column + dx)
37
		def isWalkable(self):
Ralf's avatar
Ralf committed
38 39 40 41
			for thing in self.things:
				if thing.blocksMove():
					return False
			return True
Constantin Berhard's avatar
Constantin Berhard committed
42 43
	
	# constructor for a completely read labyrinth
Ralf's avatar
Ralf committed
44 45
	def __init__(self,game=None,width=None,height=None,stream=None):
		self.game = game
Constantin Berhard's avatar
Constantin Berhard committed
46 47 48
		if width is not None: # empty labyrinth of given size
			assert height is not None
			assert stream is None
Ralf's avatar
Ralf committed
49
			self.tiles = [[Labyrinth.Field(col,row,self) for col in range(width)] for row in range(height)]
Constantin Berhard's avatar
Constantin Berhard committed
50 51 52 53 54 55 56 57 58 59
		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 = []
Ralf's avatar
Ralf committed
60 61
			for colnum,char in enumerate(line[:-1]):
				field = Labyrinth.Field(colnum,rownum,self)
Constantin Berhard's avatar
Constantin Berhard committed
62
				if char in [' ','0','_']:
Ralf's avatar
Ralf committed
63
					pass
Constantin Berhard's avatar
Constantin Berhard committed
64
				elif char in ['W','w','#','1']:
Constantin Berhard's avatar
Constantin Berhard committed
65
					wall = Wall(self.game)
Ralf's avatar
Ralf committed
66 67 68 69 70
					wall.field = field
					field._addThing(wall)
				else:
					raise Exception("Unexpected character '{0}'".format(char))
				row.append(field)
Constantin Berhard's avatar
Constantin Berhard committed
71 72 73
			tiles.append(row)
		return tiles
	
Ralf's avatar
Ralf committed
74 75
	def writelab(self):
		result = ""
Ralf's avatar
Ralf committed
76 77
		for row in self.tiles:
			for field in row:
Ralf's avatar
Ralf committed
78 79 80 81
				if not field.things:
					result += "."
				elif len(field.things) == 1:
					result += repr(field.things[0])
Ralf's avatar
Ralf committed
82
				else:
Ralf's avatar
Ralf committed
83
					result += "+"
Ralf's avatar
Ralf committed
84 85
			result += "\n"
		return result
Ralf's avatar
Ralf committed
86
	
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 Berhard's avatar
changes  
Constantin Berhard committed
102 103 104 105
	def getTiles(self):
		return reduce(lambda x,y:x+y, self.tiles)
	
	def getFreeTiles (self):
Ralf's avatar
Ralf committed
106
		return list(filter(lambda field: not field.things, self.getTiles()))
Constantin Berhard's avatar
changes  
Constantin Berhard committed
107
	
Ralf's avatar
Ralf committed
108
	def getDescription (self, player):
109
		descr = ""
Ralf's avatar
Ralf committed
110 111
		local = list(filter(lambda thing: thing != player, player.field.things))
		if local:
Ralf's avatar
Ralf committed
112
			descr += "Next to you, "+describeThings(local)+".\n"
Ralf's avatar
Ralf committed
113 114
		for direction, (dy,dx) in directions.items():
			field = player.field.neighbor(dx=dx, dy=dy)
Ralf's avatar
Ralf committed
115
			if field is None:
Constantin Berhard's avatar
Constantin Berhard committed
116
				descr += "In the "+direction+", there is a very hard wall\n"
Ralf's avatar
Ralf committed
117
			elif field.things:
Ralf's avatar
Ralf committed
118
				descr += "In the "+direction+", "+describeThings(field.things)+".\n"
Ralf's avatar
Ralf committed
119 120
		if not descr:
			descr = "There is absolutely nothing around you.\n"
121 122
		return descr
	
Ralf's avatar
Ralf committed
123
	def moveThing (self, thing, newField, beforeFinish=None):
Constantin Berhard's avatar
changes  
Constantin Berhard committed
124 125 126
		if thing.onMove(newField) == False:
			return False
		assert thing.field.labyrinth == self
Constantin Berhard's avatar
Constantin Berhard committed
127
		assert newField.labyrinth == self
Constantin Berhard's avatar
changes  
Constantin Berhard committed
128
		thing.field._removeThing(thing)
Constantin Berhard's avatar
Constantin Berhard committed
129
		oldField = thing.field
Constantin Berhard's avatar
changes  
Constantin Berhard committed
130 131
		thing.field = newField
		newField._addThing(thing)
Ralf's avatar
Ralf committed
132 133 134 135
		if beforeFinish is None:
			self.game.showAdmins()
		else:
			beforeFinish()
Constantin Berhard's avatar
Constantin Berhard committed
136
		thing.afterMove(oldField)
Constantin Berhard's avatar
changes  
Constantin Berhard committed
137 138 139 140
		return True
	
	def createThing (self, thing, field):
		thing.field = field
Constantin Berhard's avatar
Constantin Berhard committed
141
		field._addThing(thing)
Ralf's avatar
Ralf committed
142 143
		if thing.onMove(field):
			thing.afterMove(field)
Ralf's avatar
Ralf committed
144 145 146 147 148 149
		self.game.showAdmins()
	
	def removeThing(self, thing):
		field = thing.field
		field.things.remove(thing)
		self.game.showAdmins()
Constantin Berhard's avatar
Constantin Berhard committed
150
	
151 152 153 154
	def tellNeighbors(self, teller, verb, message):
		self.tell(teller, teller.neighborThings(), verb, message)
	
	def tell(self,teller,tolds,verb,message):
Constantin Berhard's avatar
Constantin Berhard committed
155 156 157 158
		for admin in self.game.admins:
			if hasattr(teller,'uid'):
				admin.send("player {0} {1}s: {2}".format(teller.uid, verb, message))
			else:
Constantin Berhard's avatar
Constantin Berhard committed
159
				admin.send("{0} {1}s: {2}".format(str(teller), verb, message))
Ralf's avatar
Ralf committed
160 161 162
		for thing in tolds:
			assert thing != teller
			thing.tell(teller,verb,message)