diff --git a/rogueviz/dynamic-wfc.cpp b/rogueviz/dynamic-wfc.cpp new file mode 100644 index 00000000..7256d389 --- /dev/null +++ b/rogueviz/dynamic-wfc.cpp @@ -0,0 +1,438 @@ +#include "../hyper.h" + +// an implementation of WFC-like constraint satisfaction problem using polytime dynamic programming +// see: https://arxiv.org/abs/2002.09534 + +// usage: hyper -canvas 0 -dynamic-wfc [constraint] [cutoff] [radius] + +// where constraint is one of: +// 0: every cell has 2 yellow neighbors +// 1: every cell's neighborhood has two consistent regions +// 2: every cell's neighborhood has four consistent regions +// 3: landscape: every cell has 1 consistent region of next type and 1 region of prev type + +// cutoff is cut off from the disk +// radius is the radius of the disk to generate + +-geo Cryst3 -crf4 + +namespace hr { + +namespace dynamic_wfc { + +vector generate_pd_list(celllister& cl) { + cell *croot = cl.lst.back(); + + cellwalker cw(croot, 0); + while(cl.listed(cw.peek())) cw++; + while(!cl.listed(cw.peek())) cw++; + + vector currpath = {}; + cellwalker cw1 = cw; + + vector result; + + // int steps = 0; + + auto push = [&] (cell *c) { + // println(hlog, "push ", c, " at ", isize(currpath)); + currpath.push_back(c); + result.push_back(c); + }; + + auto pop = [&] () { + // println(hlog, "pop ", currpath.back(), " at ", isize(currpath)-1); + currpath.pop_back(); + }; + + auto replace = [&] (int pos, cell *c) { + // println(hlog, "replace ", currpath[pos], " to ", c, " at ", pos, "/", isize(currpath)); + currpath[pos] = c; + result.push_back(c); + }; + + push(croot); + + while(true) { + cw += wstep; + cw++; + while(!cl.listed(cw.peek())) cw++; + cell *cn = cw.at; + // cn->item = itGold; + // println(hlog, "at ", cn, " [", steps++, "]"); + int cd = celldistance(cn, croot) + 1; + if(cd > isize(currpath)) + push(cn); + else { + if(cd < isize(currpath)) + pop(); + + if(currpath.back() != cn) { + int moves = 1; + + while(celldistance(currpath[cd-1-moves], cn) > moves) { + moves++; + continue; + } + + while(moves > 0) { + moves--; + forCellEx(cx, currpath[cd-2-moves]) + if(celldistance(cx, cn) == moves) + replace(cd-1-moves, cx); + } + } + } + + /* + cell *last = nullptr; + + for(cell *c: currpath) { + if(last) print(hlog, " ", celldistance(c, last), " "); + last = c; + print(hlog, c); + } + println(hlog); */ + + for(int i=1; i trans; + +vector ctf; + +vector global_list; + +int wfctype, wfcrad, cutoff; + +void wfc_build() { + int code_at = trans.back().news; + int cpos = isize(ctf); + for(int i=isize(trans)-1; i>=0; i--) { + auto& tri = trans[i]; + if(tri.news == code_at) { + // println(hlog, tie(tri.news, tri.olds, tri.proportion, tri.where, tri.id)); + if(hrandf() < tri.proportion) { + cell *c = ctf[--cpos]; + int id = tri.id; + + switch(wfctype) { + case 0: + if(id == 0) c->wall = waNone, c->landparam = 0xFF4040; + else c->wall = waNone, c->landparam = 0xFFFF40; + break; + case 1: + if(id == 0) c->wall = waNone, c->landparam = 0x4040FF; + else c->wall = waNone, c->landparam = 0x40FF40; + break; + case 2: + if(id == 0) c->wall = waNone, c->landparam = 0x8080FF; + else c->wall = waNone, c->landparam = 0x202020; + break; + case 3: + if(id == 0) c->wall = waNone, c->landparam = 0xC08080; + else if(id == 1) c->wall = waNone, c->landparam = 0x80C080; + else c->wall = waNone, c->landparam = 0x8080C0; + break; + } + + // println(hlog, code_at, " -> ", tri.olds, " [", tri.where, "=", tri.id, "]"); + code_at = tri.olds; + } + } + } + } + +void wfc() { + int rad = wfcrad; + vector< vector< vector > > new_neighborhoods; + map lorder; + map multiplicity; + + set consider; + + celllister cl(cwt.at, rad, 999999, nullptr); + for(cell *c: cl.lst) setdist(c, 7, nullptr); + auto l = generate_pd_list(cl); + int ls = isize(l); + for(int i=0; i ", mul[a]); + break; + } + } + + // println(hlog, a, ": ", v, " -> ", mul[a]); + } + + /* + mul[0] = 0; + for(int a=0; a<8; a++) + mul[1< sids; + for(auto cz: nns) { + for(int id=0; id> (cpo*v)) & mask); + p.prob *= mul[code]; + } + + for(auto cz: nns) { + multiplicity[cz]--; + if(multiplicity[cz] == 0) { + ctf.push_back(cz); + // println(hlog, "remove ", cz, " (#", lorder[cz], ") from ", inpath); + fflush(stdout); + int is = isize(inpath)-1; + int id = 0; + while(id <= is && inpath[id] != cz) id++; + if(id > is) { + println(hlog, "error"); + exit(4); + } + inpath[id] = inpath[is]; + inpath.resize(is); + for(auto& p: freq) if(p.prob) { + nfreq.push_back(p); + int tid = (p.code >> (cpo*id)) & mask; + code_t& s = nfreq.back().code; + s &=~ (mask << (cpo*id)); + if(id < is) s |= ((s >> (cpo*is)) & mask) << (cpo*id); + s &=~ (mask << (cpo*is)); + // s.resize(is); + // if(id < is) s[id] = p.code[is]; + nfreq.back().id = (nfreq.back().id << cpo) | tid; + } + + freq.clear(); + sort(nfreq.begin(), nfreq.end(), [] (const freqdata& a, const freqdata& b) { return a.code < b.code; }); + for(int i=0; i> cpo; + nt.proportion = next.prob / group.prob; + nt.id = next.id & mask; + trans.push_back(nt); + } + + nfreq.clear(); + } + } + } + } + + if(isize(freq)) println(hlog, "last freq = ", freq[0].prob); + println(hlog, "freq size = ", isize(freq)); + println(hlog, "tfreq = ", format("%lld", tfreq)); + println(hlog, "trans size = ", isize(trans)); + println(hlog, "next code = ", nextcode); + + wfc_build(); + } + +bool wfc_handleKey(int sym, int uni) { + if(sym == '5') { + wfc_build(); + return true; + } + return false; + } + +void hwrite(hstream& f, const ttrans& t) { + hwrite(f, t.news); + hwrite(f, t.olds); + hwrite(f, t.proportion); + hwrite(f, t.id); + } + +void save(string s) { + fhstream f(s, "wb"); + vector indices; + for(auto cf: ctf) + for(int i=0; i