aboutsummaryrefslogtreecommitdiff
path: root/source/spots.d
diff options
context:
space:
mode:
Diffstat (limited to 'source/spots.d')
-rw-r--r--source/spots.d145
1 files changed, 145 insertions, 0 deletions
diff --git a/source/spots.d b/source/spots.d
new file mode 100644
index 0000000..0f9d8b7
--- /dev/null
+++ b/source/spots.d
@@ -0,0 +1,145 @@
+import std.bigint;
+import std.digest.crc;
+import std.random : unpredictableSeed;
+import std.range;
+import lru : LRUCache;
+import deimos.ncurses;
+
+private BigInt positive(BigInt n)
+{
+ if (n >= 0)
+ return n * 2;
+ else
+ return -n * 2 - 1;
+}
+
+private BigInt pair(BigInt n, BigInt m)
+{
+ // https://stackoverflow.com/a/919661
+ return (positive(n) + positive(m)) * (positive(n) + positive(m) + 1) / 2 + positive(m);
+}
+
+struct Spot
+{
+ private char _contents;
+ this(char c)
+ {
+ _contents = c;
+ }
+
+ @property contents()
+ {
+ return _contents;
+ }
+
+ bool opEqual(const Spot s)
+ {
+ return s._contents == _contents;
+ }
+
+ bool isRock()
+ {
+ return _contents == '*';
+ }
+}
+
+interface SpotSource
+{
+ Spot opIndex(BigInt y, BigInt x);
+}
+
+class RandomSource : SpotSource
+{
+ uint seed;
+ double density = .0025;
+ private LRUCache!(BigInt[2], Spot) cache;
+
+ this()
+ {
+ seed = unpredictableSeed;
+ cache = new LRUCache!(BigInt[2], Spot)(50000);
+ }
+
+ this(uint seed)
+ {
+ this.seed = seed;
+ }
+
+ this(uint seed, double density)
+ {
+ this.density = density;
+ this(seed);
+ }
+
+ private uint hash(BigInt n)
+ {
+ CRC32 crc;
+ crc.start();
+ crc.put((cast(ubyte*)&seed)[0 .. seed.sizeof]);
+ foreach (i; iota(n.ulongLength))
+ {
+ auto digit = n.getDigit(i);
+ crc.put((cast(ubyte*)&digit)[0 .. digit.sizeof]);
+ }
+ auto result = crc.finish();
+ auto hash = *(cast(uint*)&result);
+ return hash;
+ }
+
+ public Spot opIndex(BigInt y, BigInt x)
+ {
+ if ([y, x] in cache)
+ return cache[[y, x]];
+ auto roll = cast(double) hash(pair(y, x)) / uint.max;
+ Spot ret;
+ if (roll < density)
+ ret = Spot('*');
+ else
+ ret = Spot(' ');
+ cache[[y, x]] = ret;
+ return ret;
+ }
+}
+
+class OverlaySource : SpotSource
+{
+ private SpotSource _base;
+ private Spot[BigInt[2]] _overlay;
+
+ this(SpotSource base)
+ {
+ _base = base;
+ }
+
+ void opIndexAssign(Spot spot, BigInt y, BigInt x)
+ {
+ Spot* p = [y, x] in _overlay;
+ if (p !is null && spot == _base[y, x])
+ {
+ _overlay.remove([y, x]);
+ }
+ else
+ {
+ _overlay[[y, x]] = spot;
+ }
+ }
+
+ Spot opIndex(BigInt y, BigInt x)
+ {
+ Spot* p = [y, x] in _overlay;
+ if (p !is null)
+ return *p;
+ else
+ return _base[y, x];
+ }
+
+ unittest
+ {
+ auto base = new RandomSource(0, 0.0);
+ auto src = new OverlaySource(base);
+ src[BigInt(0), BigInt(0)] = Spot('*');
+ src[BigInt(0), BigInt(1)] = Spot('*');
+ assert(src[BigInt(0), BigInt(0)] == Spot('*'));
+ assert(src[BigInt(0), BigInt(1)] == Spot('*'));
+ }
+}