#!/usr/bin/env python3 """Score a route's travelling-salesman heuristic on six fixed point sets: its tour's length over nearest neighbour's. Usage: score.py ROUTE_FOLDER. The route's solve.py (or the baseline's, at the top of the project, if the route has none) reads the points as JSON on stdin and prints the tour as JSON. It runs in its own process and only the tour it prints is used. Writes metrics.json; the last line sums up.""" import json import math import random import subprocess import sys import time from pathlib import Path def points(n, seed): rng = random.Random(seed) return [[round(rng.random(), 6), round(rng.random(), 6)] for _ in range(n)] SETS = {f"n{n}-{s}": points(n, s) for n, s in [(40, 1), (40, 2), (80, 3), (80, 4), (160, 5), (160, 6)]} def length(p, tour): return sum(math.dist(p[tour[i - 1]], p[tour[i]]) for i in range(len(tour))) def nearest(p): left, tour = set(range(1, len(p))), [0] while left: nxt = min(left, key=lambda j: math.dist(p[tour[-1]], p[j])) left.remove(nxt) tour.append(nxt) return tour folder = Path(sys.argv[1]) solver = folder / "solve.py" if (folder / "solve.py").is_file() else Path("input/solve.py") subtasks, seconds = {}, 0.0 for name, p in SETS.items(): start = time.perf_counter() try: out = subprocess.run([sys.executable, str(solver)], input=json.dumps(p), capture_output=True, text=True, timeout=30) tour = json.loads(out.stdout) ok = isinstance(tour, list) and sorted(tour) == list(range(len(p))) except Exception as e: print(f"{name}: the solver failed ({type(e).__name__})") sys.exit(1) seconds += time.perf_counter() - start if not ok: print(f"{name}: the tour must visit every point exactly once") sys.exit(1) subtasks[name] = round(length(p, tour) / length(p, nearest(p)), 4) if seconds > 30: print(f"took {seconds:.1f} s; the limit is 30 s") sys.exit(1) score = round(sum(subtasks.values()) / len(subtasks), 4) Path("metrics.json").write_text(json.dumps({"score": score, "cost": round(seconds, 2), "subtasks": subtasks})) print(f"{score} of nearest neighbour's tour length on {len(subtasks)} point sets, in {seconds:.1f} s")