v1.2.0: 内置地图检测并标记飞地(疏漏区块),自动扩地时优先填补这些缺口;新增 analyze_holes.py 分析工具
这个提交包含在:
+126
@@ -0,0 +1,126 @@
|
||||
import json, math, urllib.request, sys
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
URL = "http://bgjq.simpfun.cn/tiles/minecraft_overworld/markers.json"
|
||||
data = urllib.request.urlopen(
|
||||
urllib.request.Request(URL, headers={"User-Agent": "Mozilla/5.0 autostore/1.0"}),
|
||||
timeout=20).read()
|
||||
root = json.loads(data.decode("utf-8"))
|
||||
|
||||
CLAIM_PATTERN_prefix = "疆土归属:"
|
||||
|
||||
def extract_polygons(points):
|
||||
result = []
|
||||
if points is None or not isinstance(points, list):
|
||||
return result
|
||||
if len(points) == 0:
|
||||
return result
|
||||
if isinstance(points[0], dict) and "x" in points[0] and "z" in points[0]:
|
||||
poly = [(p["x"], p["z"]) for p in points]
|
||||
if len(poly) >= 3:
|
||||
result.append(poly)
|
||||
else:
|
||||
for el in points:
|
||||
result.extend(extract_polygons(el))
|
||||
return result
|
||||
|
||||
def point_in_polygon(x, z, poly):
|
||||
inside = False
|
||||
j = len(poly) - 1
|
||||
for i in range(len(poly)):
|
||||
xi, zi = poly[i]
|
||||
xj, zj = poly[j]
|
||||
if (zi > z) != (zj > z) and x < (xj - xi) * (z - zi) / (zj - zi) + xi:
|
||||
inside = not inside
|
||||
j = i
|
||||
return inside
|
||||
|
||||
def fill_polygon(out, poly):
|
||||
minx = min(math.floor(p[0]/16) for p in poly)
|
||||
maxx = max(math.floor(p[0]/16) for p in poly)
|
||||
minz = min(math.floor(p[1]/16) for p in poly)
|
||||
maxz = max(math.floor(p[1]/16) for p in poly)
|
||||
area = (maxx - minx + 1) * (maxz - minz + 1)
|
||||
if area > 6_000_000:
|
||||
return
|
||||
for cx in range(minx, maxx+1):
|
||||
for cz in range(minz, maxz+1):
|
||||
if point_in_polygon(cx*16+8, cz*16+8, poly):
|
||||
out.add((cx, cz))
|
||||
|
||||
# Polygon count per marker
|
||||
poly_counts = {}
|
||||
for layer in root:
|
||||
if layer.get("id") != "union_claims":
|
||||
continue
|
||||
for marker in layer.get("markers", []):
|
||||
popup = marker.get("popup", "")
|
||||
idx = popup.find(CLAIM_PATTERN_prefix)
|
||||
if idx < 0:
|
||||
continue
|
||||
rest = popup[idx+len(CLAIM_PATTERN_prefix):].lstrip()
|
||||
name = ""
|
||||
for ch in rest:
|
||||
if ch in "<\r\n":
|
||||
break
|
||||
name += ch
|
||||
name = name.strip()
|
||||
if not name:
|
||||
continue
|
||||
polys = extract_polygons(marker.get("points"))
|
||||
poly_counts.setdefault(name, 0)
|
||||
poly_counts[name] += len(polys)
|
||||
|
||||
print("=== markers with multiple polygons (potential holes as inner rings) ===")
|
||||
for name, cnt in poly_counts.items():
|
||||
if cnt > 1:
|
||||
print(f" {name}: {cnt} polygons")
|
||||
|
||||
# Also report how many total markers
|
||||
print(f"\nTotal markers: {sum(poly_counts.values())}, distinct countries: {len(poly_counts)}")
|
||||
|
||||
claims = {}
|
||||
for layer in root:
|
||||
if layer.get("id") != "union_claims":
|
||||
continue
|
||||
for marker in layer.get("markers", []):
|
||||
popup = marker.get("popup", "")
|
||||
idx = popup.find(CLAIM_PATTERN_prefix)
|
||||
if idx < 0:
|
||||
continue
|
||||
rest = popup[idx+len(CLAIM_PATTERN_prefix):].lstrip()
|
||||
name = ""
|
||||
for ch in rest:
|
||||
if ch in "<\r\n":
|
||||
break
|
||||
name += ch
|
||||
name = name.strip()
|
||||
if not name:
|
||||
continue
|
||||
s = claims.setdefault(name, set())
|
||||
for poly in extract_polygons(marker.get("points")):
|
||||
fill_polygon(s, poly)
|
||||
|
||||
print(f"Total countries parsed: {len(claims)}")
|
||||
print(f"Total claimed chunks: {sum(len(v) for v in claims.values())}")
|
||||
|
||||
print("\n=== Flying chunks (holes) per country ===")
|
||||
total_holes = 0
|
||||
for name, chunks in claims.items():
|
||||
if not chunks:
|
||||
continue
|
||||
xs = [c[0] for c in chunks]
|
||||
zs = [c[1] for c in chunks]
|
||||
minx, maxx = min(xs), max(xs)
|
||||
minz, maxz = min(zs), max(zs)
|
||||
holes = []
|
||||
for cx in range(minx, maxx+1):
|
||||
for cz in range(minz, maxz+1):
|
||||
if (cx, cz) in chunks:
|
||||
continue
|
||||
if all(((cx+dx, cz+dz) in chunks) for dx, dz in [(1,0),(-1,0),(0,1),(0,-1)]):
|
||||
holes.append((cx, cz))
|
||||
if holes:
|
||||
total_holes += len(holes)
|
||||
print(f" {name}: {len(holes)} holes, e.g. {holes[:8]}")
|
||||
print(f"\n=== TOTAL flying chunks: {total_holes} ===")
|
||||
在新工单中引用
屏蔽一个用户