File indexing completed on 2024-04-21 04:01:56
0001 # -*- coding: utf-8 -*- 0002 0003 """ 0004 Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de> 0005 0006 SPDX-License-Identifier: GPL-2.0 0007 0008 """ 0009 0010 import weakref 0011 0012 from common import StrMixin, Debug 0013 from tile import Tile, elements 0014 0015 0016 class WallEmpty(Exception): 0017 0018 """exception when trying to get a tile off the empty wall""" 0019 0020 0021 class KongBox: 0022 0023 """a non-ui kong box""" 0024 0025 def __init__(self): 0026 self._tiles = [] 0027 0028 def fill(self, tiles): 0029 """fill the box""" 0030 self._tiles = tiles 0031 0032 def pop(self, count): 0033 """get count tiles from kong box""" 0034 if len(self._tiles) < count: 0035 raise WallEmpty 0036 tiles = self._tiles[-count:] 0037 self._tiles = self._tiles[:-count] 0038 return tiles 0039 0040 def __getitem__(self, index): 0041 return self._tiles[index] 0042 0043 def __len__(self): 0044 """# of tiles in kong box""" 0045 return len(self._tiles) 0046 0047 0048 class Wall(StrMixin): 0049 0050 """represents the wall with four sides. self.wall[] indexes them 0051 counter clockwise, 0..3. 0 is bottom. 0052 Wall.tiles always holds references to all tiles in the game even 0053 when they are used""" 0054 tileClass = Tile 0055 kongBoxClass = KongBox 0056 0057 def __init__(self, game): 0058 """init and position the wall""" 0059 self._game = weakref.ref(game) # avoid cycles for garbage collection 0060 wallSize = int(Debug.wallSize) 0061 if not wallSize: 0062 wallSize = elements.count(game.ruleset) 0063 self.tiles = [self.tileClass(Tile.unknown) 0064 for _ in range(wallSize)] 0065 self.living = None 0066 self.kongBox = self.kongBoxClass() 0067 assert len(self.tiles) % 8 == 0 0068 0069 @property 0070 def game(self): 0071 """hide the fact that this is a weakref""" 0072 return self._game() 0073 0074 @staticmethod 0075 def __nameTile(tile, element): 0076 """define what tile this is""" 0077 if element is None: 0078 return tile 0079 assert isinstance(element, Tile), element 0080 if isinstance(tile, Tile): 0081 return element 0082 # tile is UITile 0083 tile.tile = element 0084 return tile 0085 0086 def deal(self, tiles=None, deadEnd=False): 0087 """deal tiles. May raise WallEmpty. 0088 Returns a list of tiles""" 0089 if tiles is None: 0090 tiles = [None] 0091 count = len(tiles) 0092 if deadEnd: 0093 dealTiles = self.kongBox.pop(count) 0094 if len(self.kongBox) % 2 == 0: 0095 self._placeLooseTiles() 0096 else: 0097 if len(self.living) < count: 0098 raise WallEmpty 0099 dealTiles = self.living[:count] 0100 self.living = self.living[count:] 0101 return [self.__nameTile(*x) for x in zip(dealTiles, tiles)] 0102 0103 def build(self, shuffleFirst=False): 0104 """virtual: build visible wall""" 0105 0106 def _placeLooseTiles(self, deferredResult=None): 0107 """to be done only for UIWall""" 0108 0109 def decorate4(self, deferredResult=None): # pylint: disable=unused-argument 0110 """virtual: show player info on the wall""" 0111 0112 def hide(self): 0113 """virtual: hide all four walls and their decorators""" 0114 0115 def divide(self): 0116 """divides a wall, building a living end and a dead end""" 0117 # neutralise the different directions of winds and removal of wall 0118 # tiles 0119 assert self.game.divideAt is not None 0120 # shift tiles: tile[0] becomes living end 0121 self.tiles[:] = self.tiles[ 0122 self.game.divideAt:] + \ 0123 self.tiles[0:self.game.divideAt] 0124 kongBoxSize = self.game.ruleset.kongBoxSize 0125 self.living = self.tiles[:-kongBoxSize] 0126 boxTiles = self.tiles[-kongBoxSize:] 0127 for pair in range(kongBoxSize // 2): 0128 boxTiles = boxTiles[:pair * 2] + [ 0129 boxTiles[pair * 2 + 1], 0130 boxTiles[pair * 2]] + \ 0131 boxTiles[pair * 2 + 2:] 0132 self.kongBox.fill(boxTiles) 0133 0134 @staticmethod 0135 def name(): 0136 """name for debug messages""" 0137 return '4sided wall' 0138 0139 def __str__(self): 0140 """for debugging""" 0141 return self.name()