import deimos.ncurses; import std.string : toStringz; import core.stdc.locale; import std.format; import std.stdio; import board; import spots; import portals; void main() { setlocale(LC_CTYPE, ""); // ncurses locales are weird auto stdscr = initscr(); scope (exit) endwin(); cbreak(); // disables line buffering for input noecho(); // input doesn't display on-screen intrflush(stdscr, true); keypad(stdscr, true); // allows arrow keys to work scrollok(stdscr, true); // allows scrolling in up/down arrows nonl(); // "return" goes through input auto source = new OverlaySource(new RandomSource()); auto portals = new Portals(source); auto board = new Board(source); auto disp = new BoardDisplay(board, stdscr); disp.print(); int rocks = 0; disp.status = "Press [space] to pick up and set down rocks"; refresh(); import std.bigint : BigInt; BigInt[][int] portal; int lastPortalId; outer: while (true) { auto c = getch(); switch (c) { case ',': case KEY_UP: disp.up(); break; case 'o': case KEY_DOWN: disp.down(); break; case 'e': case KEY_RIGHT: disp.right(); break; case 'a': case KEY_LEFT: disp.left(); break; case KEY_RESIZE: disp.getdims(); break; case ' ': if (source[disp.y, disp.x].isRock()) { source[disp.y, disp.x] = Spot(' '); rocks++; disp.status = format("%d", rocks); disp.print(); } else if (rocks > 0 && source[disp.y, disp.x].isSpace()) { source[disp.y, disp.x] = Spot('*'); rocks--; disp.status = format("%d", rocks); disp.print(); } break; case 'q': break outer; case 's': auto filename = stdscr.readquery("Save:"); if (filename) { try { auto file = File(filename, "wb"); file.writefln!"%s %s"(disp.y, disp.x); file.writefln!"%d"(rocks); source.save(file); portals.save(file); } catch (Exception e) { } } disp.print(); break; case 'l': auto filename = stdscr.readquery("Load:"); if (filename) { try { import std.algorithm.iteration : splitter, map; import std.conv : to; auto file = File(filename); auto coord = file.readln.splitter().map!(to!BigInt); disp.y = coord.front; coord.popFront; disp.x = coord.front; file.readf!"%d\n"(rocks); disp.status = format("%d", rocks); source.load(file); portals.load(file); } catch (Exception e) { } } disp.print(); break; case '/': auto sp = source[disp.y, disp.x]; if (sp.isSpace) { if (rocks >= 10) { rocks -= 10; portals.add(disp.y, disp.x); } } else if (sp.isPortal) { rocks += 10; portals.remove(disp.y, disp.x); } disp.status = format("%d", rocks); disp.print(); break; case 'g': auto sp = source[disp.y, disp.x]; if (sp.isPortal) { auto dest = portals.get(sp.id).pair; if (dest != null) { disp.y = dest.y; disp.x = dest.x; } } disp.print(); break; default: continue; } } } immutable char ESC = 27; string readquery(WINDOW* stdscr, string query) { import std.range : replicate; import std.conv : to; import std.array : appender; int height; int width; getmaxyx(stdscr, height, width); mvprintw(height, 0, toStringz(query ~ " ".replicate(width - query.length))); move(height, cast(int) query.length + 1); scrollok(stdscr, false); scope (success) scrollok(stdscr, true); auto build = appender!(char[]); int p = cast(int) query.length + 1; while (auto ch = getch()) { if (ch == '\r' || ch == EOF) break; if (ch == '\b' || ch == KEY_BACKSPACE || ch == 127) { if (build[].length > 0) { p--; mvaddch(height, p, ' '); build.shrinkTo(build[].length - 1); move(height, p); } } else if (ch == ESC) { return null; } else { build ~= cast(char) ch; addch(ch); p++; } } return cast(string) build[]; }