github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/families/battleship/sawtooth_battleship/battleship_board.py (about) 1 # Copyright 2016 Intel Corporation 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # ------------------------------------------------------------------------------ 15 16 # pylint: disable=consider-using-enumerate 17 18 import logging 19 import random 20 import string 21 import hashlib 22 23 from sawtooth_battleship.battleship_exceptions import BoardLayoutException 24 25 26 LOGGER = logging.getLogger(__name__) 27 28 29 class BoardLayout(object): 30 def __init__(self, size): 31 self.ship_positions = [] 32 self.size = size 33 34 def append(self, ship_position): 35 """Attempts to append the ship at the specified position. 36 37 Attributes: 38 ship_position (ShipPosition): The ship to append to the layout. 39 40 Raises: 41 BoardLayoutException: If the position is already occupied or is 42 otherwise invalid. 43 """ 44 45 self.ship_positions.append(ship_position) 46 47 # Check validity by rendering the board into a string 48 try: 49 self.render() 50 except BoardLayoutException as e: 51 self.ship_positions = self.ship_positions[:-1] 52 raise e 53 54 def render(self): 55 """Returns a game board layout as a list of strings. 56 57 Raises: 58 BoardLayoutException: If the position is already occupied or is 59 otherwise invalid. 60 """ 61 62 board = [['-'] * self.size for i in range(self.size)] 63 64 for position in self.ship_positions: 65 orientation = position.orientation 66 row = position.row 67 col = position.column 68 text = position.text 69 70 if orientation == 'horizontal': 71 for i in range(0, len(text)): 72 if board[row][col + i] != '-': 73 raise BoardLayoutException( 74 "can not place ship at {}{}, space " 75 "is occupied with {}".format( 76 'ABCDEFGHIJ'[col], 77 row, 78 board[row][col])) 79 board[row][col + i] = text[i] 80 elif orientation == 'vertical': 81 for i in range(0, len(text)): 82 if board[row + i][col] != '-': 83 raise BoardLayoutException( 84 "can not place ship at {}{}, space " 85 "is occupied with {}".format( 86 'ABCDEFGHIJ'[col], 87 row, 88 board[row][col])) 89 board[row + i][col] = text[i] 90 else: 91 assert False, "invalid orientation: {}".format(orientation) 92 93 return board 94 95 def render_hashed(self, nonces): 96 hashed_board = [[None] * self.size for _ in range(self.size)] 97 clear_board = self.render() 98 99 for row in range(0, self.size): 100 for col in range(0, self.size): 101 hashed_board[row][col] = hash_space( 102 clear_board[row][col], nonces[row][col]) 103 104 return hashed_board 105 106 def serialize(self): 107 data = {} 108 data['size'] = self.size 109 data['positions'] = [] 110 for position in self.ship_positions: 111 data['positions'].append(position.serialize()) 112 return data 113 114 @staticmethod 115 def deserialize(data): 116 layout = BoardLayout(data['size']) 117 for position in data['positions']: 118 layout.append(ShipPosition.deserialize(position)) 119 return layout 120 121 @staticmethod 122 def generate(ships, size=10, max_placement_attempts=100): 123 124 remaining = list(ships) 125 layout = BoardLayout(size) 126 127 while remaining: 128 ship = remaining[0] 129 remaining.remove(ship) 130 131 success = False 132 attempts = 0 133 while not success: 134 attempts += 1 135 136 orientation = random.choice( 137 ['horizontal', 138 'vertical']) 139 if orientation == 'horizontal': 140 row = random.randrange(0, size) 141 col = random.randrange(0, size - len(ship) + 1) 142 else: 143 row = random.randrange(0, size - len(ship) + 1) 144 col = random.randrange(0, size) 145 146 position = ShipPosition( 147 text=ship, row=row, column=col, orientation=orientation) 148 149 try: 150 layout.append(position) 151 success = True 152 except BoardLayoutException: 153 if attempts > max_placement_attempts: 154 LOGGER.debug("exceeded attempts, resetting...") 155 layout = BoardLayout(size) 156 remaining = list(ships) 157 break 158 159 return layout 160 161 162 class ShipPosition(object): 163 """Represents a ship and it's placement on the board. 164 165 Attributes: 166 text (str): Ship's textual representation (example: AAAAA, BBBB) 167 row (int): First row on which the ship appears (starts at 0) 168 column (int): First column on which the ship appears (starts at 0) 169 orientation (str): Whether placed horizontal or vertical 170 """ 171 172 def __init__(self, text, row, column, orientation): 173 self.text = text 174 self.row = row 175 self.column = column 176 self.orientation = orientation 177 178 def serialize(self): 179 data = {} 180 data['text'] = self.text 181 data['row'] = self.row 182 data['column'] = self.column 183 data['orientation'] = self.orientation 184 return data 185 186 @staticmethod 187 def deserialize(data): 188 text = data['text'] 189 row = data['row'] 190 column = data['column'] 191 orientation = data['orientation'] 192 193 return ShipPosition(text, row, column, orientation) 194 195 196 def create_nonces(board_size): 197 nonces = [[None] * board_size for _ in range(board_size)] 198 for row in range(0, board_size): 199 for col in range(0, board_size): 200 nonces[row][col] = ''.join( 201 [random.choice(string.ascii_letters) for _ in range(0, 10)]) 202 return nonces 203 204 205 def hash_space(space, nonce): 206 m = hashlib.sha512() 207 m.update(nonce.encode()) 208 m.update(space.encode()) 209 return m.hexdigest()