diff options
Diffstat (limited to 'source/portals.d')
-rw-r--r-- | source/portals.d | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/source/portals.d b/source/portals.d new file mode 100644 index 0000000..7bb5dc2 --- /dev/null +++ b/source/portals.d @@ -0,0 +1,152 @@ +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 in idMap); + } + do { + 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); // this is a REALLY big bug. Portal() is + // being copied by value into the dict, + // so it's not using its final ref. + } + + } +} + +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); + _nextId++; + } + 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)); + } +} |