import std.bigint : BigInt; import spots : OverlaySource, Spot; import std.stdio; import std.format; struct Portal { BigInt y; BigInt x; Portal* pair; this(BigInt y, BigInt x) { this.y = y; this.x = x; } void pairWith(ref Portal p) { this.pair = &p; p.pair = &this; } void buildMap(ref int[Portal* ] idMap, ref int highId) { idMap[&this] = highId++; } string toString(int[Portal* ] idMap) in { assert(pair == null || pair in idMap); } do { if (pair == null) return format!`%s, %s: -1`(y, x); else return format!`%s, %s: %d`(y, x, idMap[pair]); } void fromString(string s, Portal[int] portalsById) { import std.conv : to; import std.algorithm : until, findSkip; import std.array : array; this.y = to!BigInt(s.until(',').array); s.findSkip(", "); this.x = to!BigInt(s.until(':').array); s.findSkip(": "); int id; s.formattedRead!"%d"(id); auto p = id in portalsById; if (p) { p.pairWith(this); } } } class Portals { private Portal[int] _portals; private Portal* _lastPortal; private int _nextId; private OverlaySource _overlay; this(OverlaySource overlay) { _overlay = overlay; } int add(BigInt y, BigInt x) { _portals[_nextId] = Portal(y, x); if (_lastPortal && _lastPortal.pair == null) { _lastPortal.pairWith(_portals[_nextId]); } _lastPortal = &(_portals[_nextId]); _overlay[y, x] = Spot('/', _nextId); return _nextId++; } Portal get(int id) in { assert(id in _portals); } do { return _portals[id]; } void remove(BigInt y, BigInt x) { auto id = _overlay[y, x].id; auto pair = _portals[id].pair; if (pair) { if (_lastPortal.pair == null) { _lastPortal.pairWith(*pair); } else { pair.pair = null; } } _lastPortal = _portals[id].pair; _portals.remove(id); _overlay[y, x] = Spot(' '); } immutable string terminator = "END PORTAL DATA\n"; void save(File f) { int id = 0; int[Portal* ] idMap; foreach (ref key; _portals.byKey()) { (key in _portals).buildMap(idMap, id); } foreach (ref key; _portals.byKey()) { auto portalRef = key in _portals; f.writefln!`%d @ %s`(idMap[portalRef], portalRef.toString(idMap)); } f.write(terminator); if (_lastPortal) f.writefln!`%d`(idMap[_lastPortal]); } void load(File f) { _nextId = 0; foreach (char[] line; f.lines) { if (line == terminator) break; int id; string portalText; line.formattedRead!`%d @ %s`(id, portalText); _portals[id] = Portal(); _portals[id].fromString(portalText, _portals); _overlay[_portals[id].y, _portals[id].x] = Spot('/', id); _nextId++; // works if the file is guaranteed to use // linearly incrementing id's } int id; if (_nextId > 0) { f.readf("%d\n", id); _lastPortal = &(_portals[id]); } } unittest { import spots : RandomSource; import std.stdio; auto overlay = new OverlaySource(new RandomSource()); auto portals = new Portals(overlay); portals.add(BigInt(0), BigInt(0)); portals.remove(BigInt(0), BigInt(0)); int orig = portals.add(BigInt(0), BigInt(0)); int sec = portals.add(BigInt(0), BigInt(1)); assert(*(portals.get(orig).pair) == portals.get(sec)); portals.remove(BigInt(0), BigInt(1)); sec = portals.add(BigInt(0), BigInt(2)); assert(*(portals.get(orig).pair) == portals.get(sec)); orig = portals.add(BigInt(0), BigInt(3)); portals.remove(BigInt(0), BigInt(0)); assert(*(portals.get(orig).pair) == portals.get(sec)); } }