aboutsummaryrefslogtreecommitdiff
path: root/source/portals.d
diff options
context:
space:
mode:
Diffstat (limited to 'source/portals.d')
-rw-r--r--source/portals.d152
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));
+ }
+}