| 57 | |
| 58 | |
| 59 | class HaliteReplayer(ReplayRenderer): |
| 60 | arena = "Halite" |
| 61 | sim_glob = "*.hlt" |
| 62 | DRAW_JS = DRAW_JS |
| 63 | |
| 64 | def parse(self, raw: bytes, players=None) -> ReplayData: |
| 65 | d = json.loads(raw.decode()) |
| 66 | w, h = d["width"], d["height"] |
| 67 | frames_raw = d["frames"] |
| 68 | num_players = d.get("num_players", 2) |
| 69 | engine_names = d.get("player_names", []) |
| 70 | |
| 71 | def name_for(owner: int) -> str: |
| 72 | if players and len(players) >= owner: |
| 73 | return players[owner - 1]["name"] |
| 74 | if len(engine_names) >= owner: |
| 75 | return engine_names[owner - 1] |
| 76 | return f"Player {owner}" |
| 77 | |
| 78 | colors = {o: PALETTE[(o - 1) % len(PALETTE)] for o in range(1, num_players + 1)} |
| 79 | names = {o: name_for(o) for o in range(1, num_players + 1)} |
| 80 | |
| 81 | frames = [{"turn": i, "cells": grid} for i, grid in enumerate(frames_raw)] |
| 82 | |
| 83 | # Winner = most territory in the final frame (Halite is territory control); tie -> draw. |
| 84 | tally = Counter() |
| 85 | for row in frames_raw[-1]: |
| 86 | for owner, _strength in row: |
| 87 | if owner > 0: |
| 88 | tally[owner] += 1 |
| 89 | top = tally.most_common() |
| 90 | if not top or (len(top) > 1 and top[0][1] == top[1][1]): |
| 91 | winner, draw = None, True |
| 92 | else: |
| 93 | winner, draw = name_for(top[0][0]), False |
| 94 | |
| 95 | return ReplayData( |
| 96 | w=w, |
| 97 | h=h, |
| 98 | frames=frames, |
| 99 | winner=winner, |
| 100 | draw=draw, |
| 101 | extra={"productions": d.get("productions", []), "colors": colors, "names": names}, |
| 102 | ) |