// Hyperbolic Rogue -- dual-geometry puzzle generator // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details /** \file dpgen.cpp * \brief dual geometry puzzle generator */ #include "hyper.h" namespace hr { EX namespace dpgen { EX bool in; typedef tuple cpos; map visited; vector all; vector last; void enqueue(cpos p, int d, int li) { if(visited.count(p)) return; visited[p] = d; all.push_back(p); last.push_back(li); } void solve(cpos at) { visited.clear(); all.clear(); last.clear(); enqueue(at, 0, -1); for(int i=0; i(next); auto c1 = get<1>(next); auto d = get<2>(next); int dist = visited[next]; for(int k=0; k<4; k++) { cell *ca0 = c0->move(k); if(!ca0) continue; if(ca0->wall != waNone) continue; cell *ca1 = c1->modmove(d+k); if(!ca1) continue; if(ca1->wall != waNone) continue; int s = (c1->c.spin((d+k)%4) - c0->c.spin(k)) & 3; enqueue({ca0, ca1, s}, dist+1, i); } } } int last_elimit, last_hlimit; void launch(int seed, int elimit, int hlimit) { /* setup */ dual::enable(); stop_game(); dual::switch_to(0); specialland = firstland = laCanvas; canvas_default_wall = waSea; vid.scale = .5; dual::switch_to(1); specialland = firstland = laCanvas; shrand(seed); start_game(); in = true; cell *c0, *c1; dual::switch_to(0); vector cl0, cl1; if(1) { c0 = cwt.at; celllister cl(cwt.at, elimit, 999, nullptr); cl0 = cl.lst; for(cell *c: cl.lst) { c->wall = waNone, c->land = laCanvas; c->landparam = cl.getdist(c) % 2 ? 0x80C080 : 0x409040; } println(hlog, "c0 size = ", isize(cl.lst)); } dual::switch_to(1); if(1) { c1 = cwt.at; celllister cl(cwt.at, hlimit, 999, nullptr); cl1 = cl.lst; for(cell *c: cl.lst) { c->wall = waNone, c->land = laCanvas; int id = arcm::current.tilegroup[arcm::id_of(c->master)]; color_t yellows[5] = { 0x80C080, 0x80C0C0, 0x8080C0, 0xC080C0, 0xC0C080 }; c->landparam = yellows[id]; } println(hlog, "c1 size = ", isize(cl.lst)); } cpos start = {c0, c1, 0}; solve(start); println(hlog, "queue size = ", isize(all)); vector clboth; pair worst; if(1) { int wdist = -1, wdcount; for(cell* x0: cl0) for(cell *x1: cl1) { int x = 9999; for(int d=0; d<4; d++) if(visited.count({x0, x1, d})) x = min(x, visited[{x0, x1, d}]); if(x == 9999) continue; if(x > wdist) wdist = x, wdcount = 0; if(wdist == x) { wdcount++; if(hrand(wdcount) == 0) worst = {x0, x1}; } } // println(hlog, "wdist = ", wdist, " x ", wdcount); } clboth = cl0; for(cell *c: cl1) clboth.push_back(c); while(true) { int wdist = -1, wdcount = 0; cell *worst_block; for(cell *c: clboth) if(c->wall == waNone && c != c0 && c != c1) { c->wall = waSea; solve(start); c->wall = waNone; int x = 9999; for(int d=0; d<4; d++) if(visited.count({worst.first, worst.second, d})) x = min(x, visited[{worst.first, worst.second, d}]); if(x == 9999) continue; if(x > wdist) wdist = x, wdcount = 0; if(wdist == x) { wdcount++; if(hrand(wdcount) == 0) worst_block = c; } } println(hlog, "wdist = ", wdist, " x ", wdcount); if(wdist == -1) break; worst_block->wall = waSea; } solve(start); for(int d=0; d<4; d++) if(visited.count({worst.first, worst.second, d})) println(hlog, "D = ", d); println(hlog, "worst = ", worst); for(int i=0; i(all[i]) == worst.first && get<1>(all[i]) == worst.second) { int at = i; while(at != -1) { // get<0>(all[at])->item = itDiamond; // get<1>(all[at])->item = itDiamond; at = last[at]; } break; } worst.first->wall = waOpenPlate; worst.second->wall = waOpenPlate; } struct puzzle { string name; int seed; int el, hl; }; #define A 4,2 vector puzzles = { {"easy 1", 1, 3, 2}, {"easy 2", 2, 3, 2}, {"easy 3", 5, 3, 2}, {"medium 1", 7, 3, 3}, {"medium 2", 11, 3, 3}, {"hard 1", 1, 4, 3}, {"hard 2", 1, 3, 4}, {"hard 3", 1, 3, 5}, }; EX void check() { if(in) { int k = dual::currently_loaded; dual::switch_to(1-k); bool ok = cwt.at->wall == waOpenPlate; dual::switch_to(k); ok = ok && cwt.at->wall == waOpenPlate; if(ok) addMessage("You won!"); } } bool hide_random = false; int last_seed = 0; EX void show_menu() { gamescreen(1); dialog::init(XLAT("dual geometry puzzles")); dialog::addHelp(XLAT("move both characters to marked squares at once!")); dialog::addBreak(100); char ch = 'a'; for(auto& p: puzzles) { dialog::addItem(p.name, ch++); dialog::add_action([p] { launch(last_seed = p.seed, last_elimit = p.el, last_hlimit = p.hl); popScreenAll(); }); } dialog::addBreak(50); if(last_elimit && !hide_random) { dialog::addItem(XLAT("randomize"), 'r'); dialog::add_action([] { last_seed = rand() % 1000000; launch(last_seed, last_elimit, last_hlimit); popScreenAll(); }); dialog::addItem(XLAT("enter seed"), 's'); dialog::add_action([] { dialog::editNumber(last_seed, 0, 1000000, 1, last_seed, XLAT("seed"), ""); dialog::reaction_final = [] { launch(last_seed, last_elimit, last_hlimit); popScreenAll(); }; dialog::extra_options = [] { dialog::addSelItem("Euclidean size", its(last_elimit), 'E'); dialog::add_action([] { popScreen(); dialog::editNumber(last_elimit, 2, 10, 1, 3, XLAT("Euclidean size"), ""); }); dialog::addSelItem("hyperbolic size", its(last_hlimit), 'H'); dialog::add_action([] { popScreen(); dialog::editNumber(last_hlimit, 2, 10, 1, 2, XLAT("hyperbolic size"), ""); }); }; }); } dialog::addBreak(50); dialog::addBack(); dialog::display(); } auto sbhook = addHook(hooks_args, 100, [] { using namespace arg; if(0) ; else if(argis("-dpgen")) { shift(); last_seed = argi(); shift(); last_elimit = argi(); shift(); last_hlimit = argi(); launch(last_seed, last_elimit, last_hlimit); } else if(argis("-d:dpgen")) { pushScreen(show_menu); } else if(argis("-dph")) { last_seed = 1; last_elimit = 3; last_hlimit = 2; launch(1, 3, 2); hide_random = true; pushScreen(show_menu); } else return 1; return 0; }) + addHook(hooks_o_key, 91, [] { if(in) return named_dialog(XLAT("select a puzzle"), show_menu); else return named_functionality(); }); EX } }