"""Print per-GUID analysis of the foreground Tilemap to help identify TileIds. For each tile asset GUID, prints: - count - bounding box (min/max x, y in Unity cell coords) - 5 sample positions - top neighbor distribution: which GUID most often sits directly ABOVE this one - bottom neighbor distribution: which GUID most often sits directly BELOW Run: python tools/analyze_foreground.py """ from __future__ import annotations import os import sys from collections import Counter, defaultdict from typing import Dict, Tuple sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import unity_to_level as u2l def short(guid: str) -> str: return guid[:8] def main() -> int: with open(u2l.SCENE_PATH, "r", encoding="utf-8") as f: text = f.read() docs = u2l.split_documents(text) go_names = u2l.parse_gameobjects(docs) tms = u2l.parse_tilemaps(docs, go_names) fg = tms.get("foreground") if not fg: print("no foreground tilemap") return 1 # Build a (x, y) -> guid map; drop duplicates conservatively. cell_guid: Dict[Tuple[int, int], str] = {} for x, y, idx in fg.cells: if 0 <= idx < len(fg.asset_guids): cell_guid[(x, y)] = fg.asset_guids[idx] # Group cells by guid. by_guid: Dict[str, list] = defaultdict(list) for (x, y), guid in cell_guid.items(): by_guid[guid].append((x, y)) # Order by count desc. ordered = sorted(by_guid.items(), key=lambda kv: -len(kv[1])) print(f"foreground tile assets: {len(fg.asset_guids)}, total cells: {len(cell_guid)}") print() for guid, cells in ordered: xs = [c[0] for c in cells] ys = [c[1] for c in cells] # Neighbors: in Unity Y is up, so "above" = y+1, "below" = y-1. above_counter: Counter = Counter() below_counter: Counter = Counter() left_counter: Counter = Counter() for x, y in cells: up = cell_guid.get((x, y + 1)) dn = cell_guid.get((x, y - 1)) lf = cell_guid.get((x - 1, y)) above_counter[up] += 1 # None means empty/sky below_counter[dn] += 1 left_counter[lf] += 1 # Sample positions: a few spread out cells. sample = sorted(cells)[:: max(1, len(cells) // 5)][:5] print(f"{short(guid)} ({guid})") print(f" count : {len(cells)}") print(f" bbox : x [{min(xs)} .. {max(xs)}] y [{min(ys)} .. {max(ys)}]") print(f" samples: {sample}") # top 3 of each direction def fmt(c: Counter) -> str: top = c.most_common(3) return ", ".join( f"{short(g) if g else ''}={n}" for g, n in top ) print(f" above (Unity y+1): {fmt(above_counter)}") print(f" below (Unity y-1): {fmt(below_counter)}") print(f" left : {fmt(left_counter)}") # If almost all rows are the same y, it's probably a horizontal-only tile. y_hist = Counter(ys) top_y = y_hist.most_common(3) print(f" y histogram (top 3): {top_y}") # Single-row or single-col flag if len(set(ys)) <= 2: print(f" >>> appears on only {len(set(ys))} distinct row(s) — likely a 'top' or row-specific tile") if len(set(xs)) <= 2: print(f" >>> appears on only {len(set(xs))} distinct column(s)") print() return 0 if __name__ == "__main__": sys.exit(main())