#include "rogueviz.h" #include #include namespace rogueviz { namespace rwalk { struct line { hyperpoint a; hyperpoint b; color_t col; int timestamp; }; struct rwalker { cell *at; transmatrix ori; transmatrix T; color_t col; int simulated; }; map > drawn; vector walkers; ld total_time; bool draw_rwalk(cell *c, const shiftmatrix& V) { vid.linewidth *= 3; for(auto p: drawn[c]) if(p.timestamp <= total_time) queueline(V * p.a, V * p.b, p.col, 0); vid.linewidth /= 3; return false; } bool in_video; ld sim_speed = 5; int branch_each = 50000; ld step_size = 0.02; bool advance_walkers(int delta) { if(walkers.empty()) { walkers.emplace_back(rwalker{currentmap->gamestart(), Id, Id, 0xFFFFFFFF, 0}); } if(in_video) { ld t = history::phase / (isize(history::v) - 1); total_time = walkers[0].simulated * t; } else { total_time += delta * sim_speed; } for(int i=0; i 7) continue; while(walkers[i].simulated < total_time) { walkers[i].simulated++; if(branch_each && rand() % branch_each == 0) { walkers.push_back(walkers[i]); walkers.back().col = hrandpos() | 0x808080FF; walkers[i].col = hrandpos() | 0x808080FF; } auto& w = walkers[i]; hyperpoint h = tC0(w.T); if(WDIM == 2) { w.T = w.T * xspinpush(randd() * TAU, step_size); } else { hyperpoint dir = random_spin() * xtangent(step_size); apply_parallel_transport(w.T, w.ori, dir); } fixmatrix(w.T); hyperpoint h1 = tC0(w.T); drawn[w.at].emplace_back(line{h, h1, w.col, w.simulated}); virtualRebase(w.at, w.T); w.simulated++; } } // centerover = walkers[0].at; // View = inverse(walkers[0].T); // setdist(centerover, 0, nullptr); return false; } void enable(); int args() { using namespace arg; if(0) ; else if(argis("-rwalk")) { enable(); } else if(argis("-rwparam")) { shift_arg_formula(sim_speed); shift_arg_formula(step_size); shift(); branch_each = argi(); } else return 1; return 0; } void show() { cmode = sm::SIDE | sm::MAYDARK; gamescreen(); dialog::init(XLAT("random walk"), 0xFFFFFFFF, 150, 0); dialog::addSelItem("step size", fts(step_size), 'd'); dialog::add_action([]() { dialog::editNumber(step_size, 1e-3, 10, 0.1, 1e-2, "step size", ""); dialog::scaleLog(); }); dialog::addSelItem("steps per millisecond", fts(sim_speed), 'v'); dialog::add_action([]() { dialog::editNumber(sim_speed, 0, 10, 0.1, 5, "steps per millisecond", ""); }); dialog::addSelItem("steps per branch", its(branch_each), 'b'); dialog::add_action([]() { dialog::editNumber(branch_each, 100, 1000000, 0.1, 50000, "steps per branch", ""); dialog::scaleLog(); }); dialog::addBoolItem("create an animation", in_video, 'a'); dialog::add_action([]() { in_video = !in_video; if(!in_video) { total_time = walkers[0].simulated; history::on = false; } if(in_video) { history::create(currentmap->gamestart(), walkers[0].at, walkers[0].T); models::rotation = rand() % 360; } }); dialog::addBack(); dialog::display(); } void o_key(o_funcs& v) { v.push_back(named_dialog("random walk", show)); } string cap = "non-Euclidean random walk/"; void rw_slide(vector& v, string title, string desc, reaction_t t) { using namespace tour; v.push_back( tour::slide{cap + title, 18, LEGAL::NONE | QUICKGEO, desc, [t] (presmode mode) { setCanvas(mode, '0'); if(mode == pmStart) { tour::slide_backup(mapeditor::drawplayer, false); stop_game(); t(); start_game(); enable(); } if(mode == pmKey) { drawn.clear(); walkers.clear(); total_time = 0; } }} ); } void enable() { rv_hook(hooks_drawcell, 100, draw_rwalk); rv_hook(shmup::hooks_turn, 100, advance_walkers); rv_hook(hooks_o_key, 80, o_key); rv_hook(hooks_clearmemory, 40, [] () { drawn.clear(); walkers.clear(); total_time = 0; }); } auto msc = addHook(hooks_args, 100, args) + addHook_rvslides(180, [] (string s, vector& v) { if(s != "mixed") return; v.push_back(tour::slide{ cap+"random walk visualization", 10, tour::LEGAL::NONE | tour::QUICKSKIP, "Here we see random walk in various geometries.\n" "Press '5' to reset.\n" , [] (tour::presmode mode) { slide_url(mode, 'y', "YouTube link", "https://www.youtube.com/watch?v=sXNI_i6QZZY"); } }); rw_slide(v, "Euclidean plane", "In Euclidean plane, the random walk always returns to the neighborhood of the starting point with probability 1.", [] { set_geometry(gEuclid); set_variation(eVariation::pure); sim_speed = 5; branch_each = 0; step_size = 0.02; }); rw_slide(v, "Euclidean 3-space", "However, in Euclidean 3-space, it does not return.", [] { set_geometry(gCubeTiling); set_variation(eVariation::pure); sim_speed = 5; branch_each = 0; step_size = 0.02; }); rw_slide(v, "Hyperbolic geometry", "In H2, it does not return, even if we branch from time to time.", [] { set_geometry(gNormal); set_variation(eVariation::bitruncated); sim_speed = 5; branch_each = 50000; step_size = 0.02; }); /* it works in other geometries too -- exercise left for the reader */ }); // {4,5} : 10 6 works } }