import std.bigint; import deimos.ncurses; import std.string : toStringz; import std.math : floor, ceil; import std.array : appender; import std.range; import std.format; import std.stdio; import spots : Spot, SpotSource; public class Board { private SpotSource _source; // uint seed; // indexed as Spot = Board[y, x] this(SpotSource source) { _source = source; } Spot opIndex(BigInt y, BigInt x) { return _source[y, x]; } Spot opIndex(long y, long x) { // possible per improvements return opIndex(BigInt(y), BigInt(x)); } PartialBoard opIndex(BigInt[2] y, BigInt[2] x) { return new PartialBoard(y, x, _source); } PartialBoard opIndex(long[2] y, long[2] x) { return opIndex([BigInt(y[0]), BigInt(y[1])], [ BigInt(x[0]), BigInt(x[1]) ]); } PartialBoard opIndex(BigInt[2] y, BigInt x) { return opIndex(y, [x, x + 1]); } PartialBoard opIndex(long[2] y, long x) { return opIndex([BigInt(y[0]), BigInt(y[1])], BigInt(x)); } PartialBoard opIndex(BigInt y, BigInt[2] x) { return opIndex([y, y + 1], x); } PartialBoard opIndex(long y, long[2] x) { return opIndex(BigInt(y), [BigInt(x[0]), BigInt(x[1])]); } BigInt[2] opSlice(size_t dimension)(BigInt x, BigInt y) if (dimension >= 0 && dimension < 2) { return [x, y]; } long[2] opSlice(size_t dimension)(long x, long y) if (dimension >= 0 && dimension < 2) { return [x, y]; } // be more generic. instead of long use T where IsIntegral!T } enum Edge { LEFT, RIGHT, TOP, BOT, } class BoardDisplay { private Board _board; private WINDOW* _window; private int _height, _width; private string _status; BigInt x = 0; BigInt y = 0; this(Board board, WINDOW* window) { _board = board; _window = window; getdims(); } public void getdims() { getmaxyx(_window, _height, _width); } private void center() { mvprintw(_height, 0, toStringz(_status ~ " ".replicate(_width - status.length))); move(cast(int) ceil(_height / 2.0), cast(int) ceil(_width / 2.0)); } @property string status() { return _status; } @property status(string s) { _status = s; center(); } private BigInt edge(Edge e) { // templating?? switch (e) { case Edge.LEFT: return BigInt(cast(int) floor(-_width / 2.0)) + x; case Edge.RIGHT: return BigInt(cast(int) floor(_width / 2.0)) + x; case Edge.TOP: return BigInt(cast(int) floor(-_height / 2.0)) + y; case Edge.BOT: return BigInt(cast(int) floor(_height / 2.0)) + y; default: assert(0); } } void print() { import std.stdio; import std.datetime; mvprintw(0, 0, toStringz(_board[edge(Edge.TOP) .. edge(Edge.BOT), edge(Edge.LEFT) .. edge(Edge.RIGHT)].toString())); center(); } void up() { // the cursor moves up, the map scrolls down, leaving an empty line at top y--; scrl(-1); mvprintw(0, 0, toStringz(_board[edge(Edge.TOP), edge(Edge.LEFT) .. edge(Edge.RIGHT)].toString())); center(); } void down() { y++; scrl(1); mvprintw(_height - 1, 0, toStringz(_board[edge(Edge.BOT) - 1, // necessary because opSlice is exclusive edge(Edge.LEFT) .. edge(Edge.RIGHT)].toString())); center(); } void left() { x--; print(); } void right() { x++; print(); } } unittest { import std.stdio; import std.datetime; import lru : LRUCache; import spots : RandomSource, OverlaySource; SysTime starttime = Clock.currTime(); auto random = new RandomSource(); auto source = new OverlaySource(random); foreach (j; iota(100)) { foreach (i; iota(400)) { source[BigInt(i), BigInt(0)]; } } writeln(Clock.currTime() - starttime); foreach (j; iota(100)) { foreach (i; iota(400)) { random[BigInt(i), BigInt(0)]; } } writeln(Clock.currTime() - starttime); } class PartialBoard : Board { private BigInt[2] _yrange, _xrange; this(BigInt[2] y, BigInt[2] x, SpotSource source) { _yrange = y; _xrange = x; super(source); } override Spot opIndex(BigInt y, BigInt x) { return _source[y + _yrange[0], x + _xrange[0]]; } override string toString() { auto strBuilder = appender!string; strBuilder.reserve(cast(ulong)((_yrange[1] - _yrange[0] + 2) * (_xrange[1] - _xrange[0] + 1))); foreach (y; iota(_yrange[0], _yrange[1])) { foreach (x; iota(_xrange[0], _xrange[1])) { strBuilder.put(_source[y, x].contents); } strBuilder.put("\n"); } return strBuilder.data; } }