def test_hash(self): stone = Block(blockstate="minecraft:stone") water = Block(blockstate="minecraft:water[level=1]") granite = Block(blockstate="minecraft:granite") dirt = Block(blockstate="minecraft:dirt") conglomerate_1 = stone + water + dirt conglomerate_2 = stone + dirt + water self.assertNotEqual(conglomerate_1, conglomerate_2) self.assertNotEqual(hash(conglomerate_1), hash(conglomerate_2)) conglomerate_3 = dirt + water + dirt conglomerate_4 = dirt + dirt + water self.assertNotEqual(conglomerate_3, conglomerate_4) self.assertNotEqual(hash(conglomerate_3), hash(conglomerate_4)) conglomerate_5 = conglomerate_1 + granite conglomerate_6 = conglomerate_3 + granite self.assertNotEqual(conglomerate_1, conglomerate_5) self.assertNotEqual(conglomerate_3, conglomerate_6) self.assertNotEqual(conglomerate_5, conglomerate_6) self.assertNotEqual(hash(conglomerate_5), hash(conglomerate_6))
def test_get_from_blockstate(self): # This is mostly just sanity checks air = Block(blockstate="minecraft:air") self.assertIsInstance(air, Block) self.assertEqual("minecraft", air.namespace) self.assertEqual("air", air.base_name) self.assertEqual({}, air.properties) self.assertEqual((), air.extra_blocks) self.assertEqual("minecraft:air", air.blockstate) stone = Block(blockstate="minecraft:stone") self.assertIsInstance(stone, Block) self.assertEqual("minecraft", stone.namespace) self.assertEqual("stone", stone.base_name) self.assertEqual({}, stone.properties) self.assertEqual((), stone.extra_blocks) self.assertEqual("minecraft:stone", stone.blockstate) self.assertNotEqual(air, stone) oak_leaves = Block( blockstate="minecraft:oak_leaves[distance=1,persistent=true]") self.assertIsInstance(oak_leaves, Block) self.assertEqual("minecraft", oak_leaves.namespace) self.assertEqual("oak_leaves", oak_leaves.base_name) self.assertEqual({ "distance": "1", "persistent": "true" }, oak_leaves.properties) self.assertEqual((), oak_leaves.extra_blocks) self.assertEqual("minecraft:oak_leaves[distance=1,persistent=true]", oak_leaves.blockstate)
def get_blockstate(self, blockstate: str) -> Block: """ Converts a version-specific blockstate string into a :class:`api.blocks.Block` object by parsing the blockstate and handling any addition logic that needs to be done (IE: Adding an extra block when `waterlogged=true` for Java edition). This method is replaced at runtime with the version specific handler. :param blockstate: The blockstate string to parse/convert :return: The resulting Block object """ namespace, base_name, properties = Block.parse_blockstate_string(blockstate) return Block(namespace=namespace, base_name=base_name, properties=properties)
def test_extra_blocks_immutable(self): stone = Block(blockstate="minecraft:stone") dirt = Block(blockstate="minecraft:dirt") stone2 = stone self.assertIs(stone, stone2) stone2 += dirt self.assertIsNot(stone, stone2) stone3 = stone2 self.assertIs(stone2, stone3) stone3 -= dirt self.assertIsNot(stone, stone3)
def setUp(self): self.manager = BlockManager() initial_dirt = Block(blockstate="minecraft:dirt") initial_stone = Block(blockstate="minecraft:stone") initial_granite = Block(blockstate="minecraft:granite") initial_dirt_water = initial_dirt + Block(blockstate="minecraft:water") # Partially populate the manager self.manager.add_block(initial_dirt) self.manager.add_block(initial_stone) self.manager.add_block(initial_granite) self.manager.add_block(initial_dirt_water)
def parse_blockstate(blockstate: str) -> Block: namespace, base_name, properties = Block.parse_blockstate_string(blockstate) if properties.pop("waterlogged", "false").lower() == "true": block = Block( namespace=namespace, base_name=base_name, properties=properties, extra_blocks=(_WATER_CONSTANT,), ) else: block = Block(namespace=namespace, base_name=base_name, properties=properties) return block
def test_get_block_from_manager(self): dirt = Block(blockstate="minecraft:dirt") stone = Block(blockstate="minecraft:stone") granite = Block(blockstate="minecraft:granite") water = Block(blockstate="minecraft:water") dirt_water = dirt + water self.assertEqual(dirt, self.manager[0]) self.assertEqual(stone, self.manager[1]) self.assertEqual(granite, self.manager[2]) self.assertEqual(dirt_water, self.manager[3]) with self.assertRaises(KeyError): brain_coral = Block(blockstate="minecraft:brain_coral") internal_id = self.manager[brain_coral]
def test_extra_blocks(self): stone = Block(blockstate="minecraft:stone") water = Block(blockstate="minecraft:water[level=1]") granite = Block(blockstate="minecraft:granite") dirt = Block(blockstate="minecraft:dirt") conglomerate_1 = stone + water + dirt self.assertIsInstance(conglomerate_1, Block) self.assertEqual("minecraft", conglomerate_1.namespace) self.assertEqual("stone", conglomerate_1.base_name) self.assertEqual({}, conglomerate_1.properties) self.assertEqual(2, len(conglomerate_1.extra_blocks)) for block_1, block_2 in zip(conglomerate_1.extra_blocks, (water, dirt)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) conglomerate_2 = conglomerate_1 + granite self.assertIsInstance(conglomerate_2, Block) self.assertEqual("minecraft", conglomerate_2.namespace) self.assertEqual("stone", conglomerate_2.base_name) self.assertEqual({}, conglomerate_2.properties) self.assertEqual(3, len(conglomerate_2.extra_blocks)) for block_1, block_2 in zip(conglomerate_2.extra_blocks, (water, dirt, granite)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertNotEqual(conglomerate_1, conglomerate_2) conglomerate_3 = conglomerate_2 - dirt self.assertIsInstance(conglomerate_3, Block) self.assertEqual("minecraft", conglomerate_3.namespace) self.assertEqual("stone", conglomerate_3.base_name) self.assertEqual({}, conglomerate_3.properties) self.assertEqual(2, len(conglomerate_3.extra_blocks)) for block_1, block_2 in zip(conglomerate_3.extra_blocks, (water, granite)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertRaises(TypeError, lambda: stone + 1) self.assertRaises(TypeError, lambda: stone - 1)
def test_get_index_from_manager(self): dirt = Block(blockstate="minecraft:dirt") stone = Block(blockstate="minecraft:stone") granite = Block(blockstate="minecraft:granite") self.assertEqual(0, self.manager[dirt]) self.assertEqual(1, self.manager[stone]) self.assertEqual(2, self.manager[granite]) water = Block(blockstate="minecraft:water") dirt_water = dirt + water self.assertNotEqual(dirt, dirt_water) self.assertIsNot(dirt, dirt_water) self.assertEqual(3, self.manager[dirt_water]) with self.assertRaises(KeyError): random_block = self.manager[10000]
def make_children(self, children, piece, held, next_can_hold, put_hold): maximum = float('-inf') ind = -1 for i in range(len(children)): # unpack that child x, y, rot, moveset = children[i] # create new board and execute moves new_board = self.board.get_copy() new_piece = Block(piece) new_piece.execute_moves(moveset, self.board) new_piece.set(new_board) # calculate points new_lines = self.lines_sent combo_id = -1 new_combos_so_far = self.combos_so_far + 1 lines_cleared = new_board.clear_lines() if lines_cleared == 0: new_combos_so_far = 0 else: is_tspin = tspin_detect(moveset) is_perf_clear = perf_clear_detect(new_board) combo_id = 1 if is_tspin else 0 if is_perf_clear else -1 if is_tspin and is_perf_clear: combo_id = 2 new_lines += next_points( self.combos_so_far - 1, lines_cleared if lines_cleared != 4 else 0, lines_cleared == 4, is_tspin, is_perf_clear, combo_id == self.last_combo_id) # create the new node new_node = Tree_node(new_board, held, new_lines, new_combos_so_far, combo_id, next_can_hold) self.add_child( ([encode_move('hold')] if put_hold else []) + moveset, new_node) ranking = new_node.get_rank() if ranking > maximum: maximum = ranking ind = i return (maximum, ind)
def test_fill_operation(self): with timeout(self, 0.25, show_completion_time=True): subbox_1 = SubBox((1, 70, 3), (5, 71, 5)) box = SelectionBox((subbox_1,)) # Start sanity check self.assertEqual( "minecraft:stone", self.world.get_block(1, 70, 3).blockstate ) self.assertEqual( "minecraft:granite", self.world.get_block(1, 70, 5).blockstate ) # End sanity check self.world.run_operation_from_operation_name( "fill", box, Block("minecraft:stone") ) for x, y, z in box: self.assertEqual( "minecraft:stone", self.world.get_block(x, y, z).blockstate, f"Failed at coordinate ({x},{y},{z})", ) self.world.undo() self.assertEqual( "minecraft:stone", self.world.get_block(1, 70, 3).blockstate ) self.assertEqual( "minecraft:granite", self.world.get_block(1, 70, 5).blockstate ) self.world.redo() for x, y, z in box: self.assertEqual( "minecraft:stone", self.world.get_block(x, y, z).blockstate, f"Failed at coordinate ({x},{y},{z})", )
def test_fill_operation(self): subbox_1 = SubBox((1, 70, 3), (5, 71, 5)) box = SelectionBox((subbox_1, )) self.world.run_operation_from_operation_name( "fill", box, Block("minecraft:stone")) for x, y, z in box: self.assertEqual( "minecraft:stone", self.world.get_block(x, y, z).blockstate, f"Failed at coordinate ({x},{y},{z})", ) self.world.undo() self.assertEqual("minecraft:stone", self.world.get_block(1, 70, 3).blockstate) self.assertEqual("minecraft:granite", self.world.get_block(1, 70, 5).blockstate)
def make_nodes(self, children, piece_type, held_piece, q, is_held): for child in children: x, y, rot, moveset = child new_board = self.board.get_copy() new_piece = Block(piece_type) new_piece.execute_moves(moveset, self.board) new_piece.set(new_board) new_lines = self.lines_sent combo_id = -1 new_combos_so_far = self.combos_so_far + 1 lines_cleared = new_board.clear_lines() if lines_cleared == 0: new_combos_so_far = 0 else: is_tspin = tspin_detect(moveset) is_perf_clear = perf_clear_detect(new_board) combo_id = 1 if is_tspin else 0 if is_perf_clear else -1 if is_tspin and is_perf_clear: combo_id = 2 new_lines += next_points(self.combos_so_far - 1, lines_cleared if lines_cleared != 4 else 0, lines_cleared == 4, is_tspin, is_perf_clear, combo_id == self.last_combo_id) nq = [] if len(q) < 2 else deepcopy(q[1:]) new_node = Tree_node(new_board, -1 if len(q) == 0 else q[0], held_piece, nq, new_lines, new_combos_so_far, combo_id, self.ranker, is_held) self.add_child(([encode_move('hold')] if is_held else []) + moveset, new_node)
def generate_successor_states(board, piece_type): # number of unique rotations per piece r = valid_rotations(piece_type) # store all possible positions in a queue pos = Queue() # 3D memo for later ;) memo = [[[0 for z in range(r)] for y in range(board.get_height())] for x in range(board.get_width())] # for each unique rotation for rotation in range(r): # construct a temporary piece temp_piece = Block(piece_type) temp_piece.set_rotation(board, rotation) # get the next offset after rotating sx = temp_piece.get_offset()[0] # for each horizontal position for x in range(board.get_width() - temp_piece.get_width() + 1): # shift the piece horizontally temp_piece.set_offset(x, 0) if temp_piece.collides(board): continue # drop temp_piece.drop(board) # get final position tx, ty = temp_piece.get_offset() # memoize memo[tx][ty][rotation] = 1 #print(str(tx) + ", " + str(ty) + ", " + str(rotation)) # encode moves moves = [encode_move('crot') for i in range(rotation)] + [encode_move('left') if x - sx < 0 else encode_move('right') for i in range(abs(x-sx))] + [encode_move('drop')] # enqueue pos.put((tx, ty, rotation, moves)) # the final set to return children = [] # while the queue still contains positions while not pos.empty(): child = pos.get() # add to final bag children.append(child) x, y, rot, moves = child # make a block and put it into the correct place test_piece = Block(piece_type) test_piece.execute_moves(moves, board) o_off_x, o_off_y = test_piece.get_offset() # generate partial movements from this position, i.e. left, right, and all rotations # stored in tuples like so (dx, dy, nr) next_positions = [(1, 0, rot), (-1, 0, rot)] + [(0,0,i) for i in range(r)] # for each partial movement for npos in next_positions: # quick access variables dx, dy, nr = npos # rotate the piece for the new rotation, if possibe, else its invalid so skip if not test_piece.set_rotation(board, nr): continue offset = test_piece.get_offset() # translate the piece right or left or skip if invalid if (dx > 0 and not test_piece.r_translate(board)) or (dx < 0 and not test_piece.l_translate(board)): continue # apply gravity down = test_piece.drop(board) # get updated locations nx, ny = test_piece.get_offset() # check that the move was not already encoded if memo[nx][ny][nr] == 1: test_piece.dirty_reset_position(o_off_x, o_off_y, rot) continue # now encode additional movements # copy moves and convert drops to down movements because this is more meticulous nmoves = deepcopy(moves) # convert drops to down moves l = len(moves) - 1 if moves[l] == encode_move('drop'): nmoves = nmoves[:l] + [encode_move('down') for i in range(y)] # generate additional horizontal movements if dx != 0: nmoves.append(encode_move('left') if dx == -1 else encode_move('right')) # generate rotation movements dr = nr - rot #print("rotations:",dr) if rot == 3 and nr == 0: nmoves += [encode_move('crot')] elif dr != 0: nmoves += [encode_move('crot') if dr > 0 else encode_move('ccrot') for i in range(abs(dr))] # generate additional down movements nmoves += [encode_move('down') for i in range(down)] # enqueue pos.put((nx, ny, nr, nmoves)) # mark this new space as visited, too memo[nx][ny][nr] = 1 # undo moves test_piece.dirty_reset_position(o_off_x, o_off_y, rot) return children
def get_blocks(self, cx: int, cz: int) -> Union[numpy.ndarray, NotImplementedError]: chunk_sections, _, _ = self._region_manager.load_chunk(cx, cz) if len(chunk_sections) == 0: return NotImplementedError( "We don't support reading chunks that never been edited in Minecraft before" ) blocks = numpy.zeros((256, 16, 16), dtype=int) block_data = numpy.zeros((256, 16, 16), dtype=numpy.uint8) for section in chunk_sections: lower = section["Y"].value << 4 upper = (section["Y"].value + 1) << 4 section_blocks = numpy.frombuffer(section["Blocks"].value, dtype=numpy.uint8) section_data = numpy.frombuffer(section["Data"].value, dtype=numpy.uint8) section_blocks = section_blocks.reshape((16, 16, 16)) section_blocks.astype(numpy.uint16, copy=False) section_data = section_data.reshape( (16, 16, 8) ) # The Byte array is actually just Nibbles, so the size is off section_data = world_utils.from_nibble_array(section_data) if "Add" in section: add_blocks = numpy.frombuffer(section["Add"].value, dtype=numpy.uint8) add_blocks = add_blocks.reshape((16, 16, 8)) add_blocks = world_utils.from_nibble_array(add_blocks) section_blocks |= add_blocks.astype(numpy.uint16) << 8 blocks[lower:upper, :, :] = section_blocks block_data[lower:upper, :, :] = section_data blocks = numpy.swapaxes(blocks.swapaxes(0, 1), 0, 2) block_data_array = numpy.swapaxes(block_data.swapaxes(0, 1), 0, 2) unique_block_ids = numpy.unique( blocks ) # Flatten the 3D array into 1D and remove all duplicate entries # unique_block_ids = unique_block_ids[ # unique_block_ids != 0 # ] # Remove all air entries unique_blocks = set() for (block_id) in ( unique_block_ids ): # Find all instances of the base ID and find any occurrences of data values indices = numpy.where(blocks == block_id) for block_data in numpy.unique(block_data_array[indices]): unique_blocks.add((block_id, block_data)) block_test = numpy.zeros_like(blocks, dtype=int) for block in unique_blocks: internal = self._materials.get_block_from_definition( block, default="minecraft:unknown_{}") block_object: Block = self.get_blockstate(internal) if ( internal == "minecraft:unknown_{}" ): # If we don't have the block in our definitions, call it an unknown block try: internal_id = list( self.unknown_blocks.values()).index(block) except ValueError: block_object: Block = Block( blockstate= f"minecraft:unknown_{len(self.unknown_blocks)}") internal_id = self.block_manager.add_block(block_object) self.unknown_blocks[internal_id] = block else: internal_id = self.block_manager.add_block( block_object ) # Find the index of the block in mapping_handler block_mask = blocks == block[0] data_mask = block_data_array == block[1] mask = ( block_mask & data_mask ) # Combine the mask from the base ID array and the data value array block_test[ mask] = internal_id # Mask all occurrences and set them to the internal ID block_test = block_test.astype(f"uint{get_smallest_dtype(block_test)}" ) # Shrink the array's dtype as needed return block_test
leveldat_root = load_leveldat(directory) if "FML" in leveldat_root: return False # 1444 is the version for 17w43a snapshot (first 1.13 snapshot) # 1519 is the version for the 1.13 release version # 1628 is the version for the 1.13.1 release version # if not check_version_leveldat(leveldat_root, _min=1444, _max=1628): if not check_version_leveldat(leveldat_root, _min=1444): return False return True _WATER_CONSTANT = Block(blockstate="minecraft:water") def parse_blockstate(blockstate: str) -> Block: namespace, base_name, properties = Block.parse_blockstate_string( blockstate) if properties.pop("waterlogged", "false").lower() == "true": block = Block( namespace=namespace, base_name=base_name, properties=properties, extra_blocks=(_WATER_CONSTANT, ), ) else: block = Block(namespace=namespace,
def render(self): matrix = self.matrix w = self.width h = self.height #draw held piece for x in range(w): for y in range(1, int(h / 2)): pygame.draw.rect( self.screen, (200, 200, 200), pygame.Rect((TOTAL_PIECE_WIDTH) * x, (TOTAL_PIECE_HEIGHT) * y, PIECE_W, PIECE_H)) #locate held_piece held_piece = self.board.get_held() if held_piece != None: color = block_color(held_piece) piece = Block(held_piece) hx = piece.get_spawn(w) hy = int(h / 4) for x in range(piece.get_width()): for y in range(piece.get_height()): if piece.loc_mat[x][y] == 1: pygame.draw.rect( self.screen, color, pygame.Rect((TOTAL_PIECE_WIDTH) * (hx + x), (TOTAL_PIECE_HEIGHT) * (hy + y), PIECE_W, PIECE_H)) #draw game board cp = self.board.get_current() for x in range(w): for y in range(1, h): piece = matrix.lookup(x, y) if cp.intersects(x, y): color = cp.get_color() else: color = block_color(piece) if piece != 0 else GRAY pygame.draw.rect( self.screen, color, pygame.Rect((TOTAL_PIECE_WIDTH) * (w + x), (TOTAL_PIECE_HEIGHT) * y, PIECE_W, PIECE_H)) #draw next pieces for x in range(2 * w, 3 * w): for y in range(1, h): pygame.draw.rect( self.screen, (200, 200, 200), pygame.Rect((TOTAL_PIECE_WIDTH) * x, (TOTAL_PIECE_HEIGHT) * y, PIECE_W, PIECE_H)) q = list(self.board.next_queue) ny = 2 for next_piece in q[:5]: color = block_color(next_piece + 1) piece = Block(next_piece) nx = piece.get_spawn(w) for x in range(piece.get_width()): for y in range(piece.get_height()): if piece.loc_mat[x][y] == 1: pygame.draw.rect( self.screen, color, pygame.Rect((TOTAL_PIECE_WIDTH) * (2 * w + nx + x), (TOTAL_PIECE_HEIGHT) * (ny + y), PIECE_W, PIECE_H)) ny += 4
def test_remove_layer(self): stone = Block(blockstate="minecraft:stone") water = Block(blockstate="minecraft:water[level=1]") granite = Block(blockstate="minecraft:granite") dirt = Block(blockstate="minecraft:dirt") oak_log_axis_x = Block(blockstate="minecraft:oak_log[axis=x]") conglomerate_1 = stone + water + dirt + dirt + granite self.assertIsInstance(conglomerate_1, Block) self.assertEqual("minecraft", conglomerate_1.namespace) self.assertEqual("stone", conglomerate_1.base_name) self.assertEqual({}, conglomerate_1.properties) self.assertEqual(4, len(conglomerate_1.extra_blocks)) for block_1, block_2 in zip(conglomerate_1.extra_blocks, (water, dirt, dirt, granite)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_conglomerate = conglomerate_1.remove_layer(2) for block_1, block_2 in zip(new_conglomerate.extra_blocks, (water, dirt, granite)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) conglomerate_2 = granite + water + stone + dirt + oak_log_axis_x self.assertIsInstance(conglomerate_2, Block) self.assertEqual("minecraft", conglomerate_2.namespace) self.assertEqual("granite", conglomerate_2.base_name) self.assertEqual({}, conglomerate_2.properties) self.assertEqual(4, len(conglomerate_2.extra_blocks)) for block_1, block_2 in zip(conglomerate_2.extra_blocks, (water, stone, dirt, oak_log_axis_x)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_conglomerate = conglomerate_2.remove_layer(3) self.assertEqual(3, len(new_conglomerate.extra_blocks)) for block_1, block_2 in zip(new_conglomerate.extra_blocks, (water, stone, oak_log_axis_x)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_base = conglomerate_2.remove_layer(0) self.assertEqual(3, len(new_base.extra_blocks)) self.assertEqual("minecraft", new_base.namespace) self.assertEqual("water", new_base.base_name) self.assertEqual({"level": "1"}, new_base.properties) for block_1, block_2 in zip(new_base.extra_blocks, (stone, dirt, oak_log_axis_x)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertNotEqual(new_base, new_base.remove_layer(1)) with self.assertRaises(InvalidBlockException): no_block = granite.remove_layer(0) with self.assertRaises(InvalidBlockException): non_present_layer = granite.remove_layer(7) non_present_layer = conglomerate_2.remove_layer(5) conglomerate_2.remove_layer( 4) # Check if last layer can still be removed