#!/usr/bin/env python3 """Read-only trapdoor panel/contact checks for actual or overlaid block readers. Pass a ``state_reader(x, y, z)`` returning canonical block data. Each fixture is ``{'pos': [x,y,z], 'support': [x,y,z]}`` for an adjacent support cell, or uses ``'edge': 'south'`` for an intended edge within the trapdoor's own cell. Support materials and partial support shapes must be validated by the caller; this check measures contact with that cell's face and rejects an empty cell. Shape source: pinned Paper 26.2 TrapDoorBlock.getShape / static initializer, Block.boxZ(16,13,16), Shapes.rotateAll. An open NORTH trapdoor occupies the SOUTH 3/16 strip. HALF affects closed panels only. No world/file/network writes. >>> world = {(0, 0, 0): 'minecraft:spruce_trapdoor[facing=east,half=bottom,open=true]', (1, 0, 0): 'minecraft:spruce_planks'} >>> read = lambda x, y, z: world.get((x,y,z), 'minecraft:air') >>> check_contact(read, (0,0,0), support=(1,0,0))['gap_blocks'] 0.8125 >>> world[(0,0,0)] = 'minecraft:spruce_trapdoor[open=true,facing=west,half=top]' >>> check_contact(read, (0,0,0), support=(1,0,0))['passed'] True >>> check_contact(read, (0,0,0), edge='north')['passed'] False >>> world[(0,0,0)] = 'minecraft:iron_trapdoor[open=false,facing=south,half=top]' >>> check_contact(read, (0,0,0), edge='up')['passed'] True """ NORMALS = {'west': (-1, 0, 0), 'east': (1, 0, 0), 'down': (0, -1, 0), 'up': (0, 1, 0), 'north': (0, 0, -1), 'south': (0, 0, 1)} THICKNESS = 3 / 16 AIR = {'minecraft:air', 'minecraft:cave_air', 'minecraft:void_air'} def panel_bounds(state): """Return local AABB minimum/maximum and its thickness axis (X=0,Y=1,Z=2).""" name, separator, raw = state.partition('[') if not name.startswith('minecraft:') or not name.endswith('_trapdoor') or not separator or not raw.endswith(']'): raise ValueError('Expected explicit trapdoor block data') values = dict(part.split('=', 1) for part in raw[:-1].split(',')) if values.get('open') == 'true': # Bounds are in block units; the panel sits opposite its facing. strips = {'north': (2, 1 - THICKNESS, 1), 'south': (2, 0, THICKNESS), 'east': (0, 0, THICKNESS), 'west': (0, 1 - THICKNESS, 1)} if values.get('facing') not in strips: raise ValueError('Open trapdoor needs a horizontal facing') axis, start, end = strips[values['facing']] elif values.get('open') == 'false' and values.get('half') in {'top', 'bottom'}: axis = 1 start, end = (1 - THICKNESS, 1) if values['half'] == 'top' else (0, THICKNESS) else: raise ValueError('Trapdoor needs explicit open and valid half properties') minimum, maximum = [0., 0., 0.], [1., 1., 1.] minimum[axis], maximum[axis] = start, end return minimum, maximum, axis def _position(pos): result = tuple(pos) if len(result) != 3 or any(type(value) is not int for value in result): raise ValueError('Position must contain three integer block coordinates') return result def check_contact(state_reader, pos, *, support=None, edge=None): """Measure a panel's gap/alignment to a requested edge or adjacent cell face. ``passed`` requires the broad panel face to be parallel and flush. Merely touching with a thin side edge does not pass. A support fixture also requires a nonair observed support; this is not a full support collision-shape audit. """ pos = _position(pos) if (support is None) == (edge is None): raise ValueError('Specify exactly one adjacent support or intended edge') support_state = None if support is not None: support = _position(support) delta = tuple(target - origin for origin, target in zip(pos, support)) edge = next((name for name, normal in NORMALS.items() if normal == delta), None) if edge is None: raise ValueError('Support must share one face with the trapdoor cell') support_state = state_reader(*support) if edge not in NORMALS: raise ValueError('Unknown intended edge') state = state_reader(*pos) minimum, maximum, thickness_axis = panel_bounds(state) normal = NORMALS[edge] axis = next(i for i, value in enumerate(normal) if value) gap = minimum[axis] if normal[axis] < 0 else 1 - maximum[axis] parallel = axis == thickness_axis support_present = support_state is None or support_state.partition('[')[0] not in AIR result = {'pos': list(pos), 'state': state, 'edge': edge, 'panel_bounds': {'min': minimum, 'max': maximum}, 'panel_parallel_to_face': parallel, 'gap_blocks': gap, 'passed': parallel and gap == 0 and support_present} if support is not None: result.update(support=list(support), support_state=support_state, support_present=support_present) return result def audit_contacts(state_reader, fixtures): """Return a compact summary and per-fixture geometric evidence.""" checks = [] for fixture in fixtures: result = check_contact(state_reader, fixture['pos'], support=fixture.get('support'), edge=fixture.get('edge')) if 'name' in fixture: result['name'] = fixture['name'] checks.append(result) failures = [check for check in checks if not check['passed']] return {'passed': not failures, 'checked': len(checks), 'failed': len(failures), 'max_gap_blocks': max((check['gap_blocks'] for check in checks), default=0), 'checks': checks} if __name__ == '__main__': import doctest result = doctest.testmod() raise SystemExit(bool(result.failed))