2021-07-29 12:07:30 +00:00
// Hyperbolic Rogue -- rule generator
// Copyright (C) 2011-2021 Zeno Rogue, see 'hyper.cpp' for details
/** \file rulegen.cpp
* \ brief An algorithm to create strict tree rules for arb tessellations
*/
# include "hyper.h"
namespace hr {
EX namespace rulegen {
/* limits */
EX int max_retries = 999 ;
2021-07-31 13:48:12 +00:00
EX int max_tcellcount = 1000000 ;
2021-07-29 12:07:30 +00:00
EX int max_adv_steps = 100 ;
EX int max_examine_branch = 5040 ;
EX int max_bdata = 1000 ;
2021-08-19 22:48:09 +00:00
EX int max_getside = 10000 ;
2021-08-21 22:12:22 +00:00
EX int rulegen_timeout = 60 ;
2021-07-29 12:07:30 +00:00
# if HDR
/** exception thrown by this algoritm in case of any problems */
struct rulegen_failure : hr_exception {
rulegen_failure ( string _s ) : hr_exception ( _s ) { }
} ;
/** this exception is thrown if we want to restart the computation -- this is normal, but if thrown more than max_retries times, just surrender */
struct rulegen_retry : rulegen_failure {
rulegen_retry ( string _s ) : rulegen_failure ( _s ) { }
} ;
/** this exception is thrown in case if we run into a special case that is not implemented yet */
struct rulegen_surrender : rulegen_failure {
rulegen_surrender ( string _s ) : rulegen_failure ( _s ) { }
} ;
const int MYSTERY = 31999 ;
# endif
2021-08-17 12:20:54 +00:00
EX bool parent_debug ;
2021-07-29 12:07:30 +00:00
/* === tcell === */
/** number of tcells created */
EX int tcellcount = 0 ;
/** number of tcells united into other tcells */
EX int tunified = 0 ;
2021-08-21 21:38:36 +00:00
/** hard cases for get_parent_dir */
EX int hard_parents = 0 ;
/** the number of roots with single live branches */
EX int single_live_branches = 0 ;
/** the number of roots with double live branches */
EX int double_live_branches = 0 ;
2021-08-21 22:10:02 +00:00
/** the number of treestates pre-minimization */
EX int states_premini = 0 ;
2021-08-22 17:41:44 +00:00
# if HDR
2021-08-21 22:10:02 +00:00
/** change some flags -- they usually make it worse */
2021-08-22 12:30:24 +00:00
static const flagtype w_numerical = Flag ( 1 ) ; /*< build trees numerically */
2021-08-21 22:10:02 +00:00
static const flagtype w_single_shortcut = Flag ( 2 ) ; /*< generate just one shortcut */
static const flagtype w_no_shortcut = Flag ( 3 ) ; /*< generate no shortcuts */
static const flagtype w_no_restart = Flag ( 4 ) ; /*< do not restart at powers of two */
static const flagtype w_no_sidecache = Flag ( 5 ) ; /*< do not cache get_side */
static const flagtype w_no_relative_distance = Flag ( 6 ) ; /*< do not build relative distances into codes */
static const flagtype w_examine_once = Flag ( 7 ) ; /*< restart after first conflict found in analysis */
static const flagtype w_examine_all = Flag ( 8 ) ; /*< focus on all conflicts found in analysis even if we know them */
static const flagtype w_conflict_all = Flag ( 9 ) ; /*< full extension in case of conflicts */
static const flagtype w_parent_always = Flag ( 10 ) ; /*< always consider the full parent rule */
static const flagtype w_parent_reverse = Flag ( 11 ) ; /*< reverse paths in parent_dir */
static const flagtype w_parent_side = Flag ( 12 ) ; /*< allow side paths in parent_dir */
static const flagtype w_parent_never = Flag ( 13 ) ; /*< never consider the full parent rule */
static const flagtype w_always_clean = Flag ( 14 ) ; /*< restart following phases after any distance errors */
static const flagtype w_single_origin = Flag ( 15 ) ; /*< consider only one origin */
static const flagtype w_slow_side = Flag ( 16 ) ; /*< do not try get_side optimization */
2021-08-22 17:42:07 +00:00
static const flagtype w_bfs = Flag ( 17 ) ; /*< compute distances using BFS */
2021-08-22 12:28:54 +00:00
# endif
2021-08-21 22:10:02 +00:00
EX flagtype flags = 0 ;
2021-07-29 12:07:30 +00:00
# if HDR
struct tcell * tmove ( tcell * c , int d ) ;
/** rulegen algorithm works on tcells which have their own map generation */
struct tcell {
/** tcells form a list */
tcell * next ;
/** shape ID in arb::current */
int id ;
/** degree */
int type ;
/** distance from the root */
short dist ;
/** cached code */
short code ;
/** direction to the parent in the tree */
short parent_dir ;
/** can we assume that dist is correct? if we assumed that the dist is correct but then find out it was wrong, throw an error */
bool is_solid ;
bool distance_fixed ;
/** sometimes we find out that multiple tcells represent the same actual cell -- in this case we unify them; unified_to is used for the union-find algorithm */
walker < tcell > unified_to ;
int degree ( ) { return type ; }
connection_table < tcell > c ;
tcell * & move ( int d ) { return c . move ( d ) ; }
tcell * & modmove ( int d ) { return c . modmove ( d ) ; }
tcell * cmove ( int d ) { return tmove ( this , d ) ; }
tcell * cmodmove ( int d ) { return tmove ( this , c . fix ( d ) ) ; }
tcell ( ) { }
} ;
inline void print ( hstream & hs , tcell * h ) { print ( hs , " P " , index_pointer ( h ) ) ; }
using twalker = walker < tcell > ;
# endif
queue < reaction_t > fix_queue ;
2021-08-17 18:06:48 +00:00
void push_unify ( twalker a , twalker b ) {
if ( a . at - > id ! = b . at - > id ) {
throw hr_exception ( " queued bad unify " ) ;
}
fix_queue . push ( [ = ] { unify ( a , b ) ; } ) ;
}
2021-07-29 12:07:30 +00:00
bool in_fixing = false ;
void process_fix_queue ( ) {
if ( in_fixing ) return ;
in_fixing = true ;
while ( ! fix_queue . empty ( ) ) {
fix_queue . front ( ) ( ) ;
fix_queue . pop ( ) ;
}
in_fixing = false ;
}
2021-08-23 22:32:53 +00:00
EX void ufind ( twalker & p ) {
2021-07-29 12:07:30 +00:00
if ( p . at - > unified_to . at = = p . at ) return ;
twalker p1 = p . at - > unified_to ;
ufind ( p1 ) ;
p . at - > unified_to = p1 ;
p = p1 + p . spin ;
}
2021-08-17 21:05:56 +00:00
EX void ufindc ( tcell * & c ) {
2021-08-04 16:35:04 +00:00
twalker cw = c ; ufind ( cw ) ; c = cw . at ;
}
2021-07-29 12:07:30 +00:00
EX tcell * first_tcell = nullptr ;
2021-08-18 23:27:26 +00:00
twalker addstep ( twalker x ) {
x . cpeek ( ) ;
ufind ( x ) ;
return x + wstep ;
}
2021-07-29 12:07:30 +00:00
void connect_and_check ( twalker p1 , twalker p2 ) ;
void unify ( twalker pw1 , twalker pw2 ) ;
tcell * gen_tcell ( int id ) {
int d = isize ( arb : : current . shapes [ id ] . connections ) ;
auto c = tailored_alloc < tcell > ( d ) ;
c - > id = id ;
c - > next = first_tcell ;
c - > unified_to = twalker ( c , 0 ) ;
c - > is_solid = false ;
c - > distance_fixed = false ;
c - > dist = MYSTERY ;
c - > code = MYSTERY ;
c - > parent_dir = MYSTERY ;
first_tcell = c ;
// println(hlog, c, " is a new tcell of id ", id);
tcellcount + + ;
return c ;
}
2021-08-22 12:30:24 +00:00
map < cell * , tcell * > cell_to_tcell ;
map < tcell * , cell * > tcell_to_cell ;
2021-07-29 12:07:30 +00:00
tcell * tmove ( tcell * c , int d ) {
2021-08-17 18:06:48 +00:00
if ( d < 0 | | d > = c - > type ) throw hr_exception ( " wrong d " ) ;
2021-07-29 12:07:30 +00:00
if ( c - > move ( d ) ) return c - > move ( d ) ;
2021-08-22 12:30:24 +00:00
if ( flags & w_numerical ) {
cell * oc = tcell_to_cell [ c ] ;
cell * oc1 = oc - > cmove ( d ) ;
auto & c1 = cell_to_tcell [ oc1 ] ;
if ( ! c1 ) {
c1 = gen_tcell ( shvid ( oc1 ) ) ;
tcell_to_cell [ c1 ] = oc1 ;
}
c - > c . connect ( d , cell_to_tcell [ oc1 ] , oc - > c . spin ( d ) , false ) ;
return c1 ;
}
2021-07-29 12:07:30 +00:00
auto cd = twalker ( c , d ) ;
ufind ( cd ) ;
2021-08-17 21:05:56 +00:00
auto & co = arb : : current . shapes [ c - > id ] . connections [ cd . spin ] ;
2021-07-29 12:07:30 +00:00
tcell * c1 = gen_tcell ( co . sid ) ;
connect_and_check ( cd , twalker ( c1 , co . eid ) ) ;
return c1 ;
}
/** check whether we have completed the vertex to the right of edge d of c */
void check_loops ( twalker pw ) {
ufind ( pw ) ;
auto & shs = arb : : current . shapes ;
int id = pw . at - > id ;
int valence = shs [ id ] . vertex_valence [ pw . spin ] ;
int steps = 0 ;
twalker pwf = pw ;
twalker pwb = pw ;
while ( true ) {
if ( ! pwb . peek ( ) ) break ;
pwb = pwb + wstep - 1 ;
steps + + ;
if ( pwb = = pwf ) {
if ( steps = = valence ) return ; /* that's great, we already know this loop */
else throw hr_exception ( " vertex valence too small " ) ;
}
if ( steps = = valence ) {
2021-08-17 18:06:48 +00:00
push_unify ( pwf , pwb ) ;
2021-07-29 12:07:30 +00:00
return ;
}
}
while ( true ) {
pwf + + ;
if ( ! pwf . peek ( ) ) break ;
pwf + = wstep ;
steps + + ;
if ( pwb = = pwf ) {
if ( steps = = valence ) return ; /* that's great, we already know this loop */
else throw hr_exception ( " vertex valence too small " ) ;
}
if ( steps = = valence ) {
2021-08-17 18:06:48 +00:00
push_unify ( pwf , pwb ) ;
2021-07-29 12:07:30 +00:00
return ;
}
}
if ( steps = = valence - 1 ) {
connect_and_check ( pwb , pwf ) ;
}
}
void connect_and_check ( twalker p1 , twalker p2 ) {
2021-08-17 21:05:56 +00:00
ufind ( p1 ) ; ufind ( p2 ) ;
2021-07-29 12:07:30 +00:00
p1 . at - > c . connect ( p1 . spin , p2 . at , p2 . spin , false ) ;
fix_queue . push ( [ = ] { check_loops ( p1 ) ; } ) ;
fix_queue . push ( [ = ] { check_loops ( p2 ) ; } ) ;
process_fix_queue ( ) ;
}
2021-08-17 18:06:48 +00:00
EX void unify ( twalker pw1 , twalker pw2 ) {
2021-07-29 12:07:30 +00:00
ufind ( pw1 ) ;
ufind ( pw2 ) ;
2021-08-17 21:05:56 +00:00
if ( pw1 = = pw2 ) return ;
2021-07-29 12:07:30 +00:00
if ( pw1 . at - > unified_to . at ! = pw1 . at )
throw hr_exception ( " not unified to itself " ) ;
if ( pw2 . at - > unified_to . at ! = pw2 . at )
throw hr_exception ( " not unified to itself " ) ;
if ( pw1 . at = = pw2 . at ) {
if ( pw1 . spin ! = pw2 . spin ) throw hr_exception ( " called unify with self and wrong direction " ) ;
return ;
}
if ( pw1 . at - > id ! = pw2 . at - > id )
throw hr_exception ( " unifying two cells of different id's " ) ;
auto & shs = arb : : current . shapes ;
2021-08-17 18:06:48 +00:00
if ( ( pw1 . spin - pw2 . spin ) % shs [ pw1 . at - > id ] . cycle_length )
throw hr_exception ( " unification spin disagrees with cycle_length " ) ;
2021-08-19 11:12:26 +00:00
unify_distances ( pw1 . at , pw2 . at , pw2 . spin - pw1 . spin ) ;
2021-07-29 12:07:30 +00:00
int id = pw1 . at - > id ;
for ( int i = 0 ; i < shs [ id ] . size ( ) ; i + + ) {
if ( ! pw2 . peek ( ) ) {
/* no need to reconnect */
}
else if ( ! pw1 . peek ( ) ) {
connect_and_check ( pw1 , pw2 + wstep ) ;
}
else {
2021-08-17 18:06:48 +00:00
push_unify ( pw1 + wstep , pw2 + wstep ) ;
2021-07-29 12:07:30 +00:00
auto ss = pw1 + wstep ;
connect_and_check ( pw1 , pw2 + wstep ) ;
connect_and_check ( pw1 , ss ) ;
}
pw1 + + ;
pw2 + + ;
}
pw2 . at - > unified_to = pw1 - pw2 . spin ;
tunified + + ;
}
2021-07-30 13:18:32 +00:00
EX vector < tcell * > t_origin ;
2021-07-29 12:07:30 +00:00
void delete_tmap ( ) {
while ( first_tcell ) {
auto second = first_tcell - > next ;
tailored_delete ( first_tcell ) ;
first_tcell = second ;
}
tcellcount = 0 ;
tunified = 0 ;
2021-07-30 13:18:32 +00:00
t_origin . clear ( ) ;
2021-07-29 12:07:30 +00:00
}
/* used in the debugger */
EX vector < twalker > debuglist ;
/* === distances === */
bool no_errors = false ;
struct hr_solid_error : rulegen_retry {
hr_solid_error ( ) : rulegen_retry ( " solid error " ) { }
} ;
2021-08-22 12:30:52 +00:00
/** since the last restart */
2021-07-29 12:07:30 +00:00
int solid_errors ;
2021-08-22 12:30:52 +00:00
/** total solid errors */
EX int all_solid_errors ;
2021-08-15 15:26:38 +00:00
# if HDR
2021-08-04 16:35:04 +00:00
struct shortcut {
vector < int > pre ;
vector < int > post ;
tcell * sample ;
int delta ;
} ;
2021-08-15 15:26:38 +00:00
# endif
2021-08-04 16:35:04 +00:00
2021-08-18 23:25:18 +00:00
EX map < int , vector < unique_ptr < shortcut > > > shortcuts ;
2021-08-04 16:35:04 +00:00
vector < int > root_path ( twalker cw ) {
cw + = wstep ;
vector < int > res ;
while ( true ) {
int i = cw . at - > dist = = 0 ? 0 : get_parent_dir ( cw . at ) ;
int j = cw . to_spin ( i ) ;
res . push_back ( j ) ;
if ( cw . at - > dist = = 0 ) return res ;
cw + = j ;
cw + = wstep ;
}
}
2021-08-19 11:12:26 +00:00
EX void shortcut_found ( tcell * c , tcell * alt , const vector < twalker > & walkers , const vector < twalker > & walkers2 , const vector < int > & walkerdir , const vector < int > & walkerdir2 ) {
2021-08-04 16:35:04 +00:00
auto at0 = walkers2 . back ( ) . at ;
tcell * at = at0 ;
2021-08-19 11:12:26 +00:00
twalker at1 ; at1 . at = nullptr ;
2021-08-04 16:35:04 +00:00
for ( int i = isize ( walkers ) - 1 ; i > = 1 ; i - - ) if ( at = = walkers [ i ] . at ) at1 = walkers [ i ] ;
2021-08-19 11:12:26 +00:00
if ( ! at1 . at ) return ; /* made obsolete by unification */
2021-08-04 16:35:04 +00:00
vector < int > pre ;
for ( int i = isize ( walkers ) - 1 ; i > = 1 ; i - - ) if ( at = = walkers [ i ] . at ) {
pre . push_back ( walkerdir [ i ] ) ; at = walkers [ i ] . peek ( ) ;
}
2021-08-19 11:12:26 +00:00
if ( at ! = c ) {
if ( parent_debug ) println ( hlog , " did not return to c " ) ;
return ;
}
2021-08-04 16:35:04 +00:00
at = at0 ;
vector < int > post ;
for ( int i = isize ( walkers2 ) - 1 ; i > = 1 ; i - - ) if ( at = = walkers2 [ i ] . at ) {
post . push_back ( walkerdir2 [ i ] ) ; at = walkers2 [ i ] . peek ( ) ;
}
2021-08-19 11:12:26 +00:00
if ( at ! = alt ) {
if ( parent_debug ) println ( hlog , " did not return to alt " ) ;
return ;
}
2021-08-04 16:35:04 +00:00
reverse ( pre . begin ( ) , pre . end ( ) ) ;
reverse ( post . begin ( ) , post . end ( ) ) ;
int delta = at1 . to_spin ( walkers2 . back ( ) . spin ) ;
2021-08-19 11:12:26 +00:00
for ( auto & s : shortcuts [ c - > id ] ) if ( s - > pre = = pre & & s - > post = = post ) {
if ( parent_debug ) println ( hlog , " already knew that " , pre , " ~ " , post ) ;
return ;
}
2021-08-04 16:35:04 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " new shortcut found, pre = " , pre , " post = " , post , " pre reaches " , at1 , " post reaches " , walkers2 . back ( ) , " of type " , at1 . at - > id , " sample = " , c ) ;
2021-08-17 17:27:02 +00:00
if ( isize ( pre ) > 500 ) {
debuglist = { c } ;
throw rulegen_failure ( " shortcut too long " ) ;
}
2021-08-15 15:26:38 +00:00
shortcuts [ c - > id ] . emplace_back ( unique_ptr < shortcut > ( new shortcut ) ) ;
auto & sh = shortcuts [ c - > id ] . back ( ) ;
sh - > pre = pre ;
sh - > post = post ;
sh - > sample = c ;
sh - > delta = delta ;
auto & sh1 = * sh ;
2021-08-04 16:35:04 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " exhaustive search: " ) ;
indenter ind ( 2 ) ;
tcell * c1 = first_tcell ;
while ( c1 ) {
2021-08-15 15:26:38 +00:00
if ( c1 - > id = = c - > id ) look_for_shortcuts ( c1 , sh1 ) ;
2021-08-04 16:35:04 +00:00
c1 = c1 - > next ;
}
}
2021-08-19 11:13:59 +00:00
EX void find_new_shortcuts ( tcell * c , int d , tcell * alt , int delta ) {
2021-08-21 22:10:02 +00:00
2021-08-22 12:30:52 +00:00
solid_errors + + ;
all_solid_errors + + ;
2021-08-21 22:10:02 +00:00
if ( flags & w_no_shortcut ) return ;
2021-08-19 11:12:26 +00:00
ufindc ( c ) ;
if ( debugflags & DF_GEOM )
println ( hlog , " solid " , c , " changes " , c - > dist , " to " , d , " alt= " , alt ) ;
if ( c - > dist = = MYSTERY )
2021-08-19 11:13:59 +00:00
throw rulegen_failure ( " find_new_shortcuts with MYSTERY distance " ) ;
2021-08-19 11:12:26 +00:00
set < tcell * > seen ;
vector < twalker > walkers ;
vector < int > walkerdir = { - 1 } ;
seen . insert ( c ) ;
walkers . push_back ( c ) ;
for ( int j = 0 ; j < isize ( walkers ) ; j + + ) {
auto w = walkers [ j ] ;
for ( int s = 0 ; s < w . at - > type ; s + + ) {
twalker w1 = w + s ;
if ( w1 . peek ( ) & & w1 . peek ( ) - > dist = = w . at - > dist - 1 & & ! seen . count ( w1 . peek ( ) ) ) {
seen . insert ( w1 . peek ( ) ) ;
walkers . push_back ( w1 + wstep ) ;
walkerdir . push_back ( s ) ;
}
}
}
c - > dist = d ;
set < tcell * > seen2 ;
vector < twalker > walkers2 ;
vector < int > walkerdir2 = { - 1 } ;
walkers2 . push_back ( twalker ( alt , delta ) ) ;
for ( int j = 0 ; j < isize ( walkers2 ) ; j + + ) {
auto w = walkers2 [ j ] ;
2021-08-19 22:44:12 +00:00
if ( j = = 0 | | ! seen . count ( w . at ) )
2021-08-19 11:12:26 +00:00
for ( int s = 0 ; s < w . at - > type ; s + + ) {
twalker w1 = w + s ;
if ( ! w1 . peek ( ) ) continue ;
if ( w1 . peek ( ) - > dist = = w . at - > dist - 1 & & ! seen2 . count ( w1 . peek ( ) ) ) {
seen2 . insert ( w1 . peek ( ) ) ;
walkers2 . push_back ( w1 + wstep ) ;
walkerdir2 . push_back ( s ) ;
if ( seen . count ( w1 . peek ( ) ) ) {
shortcut_found ( c , alt , walkers , walkers2 , walkerdir , walkerdir2 ) ;
2021-08-21 22:10:02 +00:00
if ( flags & w_single_shortcut ) return ;
2021-08-19 11:12:26 +00:00
/* we do not want to go further */
for ( auto & w : walkers ) ufind ( w ) ;
for ( auto & w : walkers2 ) ufind ( w ) ;
seen . clear ( ) ; for ( auto & w : walkers ) seen . insert ( w . at ) ;
seen2 . clear ( ) ; for ( auto & w : walkers2 ) seen2 . insert ( w . at ) ;
}
}
}
}
}
2021-08-16 22:24:38 +00:00
EX void remove_parentdir ( tcell * c ) {
2021-08-18 23:24:01 +00:00
sidecache . clear ( ) ;
2021-08-16 22:24:38 +00:00
c - > parent_dir = MYSTERY ;
c - > code = MYSTERY ;
for ( int i = 0 ; i < c - > type ; i + + ) if ( c - > move ( i ) ) {
c - > move ( i ) - > parent_dir = MYSTERY ;
c - > move ( i ) - > code = MYSTERY ;
}
}
2021-08-22 17:42:07 +00:00
queue < tcell * > bfs_queue ;
2021-08-04 16:35:04 +00:00
void fix_distances ( tcell * c ) {
2021-08-22 17:42:07 +00:00
if ( flags & w_bfs ) while ( true ) {
ufindc ( c ) ;
if ( c - > dist ! = MYSTERY ) return ;
if ( tcellcount > = max_tcellcount ) throw rulegen_surrender ( " max_tcellcount exceeded " ) ;
if ( bfs_queue . empty ( ) ) throw rulegen_failure ( " empty bfs queue " ) ;
auto c1 = bfs_queue . front ( ) ;
ufindc ( c1 ) ;
bfs_queue . pop ( ) ;
for ( int i = 0 ; i < c1 - > type ; i + + ) {
tcell * c2 = c1 - > cmove ( i ) ;
if ( c2 - > dist = = MYSTERY ) {
c2 - > dist = c1 - > dist + 1 ;
bfs_queue . push ( c2 ) ;
}
}
}
2021-07-29 12:07:30 +00:00
c - > distance_fixed = true ;
vector < tcell * > q = { c } ;
for ( int qi = 0 ; qi < isize ( q ) ; qi + + ) {
c = q [ qi ] ;
auto & d = c - > dist ;
restart :
for ( int i = 0 ; i < c - > type ; i + + ) {
2021-08-17 18:08:06 +00:00
if ( ! c - > move ( i ) ) continue ;
2021-07-29 12:07:30 +00:00
tcell * c1 = c - > cmove ( i ) ;
2021-08-04 16:35:04 +00:00
ufindc ( c ) ;
c1 = c - > cmove ( i ) ;
2021-07-29 12:07:30 +00:00
if ( c1 - > dist = = MYSTERY ) continue ;
auto & d1 = c1 - > dist ;
2021-08-16 22:24:38 +00:00
if ( d > d1 + 1 ) { d = d1 + 1 ; remove_parentdir ( c ) ; goto restart ; }
2021-07-29 12:07:30 +00:00
if ( d1 > d + 1 ) {
if ( c1 - > is_solid ) {
2021-08-19 11:13:59 +00:00
find_new_shortcuts ( c1 , d + 1 , c1 , 0 ) ;
2021-07-29 12:07:30 +00:00
}
d1 = d + 1 ;
2021-08-16 22:24:38 +00:00
remove_parentdir ( c1 ) ;
2021-07-29 12:07:30 +00:00
q . push_back ( c1 ) ;
}
}
}
}
void calc_distances ( tcell * c ) {
if ( c - > dist ! = MYSTERY ) return ;
fix_distances ( c ) ;
}
2021-08-19 11:12:26 +00:00
EX void unify_distances ( tcell * c1 , tcell * c2 , int delta ) {
2021-07-29 12:07:30 +00:00
int d1 = c1 - > dist ;
int d2 = c2 - > dist ;
int d = min ( d1 , d2 ) ;
2021-08-19 11:13:59 +00:00
if ( c1 - > is_solid & & d ! = d1 ) { solid_errors + + ; find_new_shortcuts ( c1 , d , c2 , delta ) ; remove_parentdir ( c1 ) ; fix_distances ( c1 ) ; }
2021-07-29 12:07:30 +00:00
c1 - > dist = d ;
2021-08-19 11:13:59 +00:00
if ( c2 - > is_solid & & d ! = d2 ) { solid_errors + + ; find_new_shortcuts ( c2 , d , c1 , - delta ) ; remove_parentdir ( c2 ) ; fix_distances ( c2 ) ; }
2021-07-29 12:07:30 +00:00
c2 - > dist = d ;
2021-08-19 11:12:26 +00:00
c1 - > distance_fixed = c2 - > distance_fixed = c1 - > distance_fixed | | c2 - > distance_fixed ;
c1 - > is_solid = c2 - > is_solid = c1 - > is_solid | | c2 - > is_solid ;
2021-07-29 12:07:30 +00:00
}
2021-08-19 11:12:26 +00:00
EX void handle_distance_errors ( ) {
2021-07-29 12:07:30 +00:00
bool b = solid_errors ;
solid_errors = 0 ;
2021-08-05 09:56:49 +00:00
if ( b & & ! no_errors ) {
2021-08-18 23:24:01 +00:00
sidecache . clear ( ) ;
2021-08-21 22:10:02 +00:00
if ( flags & w_always_clean ) clean_data ( ) ;
2021-08-05 09:56:49 +00:00
throw hr_solid_error ( ) ;
}
2021-07-29 12:07:30 +00:00
}
/** make sure that we know c->dist */
void be_solid ( tcell * c ) {
if ( c - > is_solid ) return ;
if ( tcellcount > = max_tcellcount )
throw rulegen_surrender ( " max_tcellcount exceeded " ) ;
2021-08-04 16:35:04 +00:00
ufindc ( c ) ;
calc_distances ( c ) ;
ufindc ( c ) ;
look_for_shortcuts ( c ) ;
ufindc ( c ) ;
2021-08-18 23:33:07 +00:00
if ( c - > dist = = MYSTERY ) {
2021-08-21 21:40:50 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " set solid but no dist " , c ) ;
2021-08-18 23:25:18 +00:00
debuglist = { c } ;
throw rulegen_failure ( " set solid but no dist " ) ;
2021-08-04 16:35:04 +00:00
}
2021-07-29 12:07:30 +00:00
c - > is_solid = true ;
}
2021-08-15 15:26:38 +00:00
EX void look_for_shortcuts ( tcell * c , shortcut & sh ) {
if ( c - > dist < = 0 ) return ;
if ( 1 ) {
twalker tw0 ( c , 0 ) ;
twalker tw ( c , 0 ) ;
ufind ( tw ) ;
ufind ( tw0 ) ;
2021-08-04 16:35:04 +00:00
2021-08-15 15:26:38 +00:00
vector < tcell * > opath ;
2021-08-04 16:35:04 +00:00
2021-08-15 15:26:38 +00:00
for ( auto & v : sh . pre ) {
2021-08-04 16:35:04 +00:00
opath . push_back ( tw . at ) ;
2021-08-15 15:26:38 +00:00
tw + = v ;
if ( ! tw . peek ( ) ) return ;
if ( tw . peek ( ) - > dist ! = tw . at - > dist - 1 ) return ;
ufind ( tw ) ;
tw + = wstep ;
}
opath . push_back ( tw . at ) ;
2021-08-04 16:35:04 +00:00
2021-08-15 15:26:38 +00:00
ufind ( tw0 ) ;
vector < tcell * > npath ;
for ( auto & v : sh . post ) {
2021-08-04 16:35:04 +00:00
npath . push_back ( tw0 . at ) ;
2021-08-15 15:26:38 +00:00
tw0 + = v ;
ufind ( tw0 ) ;
tw0 + = wstep ;
calc_distances ( tw0 . at ) ;
}
npath . push_back ( tw0 . at ) ;
int d = sh . delta ;
auto tw1 = tw + d ;
2021-08-19 11:12:26 +00:00
if ( tw1 . at - > id ! = tw0 . at - > id )
println ( hlog , " ERROR: improper shortcut " ) ;
else
push_unify ( tw1 , tw0 ) ;
2021-08-15 15:26:38 +00:00
process_fix_queue ( ) ;
for ( auto t : npath ) {
ufindc ( t ) ;
fix_distances ( t ) ;
2021-08-04 16:35:04 +00:00
}
2021-08-15 15:26:38 +00:00
ufindc ( c ) ;
2021-08-04 16:35:04 +00:00
}
}
2021-08-15 15:26:38 +00:00
EX void look_for_shortcuts ( tcell * c ) {
if ( c - > dist > 0 )
for ( int i = 0 ; i < isize ( shortcuts [ c - > id ] ) ; i + + )
look_for_shortcuts ( c , * shortcuts [ c - > id ] [ i ] ) ;
}
2021-08-17 12:18:54 +00:00
void trace_root_path ( vector < int > & rp , twalker cw ) {
auto d = cw . peek ( ) - > dist ;
cw + = wstep ;
2021-08-21 22:10:02 +00:00
bool side = ( flags & w_parent_side ) ;
2021-08-17 12:18:54 +00:00
next :
if ( d > 0 ) {
2021-08-19 23:35:19 +00:00
ufind ( cw ) ;
handle_distance_errors ( ) ;
2021-08-21 22:10:02 +00:00
int di = side ? - 1 : get_parent_dir ( cw . at ) ;
2021-08-17 12:18:54 +00:00
for ( int i = 0 ; i < cw . at - > type ; i + + ) {
2021-08-21 22:10:02 +00:00
if ( ( ! side ) & & ( cw + i ) . spin ! = di ) continue ;
2021-08-17 12:18:54 +00:00
tcell * c1 = ( cw + i ) . peek ( ) ;
2021-08-19 23:35:19 +00:00
if ( ! c1 ) continue ;
2021-08-17 12:18:54 +00:00
be_solid ( c1 ) ;
if ( c1 - > dist < d ) {
rp . push_back ( i ) ;
cw + = i ;
cw + = wstep ;
d - - ;
goto next ;
}
}
}
rp . push_back ( cw . to_spin ( 0 ) ) ;
2021-08-21 22:10:02 +00:00
if ( flags & w_parent_reverse ) reverse ( rp . begin ( ) , rp . end ( ) ) ;
2021-08-17 12:18:54 +00:00
}
2021-07-29 12:07:30 +00:00
/** which neighbor will become the parent of c */
2021-08-16 22:24:38 +00:00
EX int get_parent_dir ( tcell * c ) {
2021-07-29 12:07:30 +00:00
if ( c - > parent_dir ! = MYSTERY ) return c - > parent_dir ;
int bestd = - 1 ;
vector < int > bestrootpath ;
2021-08-04 16:35:04 +00:00
look_for_shortcuts ( c ) ;
2021-07-29 12:07:30 +00:00
be_solid ( c ) ;
if ( c - > dist > 0 ) {
auto & sh = arb : : current . shapes [ c - > id ] ;
int n = sh . size ( ) ;
int k = sh . cycle_length ;
2021-08-15 16:06:23 +00:00
vector < int > nearer ;
auto beats = [ & ] ( int i , int old ) {
if ( old = = - 1 ) return true ;
if ( i % k ! = old % k ) return i % k < old % k ;
2021-08-16 22:24:38 +00:00
if ( old < i ) old + = n ;
2021-08-17 12:18:05 +00:00
return old < = i + n / 2 ;
2021-08-15 16:06:23 +00:00
} ;
int d = c - > dist ;
for ( int i = 0 ; i < n ; i + + ) {
tcell * c1 = c - > cmove ( i ) ;
be_solid ( c1 ) ;
2021-08-18 23:25:18 +00:00
if ( parent_debug ) println ( hlog , " direction = " , i , " is " , c1 , " distance = " , c1 - > dist ) ;
2021-08-15 16:06:23 +00:00
if ( c1 - > dist < d ) nearer . push_back ( i ) ;
}
2021-08-17 12:20:54 +00:00
if ( parent_debug ) println ( hlog , " nearer = " , nearer , " n= " , n , " k= " , k ) ;
2021-08-16 22:24:38 +00:00
auto oc = c ;
ufindc ( c ) ; if ( d ! = c - > dist | | oc ! = c ) {
return get_parent_dir ( c ) ;
}
2021-08-15 16:06:23 +00:00
2021-08-21 22:10:02 +00:00
bool failed = false ;
if ( flags & w_parent_always ) { failed = true ; goto resolve ; }
2021-08-15 16:06:23 +00:00
// celebrity identification problem
for ( auto ne : nearer )
if ( beats ( ne , bestd ) )
bestd = ne ;
2021-08-17 12:20:54 +00:00
if ( parent_debug ) for ( auto ne : nearer ) println ( hlog , " beats " , tie ( ne , bestd ) , " = " , beats ( ne , bestd ) ) ;
2021-08-15 16:06:23 +00:00
for ( auto ne : nearer )
2021-08-21 22:10:02 +00:00
if ( ne ! = bestd & & beats ( ne , bestd ) )
failed = true ;
2021-08-17 12:18:54 +00:00
2021-08-21 22:10:02 +00:00
if ( failed ) {
2021-08-17 12:18:54 +00:00
2021-08-21 22:10:02 +00:00
if ( flags & w_parent_never ) {
2021-08-16 22:24:38 +00:00
debuglist = { c } ;
2021-08-15 16:06:23 +00:00
throw rulegen_failure ( " still confused " ) ;
2021-08-16 22:24:38 +00:00
}
2021-08-15 16:06:23 +00:00
2021-08-21 22:10:02 +00:00
resolve :
hard_parents + + ;
vector < int > best ;
int bestfor = nearer [ 0 ] ;
trace_root_path ( best , twalker ( c , nearer [ 0 ] ) ) ;
for ( auto ne1 : nearer ) {
vector < int > other ;
trace_root_path ( other , twalker ( c , ne1 ) ) ;
if ( other < best ) best = other , bestfor = ne1 ;
}
bestd = bestfor ;
}
2021-08-15 16:06:23 +00:00
if ( bestd = = - 1 ) {
throw rulegen_failure ( " should not happen " ) ;
2021-07-29 12:07:30 +00:00
}
}
2021-08-18 23:25:18 +00:00
if ( parent_debug ) println ( hlog , " set parent_dir to " , bestd ) ;
2021-07-29 12:07:30 +00:00
c - > parent_dir = bestd ;
return bestd ;
}
/** determine states for tcells */
# if HDR
using aid_t = pair < int , int > ;
struct analyzer {
vector < twalker > spread ;
vector < int > parent_id ;
vector < int > spin ;
2021-08-04 16:35:04 +00:00
void add_step ( int pid , int s ) ;
2021-07-29 12:07:30 +00:00
} ;
# endif
2021-08-04 16:35:04 +00:00
void analyzer : : add_step ( int pid , int s ) {
twalker cw = spread [ pid ] ;
cw = cw + s ;
cw . peek ( ) ;
ufind ( cw ) ;
cw = cw + wstep ;
spread . push_back ( cw ) ;
parent_id . push_back ( pid ) ;
spin . push_back ( s ) ;
}
2021-08-05 09:56:49 +00:00
EX map < aid_t , analyzer > analyzers ;
2021-07-29 12:07:30 +00:00
EX aid_t get_aid ( twalker cw ) {
ufind ( cw ) ;
auto ide = cw . at - > id ;
return { ide , gmod ( cw . to_spin ( 0 ) , arb : : current . shapes [ ide ] . cycle_length ) } ;
}
EX analyzer & get_analyzer ( twalker cw ) {
auto aid = get_aid ( cw ) ;
auto & a = analyzers [ aid ] ;
if ( a . spread . empty ( ) ) {
a . spread . push_back ( cw ) ;
a . parent_id . push_back ( - 1 ) ;
a . spin . push_back ( - 1 ) ;
for ( int i = 0 ; i < cw . at - > type ; i + + )
a . add_step ( 0 , i ) ;
}
return a ;
}
EX vector < twalker > spread ( analyzer & a , twalker cw ) {
vector < twalker > res ;
int N = isize ( a . spread ) ;
res . reserve ( N ) ;
res . push_back ( cw ) ;
2021-08-04 16:35:04 +00:00
for ( int i = 1 ; i < N ; i + + ) {
auto & r = res [ a . parent_id [ i ] ] ;
ufind ( r ) ;
auto r1 = r + a . spin [ i ] ;
r1 . peek ( ) ; ufind ( r1 ) ;
res . push_back ( r1 + wstep ) ;
}
2021-07-29 12:07:30 +00:00
return res ;
}
void extend_analyzer ( twalker cw_target , int dir , int id , int mism , twalker rg ) {
2021-08-17 12:25:20 +00:00
ufind ( cw_target ) ; ufind ( rg ) ;
2021-07-29 12:07:30 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " extend called, cw_target = " , cw_target ) ;
twalker cw_conflict = cw_target + dir + wstep ;
auto & a_target = get_analyzer ( cw_target ) ;
auto & a_conflict = get_analyzer ( cw_conflict ) ;
2021-08-05 09:56:49 +00:00
// twalker model = a_target.spread[0] + dir + wstep;
// auto res = spread(a_conflict, model);
2021-07-29 12:07:30 +00:00
vector < int > ids_to_add ;
int k = id ;
while ( k ) {
ids_to_add . emplace_back ( a_conflict . spin [ k ] ) ;
k = a_conflict . parent_id [ k ] ;
}
int gid = 1 + dir ;
bool added = false ;
while ( ! ids_to_add . empty ( ) ) {
int spin = ids_to_add . back ( ) ;
ids_to_add . pop_back ( ) ;
int next_gid = - 1 ;
for ( int i = 0 ; i < isize ( a_target . parent_id ) ; i + + )
if ( a_target . parent_id [ i ] = = gid & & a_target . spin [ i ] = = spin ) {
next_gid = i ;
}
if ( next_gid = = - 1 ) {
next_gid = isize ( a_target . parent_id ) ;
a_target . add_step ( gid , spin ) ;
added = true ;
}
gid = next_gid ;
}
2021-08-17 12:24:06 +00:00
if ( mism = = 0 & & ! added )
/* in rare cases this happens due to unification or something */
throw rulegen_retry ( " no extension " ) ;
2021-07-29 12:07:30 +00:00
}
# if HDR
using code_t = pair < aid_t , vector < int > > ;
struct treestate {
int id ;
bool known ;
vector < int > rules ;
twalker giver ;
int sid ;
int parent_dir ;
tcell * where_seen ;
code_t code ;
bool is_live ;
bool is_possible_parent ;
2021-07-30 13:18:32 +00:00
bool is_root ;
2021-07-29 12:07:30 +00:00
vector < pair < int , int > > possible_parents ;
} ;
static const int C_IGNORE = 0 ;
static const int C_CHILD = 1 ;
static const int C_UNCLE = 2 ;
static const int C_EQUAL = 4 ;
static const int C_NEPHEW = 6 ;
static const int C_PARENT = 8 ;
# endif
EX vector < treestate > treestates ;
2021-08-17 00:32:03 +00:00
EX set < tcell * > single_live_branch_close_to_root ;
2021-07-29 12:07:30 +00:00
/** is what on the left side, or the right side, of to_what? */
2021-08-17 15:04:17 +00:00
void treewalk ( twalker & cw , int delta ) {
int d = get_parent_dir ( cw . at ) ;
2021-08-17 21:05:56 +00:00
if ( cw . spin = = d ) cw + = wstep ;
else {
2021-08-18 23:27:26 +00:00
auto cw1 = addstep ( cw ) ;
2021-08-17 21:05:56 +00:00
get_parent_dir ( cw1 . at ) ;
ufind ( cw1 ) ;
if ( get_parent_dir ( cw1 . at ) = = cw1 . spin ) cw + = wstep ;
}
2021-08-17 15:04:17 +00:00
cw + = delta ;
}
2021-08-18 23:24:01 +00:00
EX std : : map < twalker , int > sidecache ;
2021-08-17 12:24:32 +00:00
int get_side ( twalker what ) {
2021-08-21 22:10:02 +00:00
bool side = ! ( flags & w_no_sidecache ) ;
bool fast = ! ( flags & w_slow_side ) ;
if ( side ) {
auto ww = at_or_null ( sidecache , what ) ;
if ( ww ) return * ww ;
}
int res = 99 ;
2021-08-17 12:24:32 +00:00
int steps = 0 ;
2021-08-21 22:10:02 +00:00
if ( fast ) {
twalker w = what ;
twalker tw = what + wstep ;
auto adv = [ ] ( twalker & cw ) {
int d = get_parent_dir ( cw . at ) ;
ufind ( cw ) ;
if ( cw . at - > move ( d ) - > dist > = cw . at - > dist ) {
handle_distance_errors ( ) ;
if ( debugflags & DF_GEOM )
println ( hlog , " get_parent_dir error at " , cw , " and " , cw . at - > move ( d ) , " : " , cw . at - > dist , " :: " , cw . at - > move ( d ) - > dist ) ;
throw rulegen_failure ( " get_parent_dir error " ) ;
}
cw . spin = d ;
cw + = wstep ;
} ;
while ( w . at ! = tw . at ) {
steps + + ; if ( steps > max_getside ) {
debuglist = { what , w , tw } ;
throw rulegen_failure ( " qsidefreeze " ) ;
}
ufind ( w ) ; ufind ( tw ) ;
if ( w . at - > dist > tw . at - > dist )
adv ( w ) ;
else if ( w . at - > dist < tw . at - > dist )
adv ( tw ) ;
else {
adv ( w ) ; adv ( tw ) ;
}
2021-07-29 12:07:30 +00:00
}
2021-08-21 22:10:02 +00:00
int d = get_parent_dir ( w . at ) ;
2021-08-17 00:32:03 +00:00
2021-08-21 22:10:02 +00:00
if ( d > = 0 & & ! single_live_branch_close_to_root . count ( w . at ) ) {
twalker last ( w . at , d ) ;
res = last . to_spin ( w . spin ) - last . to_spin ( tw . spin ) ;
}
2021-07-29 12:07:30 +00:00
}
// failed to solve this in the simple way (ended at the root) -- go around the tree
2021-08-17 12:24:32 +00:00
twalker wl = what ;
2021-07-29 12:07:30 +00:00
twalker wr = wl ;
2021-08-17 12:24:32 +00:00
auto to_what = what + wstep ;
2021-08-21 22:10:02 +00:00
auto ws = what ; treewalk ( ws , 0 ) ; if ( ws = = to_what ) res = 0 ;
2021-08-17 12:24:32 +00:00
2021-08-21 22:10:02 +00:00
while ( res = = 99 ) {
2021-08-18 23:24:01 +00:00
handle_distance_errors ( ) ;
2021-08-19 22:48:09 +00:00
steps + + ; if ( steps > max_getside ) {
2021-08-18 23:24:01 +00:00
debuglist = { what , to_what , wl , wr } ;
throw rulegen_failure ( " xsidefreeze " ) ;
2021-08-17 12:24:32 +00:00
}
2021-08-19 22:44:24 +00:00
bool gl = wl . at - > dist < = wr . at - > dist ;
bool gr = wl . at - > dist > = wr . at - > dist ;
if ( gl ) {
treewalk ( wl , - 1 ) ;
2021-08-21 22:10:02 +00:00
if ( wl = = to_what ) { res = 1 ; }
2021-08-19 22:44:24 +00:00
}
if ( gr ) {
treewalk ( wr , + 1 ) ;
2021-08-21 22:10:02 +00:00
if ( wr = = to_what ) { res = - 1 ; }
2021-08-19 22:44:24 +00:00
}
2021-07-29 12:07:30 +00:00
}
2021-08-21 22:10:02 +00:00
if ( side ) sidecache [ what ] = res ;
return res ;
2021-07-29 12:07:30 +00:00
}
code_t id_at_spin ( twalker cw ) {
code_t res ;
ufind ( cw ) ;
res . first = get_aid ( cw ) ;
auto & a = get_analyzer ( cw ) ;
vector < twalker > sprawl = spread ( a , cw ) ;
int id = 0 ;
for ( auto cs : sprawl ) {
be_solid ( cs . at ) ;
be_solid ( cw . at ) ;
ufind ( cw ) ;
ufind ( cs ) ;
int x ;
int pid = a . parent_id [ id ] ;
if ( pid > - 1 & & ( res . second [ pid ] ! = C_CHILD ) ) {
x = C_IGNORE ;
}
2021-08-17 12:24:58 +00:00
else if ( id = = 0 ) x = C_CHILD ;
2021-07-29 12:07:30 +00:00
else {
int p = get_parent_dir ( cs . at ) ;
if ( p > = 0 & & get_parent_dir ( cs . at ) = = cs . spin )
x = C_CHILD ;
else {
2021-08-04 16:35:04 +00:00
auto cs2 = cs + wstep ;
ufind ( cs ) ; ufind ( cs2 ) ; be_solid ( cs2 . at ) ;
fix_distances ( cs . at ) ;
2021-07-29 12:07:30 +00:00
int y = cs . at - > dist - cs . peek ( ) - > dist ;
2021-08-21 22:10:02 +00:00
if ( ! ( flags & w_no_relative_distance ) ) x = C_EQUAL ;
else if ( y = = 1 ) x = C_NEPHEW ;
2021-07-29 12:07:30 +00:00
else if ( y = = 0 ) x = C_EQUAL ;
else if ( y = = - 1 ) x = C_UNCLE ;
2021-08-04 16:35:04 +00:00
else throw rulegen_failure ( " distance problem y= " + its ( y ) + lalign ( 0 , " cs= " , cs , " cs2= " , cs2 , " peek= " , cs . peek ( ) , " dist= " , cs . at - > dist , " dist2= " , cs2 . at - > dist ) ) ;
2021-08-17 12:24:32 +00:00
auto gs = get_side ( cs ) ;
2021-07-29 12:07:30 +00:00
if ( gs = = 0 & & x = = C_UNCLE ) x = C_PARENT ;
if ( gs > 0 ) x + + ;
}
}
res . second . push_back ( x ) ;
id + + ;
}
return res ;
}
map < code_t , int > code_to_id ;
EX pair < int , int > get_code ( tcell * c ) {
2021-08-17 18:07:17 +00:00
if ( c - > code ! = MYSTERY & & c - > parent_dir ! = MYSTERY ) {
2021-07-29 12:07:30 +00:00
int bestd = c - > parent_dir ;
if ( bestd = = - 1 ) bestd = 0 ;
return { bestd , c - > code } ;
}
be_solid ( c ) ;
int bestd = get_parent_dir ( c ) ;
if ( bestd = = - 1 ) bestd = 0 ;
indenter ind ( 2 ) ;
code_t v = id_at_spin ( twalker ( c , bestd ) ) ;
if ( code_to_id . count ( v ) ) {
c - > code = code_to_id [ v ] ;
return { bestd , code_to_id [ v ] } ;
}
int id = isize ( treestates ) ;
code_to_id [ v ] = id ;
2021-08-18 23:25:18 +00:00
if ( c - > code ! = MYSTERY & & ( c - > code ! = id | | c - > parent_dir ! = bestd ) ) {
throw rulegen_retry ( " exit from get_code " ) ;
}
2021-07-29 12:07:30 +00:00
c - > code = id ;
treestates . emplace_back ( ) ;
auto & nts = treestates . back ( ) ;
nts . id = id ;
nts . code = v ;
nts . where_seen = c ;
nts . known = false ;
nts . is_live = true ;
return { bestd , id } ;
}
/* == rule generation == */
EX int rule_root ;
vector < int > gen_rule ( twalker cwmain ) ;
EX int try_count ;
2021-08-18 23:25:18 +00:00
EX vector < tcell * > important ;
2021-07-29 12:07:30 +00:00
vector < tcell * > cq ;
# if HDR
/* special codes */
static const int DIR_UNKNOWN = - 1 ;
static const int DIR_LEFT = - 4 ;
static const int DIR_RIGHT = - 5 ;
static const int DIR_PARENT = - 6 ;
# endif
2021-08-19 23:36:14 +00:00
vector < int > gen_rule ( twalker cwmain , int id ) {
2021-07-29 12:07:30 +00:00
vector < int > cids ;
for ( int a = 0 ; a < cwmain . at - > type ; a + + ) {
auto front = cwmain + a ;
tcell * c1 = front . cpeek ( ) ;
be_solid ( c1 ) ;
if ( a = = 0 & & cwmain . at - > dist ) { cids . push_back ( DIR_PARENT ) ; continue ; }
if ( c1 - > dist < = cwmain . at - > dist ) { cids . push_back ( DIR_UNKNOWN ) ; continue ; }
auto co = get_code ( c1 ) ;
auto & d1 = co . first ;
auto & id1 = co . second ;
if ( c1 - > cmove ( d1 ) ! = cwmain . at | | c1 - > c . spin ( d1 ) ! = front . spin ) {
cids . push_back ( DIR_UNKNOWN ) ; continue ;
}
cids . push_back ( id1 ) ;
}
2021-08-19 23:36:14 +00:00
for ( int i = 0 ; i < isize ( cids ) ; i + + ) if ( cids [ i ] = = DIR_UNKNOWN ) {
int val = treestates [ id ] . code . second [ i + 1 ] ;
if ( val < 2 | | val > = 8 ) {
debuglist = { cwmain } ;
2021-08-21 21:40:50 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " i = " , i , " val = " , val , " code = " , treestates [ id ] . code ) ;
2021-08-19 23:36:14 +00:00
throw rulegen_retry ( " wrong code in gen_rule " ) ;
}
cids [ i ] = ( ( val & 1 ) ? DIR_RIGHT : DIR_LEFT ) ;
}
2021-07-29 12:07:30 +00:00
return cids ;
}
void rules_iteration_for ( tcell * c ) {
indenter ri ( 2 ) ;
2021-08-19 23:36:14 +00:00
ufindc ( c ) ;
2021-07-29 12:07:30 +00:00
auto co = get_code ( c ) ;
auto & d = co . first ;
auto & id = co . second ;
twalker cwmain ( c , d ) ;
ufind ( cwmain ) ;
2021-08-19 23:36:14 +00:00
vector < int > cids = gen_rule ( cwmain , id ) ;
2021-07-29 12:07:30 +00:00
auto & ts = treestates [ id ] ;
if ( ! ts . known ) {
ts . known = true ;
ts . rules = cids ;
ts . giver = cwmain ;
ts . sid = cwmain . at - > id ;
ts . parent_dir = cwmain . spin ;
2021-07-30 13:18:32 +00:00
ts . is_root = c - > dist = = 0 ;
2021-07-29 12:07:30 +00:00
}
else if ( ts . rules ! = cids ) {
2021-08-04 16:35:04 +00:00
handle_distance_errors ( ) ;
2021-07-29 12:07:30 +00:00
auto & r = ts . rules ;
if ( debugflags & DF_GEOM ) {
println ( hlog , " merging " , ts . rules , " vs " , cids ) ;
println ( hlog , " C " , treestates [ id ] . code , " [ " , id , " ] " ) ;
}
int mismatches = 0 ;
for ( int z = 0 ; z < isize ( cids ) ; z + + ) {
if ( r [ z ] = = cids [ z ] ) continue ;
2021-08-19 23:36:14 +00:00
if ( r [ z ] < 0 | | cids [ z ] < 0 ) {
debuglist = { cwmain , ts . giver } ;
2021-07-29 12:07:30 +00:00
throw rulegen_failure ( " neg rule mismatch " ) ;
2021-08-19 23:36:14 +00:00
}
2021-07-29 12:07:30 +00:00
auto & c1 = treestates [ r [ z ] ] . code . second ;
auto & c2 = treestates [ cids [ z ] ] . code . second ;
if ( debugflags & DF_GEOM ) {
println ( hlog , " direction " , z , " : " ) ;
println ( hlog , " A " , treestates [ r [ z ] ] . code , " [ " , r [ z ] , " ] " ) ;
println ( hlog , " B " , treestates [ cids [ z ] ] . code , " [ " , cids [ z ] , " ] " ) ;
}
if ( isize ( c1 ) ! = isize ( c2 ) ) {
throw rulegen_failure ( " length mismatch " ) ;
}
for ( int k = 0 ; k < isize ( c1 ) ; k + + ) {
if ( c1 [ k ] = = C_IGNORE | | c2 [ k ] = = C_IGNORE ) continue ;
if ( c1 [ k ] ! = c2 [ k ] ) {
if ( debugflags & DF_GEOM ) {
println ( hlog , " code mismatch ( " , c1 [ k ] , " vs " , c2 [ k ] , " at position " , k , " out of " , isize ( c1 ) , " ) " ) ;
println ( hlog , " rulegiver = " , treestates [ id ] . giver , " c = " , cwmain ) ;
println ( hlog , " gshvid = " , c - > id ) ;
println ( hlog , " cellcount = " , tcellcount , " - " , tunified , " codes discovered = " , isize ( treestates ) ) ;
}
extend_analyzer ( cwmain , z , k , mismatches , treestates [ id ] . giver ) ;
mismatches + + ;
2021-08-21 22:10:02 +00:00
if ( ! ( flags & w_conflict_all ) )
throw rulegen_retry ( " mismatch error " ) ;
2021-07-29 12:07:30 +00:00
}
}
}
debuglist = { cwmain , ts . giver } ;
if ( mismatches )
2021-08-19 11:19:01 +00:00
throw rulegen_retry ( " mismatch error " ) ;
2021-07-29 12:07:30 +00:00
throw rulegen_failure ( " no mismatches?! " ) ;
}
}
void minimize_rules ( ) {
2021-08-21 22:10:14 +00:00
states_premini = isize ( treestates ) ;
2021-07-29 12:07:30 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " minimizing rules... " ) ;
int next_id = isize ( treestates ) ;
vector < int > new_id ( next_id ) ;
map < aid_t , int > new_id_of ;
int new_ids = 0 ;
for ( int id = 0 ; id < next_id ; id + + ) {
auto aid = get_aid ( treestates [ id ] . giver ) ;
if ( ! new_id_of . count ( aid ) ) new_id_of [ aid ] = new_ids + + ;
new_id [ id ] = new_id_of [ aid ] ;
}
int last_new_ids = 0 ;
while ( new_ids > last_new_ids & & new_ids < next_id ) {
last_new_ids = new_ids ;
map < vector < int > , int > hashes ;
new_ids = 0 ;
auto last_new_id = new_id ;
for ( int id = 0 ; id < next_id ; id + + ) {
vector < int > hash ;
hash . push_back ( last_new_id [ id ] ) ;
auto & ts = treestates [ id ] ;
for ( auto & r : ts . rules )
if ( r > = 0 ) hash . push_back ( last_new_id [ r ] ) ;
else hash . push_back ( r ) ;
if ( ! hashes . count ( hash ) )
hashes [ hash ] = new_ids + + ;
new_id [ id ] = hashes [ hash ] ;
}
}
if ( debugflags & DF_GEOM )
println ( hlog , " final new_ids = " , new_ids , " / " , next_id ) ;
if ( 1 ) {
vector < int > old_id ( new_ids , - 1 ) ;
for ( int i = 0 ; i < next_id ; i + + ) if ( old_id [ new_id [ i ] ] = = - 1 ) old_id [ new_id [ i ] ] = i ;
for ( int i = 0 ; i < new_ids ; i + + ) treestates [ i ] = treestates [ old_id [ i ] ] ;
2021-07-30 01:50:29 +00:00
for ( int i = 0 ; i < new_ids ; i + + ) treestates [ i ] . id = i ;
2021-07-29 12:07:30 +00:00
treestates . resize ( new_ids ) ;
for ( auto & ts : treestates ) {
for ( auto & r : ts . rules )
if ( r > = 0 ) r = new_id [ r ] ;
}
for ( auto & p : code_to_id ) p . second = new_id [ p . second ] ;
}
}
void find_possible_parents ( ) {
for ( auto & ts : treestates ) {
ts . is_possible_parent = false ;
for ( int r : ts . rules )
if ( r = = DIR_PARENT )
ts . is_possible_parent = true ;
}
while ( true ) {
int changes = 0 ;
for ( auto & ts : treestates ) ts . possible_parents . clear ( ) ;
for ( auto & ts : treestates )
if ( ts . is_possible_parent ) {
int rid = 0 ;
for ( int r : ts . rules ) {
if ( r > = 0 ) treestates [ r ] . possible_parents . emplace_back ( ts . id , rid ) ;
rid + + ;
}
}
for ( auto & ts : treestates )
if ( ts . is_possible_parent & & ts . possible_parents . empty ( ) ) {
ts . is_possible_parent = false ;
changes + + ;
}
if ( ! changes ) break ;
}
int pp = 0 ;
for ( auto & ts : treestates ) if ( ts . is_possible_parent ) pp + + ;
if ( debugflags & DF_GEOM )
println ( hlog , pp , " of " , isize ( treestates ) , " states are possible_parents " ) ;
}
/* == branch testing == */
2021-08-17 15:04:17 +00:00
using tsinfo = pair < int , int > ;
tsinfo get_tsinfo ( twalker tw ) {
auto co = get_code ( tw . at ) ;
int spin ;
if ( co . first = = - 1 ) spin = tw . spin ;
else spin = gmod ( tw . spin - co . first , tw . at - > type ) ;
return { co . second , spin } ;
}
2021-08-22 12:31:13 +00:00
int get_rule ( const twalker tw , tsinfo s ) {
auto & r = treestates [ s . first ] . rules ;
if ( r . empty ( ) ) {
important . push_back ( tw . at ) ;
throw rulegen_retry ( " unknown rule in get_rule " ) ;
}
return r [ s . second ] ;
2021-08-17 15:04:17 +00:00
}
set < vector < tsinfo > > verified_branches ;
void push_deadstack ( vector < tsinfo > & hash , twalker w , tsinfo tsi , int dir ) {
hash . push_back ( tsi ) ;
2021-07-29 12:07:30 +00:00
while ( true ) {
2021-08-17 15:24:11 +00:00
ufind ( w ) ;
if ( isize ( hash ) > 10000 ) throw rulegen_failure ( " deadstack overflow " ) ;
2021-08-17 15:04:17 +00:00
tsi . second + = dir ; w + = dir ;
auto & ts = treestates [ tsi . first ] ;
2021-08-17 15:24:11 +00:00
if ( ts . is_root ) return ;
2021-08-17 15:04:17 +00:00
if ( tsi . second = = 0 | | tsi . second = = isize ( ts . rules ) ) {
w + = wstep ;
tsi = get_tsinfo ( w ) ;
hash . push_back ( tsi ) ;
2021-07-29 12:07:30 +00:00
}
else {
2021-08-17 15:04:17 +00:00
int r = ts . rules [ tsi . second ] ;
if ( r > 0 & & treestates [ r ] . is_live ) return ;
2021-07-29 12:07:30 +00:00
}
}
}
2021-08-19 11:18:38 +00:00
struct verify_advance_failed : hr_exception { } ;
2021-08-23 13:49:48 +00:00
using conflict_id_type = pair < pair < int , int > , pair < int , int > > ;
set < conflict_id_type > branch_conflicts_seen ;
2021-08-17 15:04:17 +00:00
void verified_treewalk ( twalker & tw , int id , int dir ) {
if ( id > = 0 ) {
auto co = get_code ( tw . cpeek ( ) ) ;
if ( co . second ! = id | | co . first ! = ( tw + wstep ) . spin ) {
2021-08-18 23:27:56 +00:00
handle_distance_errors ( ) ;
2021-08-21 22:10:02 +00:00
2021-08-23 13:49:48 +00:00
conflict_id_type conflict_id = make_pair ( make_pair ( ( tw + wstep ) . spin , id ) , co ) ;
if ( ( flags & w_examine_all ) | | ! branch_conflicts_seen . count ( conflict_id ) ) {
branch_conflicts_seen . insert ( conflict_id ) ;
2021-08-19 22:46:22 +00:00
important . push_back ( tw . at ) ;
if ( debugflags & DF_GEOM )
2021-08-23 13:49:48 +00:00
println ( hlog , " branch conflict " , conflict_id , " found " ) ;
2021-08-19 22:46:22 +00:00
}
2021-08-21 21:40:50 +00:00
else if ( debugflags & DF_GEOM )
2021-08-23 13:49:48 +00:00
println ( hlog , " branch conflict " , conflict_id , " found again " ) ;
2021-08-17 15:04:17 +00:00
debuglist = { tw , tw + wstep } ;
2021-08-19 11:18:38 +00:00
throw verify_advance_failed ( ) ;
2021-08-15 19:01:47 +00:00
}
2021-07-29 12:07:30 +00:00
}
2021-08-17 15:04:17 +00:00
treewalk ( tw , dir ) ;
2021-07-29 12:07:30 +00:00
}
void examine_branch ( int id , int left , int right ) {
auto rg = treestates [ id ] . giver ;
2021-08-17 15:04:17 +00:00
2021-07-29 12:07:30 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " need to examine branches " , tie ( left , right ) , " of " , id , " starting from " , rg ) ;
2021-08-17 12:28:27 +00:00
indenter ind ( 2 ) ;
2021-08-17 15:04:17 +00:00
auto wl = rg + left ;
auto wr = rg + left + 1 ;
vector < twalker > lstack , rstack ;
2021-07-29 12:07:30 +00:00
int steps = 0 ;
2021-08-19 11:18:38 +00:00
try {
2021-07-29 12:07:30 +00:00
while ( true ) {
2021-08-18 23:27:56 +00:00
handle_distance_errors ( ) ;
2021-07-29 12:07:30 +00:00
steps + + ;
2021-08-17 15:04:17 +00:00
if ( steps > max_examine_branch ) {
debuglist = { rg + left , wl , wr } ;
2021-07-29 12:07:30 +00:00
throw rulegen_failure ( " max_examine_branch exceeded " ) ;
2021-08-15 19:01:47 +00:00
}
2021-07-29 12:07:30 +00:00
2021-08-17 15:04:17 +00:00
auto tsl = get_tsinfo ( wl ) ;
auto tsr = get_tsinfo ( wr ) ;
2021-08-22 12:31:13 +00:00
auto rl = get_rule ( wl , tsl ) ;
auto rr = get_rule ( wr , tsr ) ;
2021-08-17 15:04:17 +00:00
// println(hlog, "wl = ", wl, " -> ", wl+wstep, " R", rl, " wr = ", wr, " -> ", wr+wstep, " R", rr, " lstack = ", lstack, " rstack = ", rstack);
if ( rl = = DIR_RIGHT & & rr = = DIR_LEFT & & lstack . empty ( ) & & rstack . empty ( ) ) {
vector < tsinfo > hash ;
push_deadstack ( hash , wl , tsl , - 1 ) ;
hash . emplace_back ( - 1 , - 1 ) ;
push_deadstack ( hash , wr , tsr , + 1 ) ;
// println(hlog, "hash = ", hash);
if ( verified_branches . count ( hash ) ) return ;
verified_branches . insert ( hash ) ;
verified_treewalk ( wl , rl , - 1 ) ;
verified_treewalk ( wr , rr , + 1 ) ;
2021-08-15 19:01:47 +00:00
}
2021-07-29 12:07:30 +00:00
2021-08-17 15:04:17 +00:00
else if ( rl = = DIR_RIGHT & & ! lstack . empty ( ) & & lstack . back ( ) = = wl + wstep ) {
lstack . pop_back ( ) ;
verified_treewalk ( wl , rl , - 1 ) ;
2021-07-29 12:07:30 +00:00
}
2021-08-17 15:04:17 +00:00
else if ( rr = = DIR_LEFT & & ! rstack . empty ( ) & & rstack . back ( ) = = wr + wstep ) {
rstack . pop_back ( ) ;
verified_treewalk ( wr , rr , + 1 ) ;
2021-07-29 12:07:30 +00:00
}
2021-08-17 15:04:17 +00:00
else if ( rl = = DIR_LEFT ) {
lstack . push_back ( wl ) ;
verified_treewalk ( wl , rl , - 1 ) ;
}
else if ( rr = = DIR_RIGHT ) {
rstack . push_back ( wr ) ;
verified_treewalk ( wr , rr , + 1 ) ;
}
else if ( rl ! = DIR_RIGHT )
verified_treewalk ( wl , rl , - 1 ) ;
else if ( rr ! = DIR_RIGHT )
verified_treewalk ( wr , rr , + 1 ) ;
else throw rulegen_failure ( " cannot advance while examining " ) ;
2021-07-29 12:07:30 +00:00
}
2021-08-19 11:18:38 +00:00
}
2021-08-21 22:10:02 +00:00
catch ( verify_advance_failed & ) {
if ( flags & w_examine_once ) throw rulegen_retry ( " advance failed " ) ;
}
2021-07-29 12:07:30 +00:00
}
/* == main algorithm == */
void clear_codes ( ) {
treestates . clear ( ) ;
code_to_id . clear ( ) ;
auto c = first_tcell ;
while ( c ) {
c - > code = MYSTERY ;
c = c - > next ;
}
}
2021-08-17 00:32:03 +00:00
void find_single_live_branch ( twalker at ) {
handle_distance_errors ( ) ;
2021-08-19 23:36:14 +00:00
rules_iteration_for ( at . at ) ;
2021-08-17 00:32:03 +00:00
int id = get_code ( at . at ) . second ;
int t = at . at - > type ;
2021-08-22 12:31:13 +00:00
auto r = treestates [ id ] . rules ; /* no & because may move */
2021-08-17 00:32:03 +00:00
int q = 0 ;
2021-08-19 11:19:01 +00:00
if ( r . empty ( ) ) { important . push_back ( at . at ) ; throw rulegen_retry ( " no giver in find_single_live_branch " ) ; }
2021-08-17 00:32:03 +00:00
for ( int i = 0 ; i < t ; i + + ) if ( r [ i ] > = 0 ) {
if ( treestates [ r [ i ] ] . is_live ) q + + ;
}
for ( int i = 0 ; i < t ; i + + ) if ( r [ i ] > = 0 ) {
single_live_branch_close_to_root . insert ( at . at ) ;
if ( ! treestates [ r [ i ] ] . is_live | | q = = 1 )
find_single_live_branch ( at + i + wstep ) ;
}
}
2021-08-19 11:19:11 +00:00
EX void clean_data ( ) {
analyzers . clear ( ) ;
important = t_origin ;
}
EX void clean_parents ( ) {
clean_data ( ) ;
sidecache . clear ( ) ;
auto c = first_tcell ;
while ( c ) { c - > parent_dir = MYSTERY ; c = c - > next ; }
}
2021-08-18 23:21:44 +00:00
EX void rules_iteration ( ) {
try_count + + ;
2021-08-21 22:10:02 +00:00
if ( ( try_count & ( try_count - 1 ) ) = = 0 ) if ( ! ( flags & w_no_restart ) ) {
2021-08-19 11:19:11 +00:00
clean_data ( ) ;
2021-08-18 23:21:44 +00:00
}
if ( debugflags & DF_GEOM ) println ( hlog , " attempt: " , try_count ) ;
auto c = first_tcell ;
while ( c ) { c - > code = MYSTERY ; c = c - > next ; }
2021-07-29 12:07:30 +00:00
clear_codes ( ) ;
cq = important ;
if ( debugflags & DF_GEOM )
println ( hlog , " important = " , cq ) ;
for ( int i = 0 ; i < isize ( cq ) ; i + + ) {
rules_iteration_for ( cq [ i ] ) ;
}
2021-08-04 16:35:04 +00:00
handle_distance_errors ( ) ;
2021-07-29 12:07:30 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " number of treestates = " , isize ( treestates ) ) ;
2021-07-30 13:18:32 +00:00
rule_root = get_code ( t_origin [ 0 ] ) . second ;
2021-07-29 12:07:30 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " rule_root = " , rule_root ) ;
for ( int id = 0 ; id < isize ( treestates ) ; id + + ) {
if ( ! treestates [ id ] . known ) {
2021-08-19 11:17:56 +00:00
auto ws = treestates [ id ] . where_seen ;
rules_iteration_for ( ws ) ;
2021-07-29 12:07:30 +00:00
continue ;
}
}
2021-08-19 11:17:56 +00:00
int N = isize ( important ) ;
2021-07-29 12:07:30 +00:00
int new_deadends = - 1 ;
while ( new_deadends ) {
new_deadends = 0 ;
for ( int id = 0 ; id < isize ( treestates ) ; id + + ) {
auto & ts = treestates [ id ] ;
if ( ! ts . known ) continue ;
if ( ! ts . is_live ) continue ;
int children = 0 ;
for ( int i : ts . rules ) if ( i > = 0 & & treestates [ i ] . is_live ) children + + ;
if ( ! children )
treestates [ id ] . is_live = false , new_deadends + + ;
}
if ( debugflags & DF_GEOM )
println ( hlog , " deadend states found: " , new_deadends ) ;
}
// print_rules();
2021-08-04 16:35:04 +00:00
handle_distance_errors ( ) ;
2021-08-17 15:04:17 +00:00
verified_branches . clear ( ) ;
2021-07-29 12:07:30 +00:00
2021-08-17 00:32:03 +00:00
int q = isize ( single_live_branch_close_to_root ) ;
2021-08-21 21:38:36 +00:00
single_live_branches = 0 ;
double_live_branches = 0 ;
2021-08-17 00:32:03 +00:00
2021-08-23 13:49:48 +00:00
branch_conflicts_seen . clear ( ) ;
2021-08-23 13:50:08 +00:00
// handle dead roots -- some of their branches MUST live
for ( int id = 0 ; id < isize ( treestates ) ; id + + ) if ( treestates [ id ] . is_root & & ! treestates [ id ] . is_live ) {
auto r = treestates [ id ] . rules ;
for ( int i = 0 ; i < isize ( r ) ; i + + ) if ( r [ i ] > = 0 ) {
examine_branch ( id , i , i ) ;
break ;
}
}
2021-07-29 12:07:30 +00:00
for ( int id = 0 ; id < isize ( treestates ) ; id + + ) if ( treestates [ id ] . is_live ) {
2021-08-19 11:18:38 +00:00
auto r = treestates [ id ] . rules ; /* no & because treestates might have moved */
if ( r . empty ( ) ) continue ;
2021-07-29 12:07:30 +00:00
int last_live_branch = - 1 ;
int first_live_branch = - 1 ;
2021-08-21 21:38:36 +00:00
int qbranches = 0 ;
2021-07-29 12:07:30 +00:00
for ( int i = 0 ; i < isize ( r ) ; i + + )
if ( r [ i ] > = 0 & & treestates [ r [ i ] ] . is_live ) {
if ( first_live_branch = = - 1 ) first_live_branch = i ;
if ( last_live_branch > = 0 )
examine_branch ( id , last_live_branch , i ) ;
last_live_branch = i ;
2021-08-21 21:38:36 +00:00
qbranches + + ;
2021-07-29 12:07:30 +00:00
}
2021-08-21 21:38:36 +00:00
if ( qbranches = = 2 ) double_live_branches + + ;
2021-08-17 00:32:03 +00:00
if ( first_live_branch = = last_live_branch & & treestates [ id ] . is_root ) {
2021-08-21 21:38:36 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " for id " , id , " we have a single live branch " ) ;
single_live_branches + + ;
2021-08-17 00:32:03 +00:00
indenter ind ( 2 ) ;
2021-08-19 23:36:14 +00:00
debuglist = { treestates [ id ] . giver } ;
2021-08-17 00:32:03 +00:00
find_single_live_branch ( treestates [ id ] . giver ) ;
}
if ( isize ( single_live_branch_close_to_root ) ! = q ) {
vector < tcell * > v ;
for ( auto c : single_live_branch_close_to_root ) v . push_back ( c ) ;
2021-08-21 21:38:36 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " changed single_live_branch_close_to_root from " , q , " to " , v ) ;
2021-08-17 00:32:03 +00:00
debuglist = { treestates [ id ] . giver } ;
throw rulegen_retry ( " single live branch " ) ;
}
2021-08-04 16:35:37 +00:00
if ( treestates [ id ] . is_root ) examine_branch ( id , last_live_branch , first_live_branch ) ;
2021-07-29 12:07:30 +00:00
}
2021-08-22 12:31:13 +00:00
for ( int id = 0 ; id < isize ( treestates ) ; id + + ) if ( ! treestates [ id ] . giver . at ) {
important . push_back ( treestates [ id ] . where_seen ) ;
}
2021-07-29 12:07:30 +00:00
2021-08-04 16:35:04 +00:00
handle_distance_errors ( ) ;
2021-08-19 11:19:01 +00:00
if ( isize ( important ) ! = N )
throw rulegen_retry ( " need more rules after examine " ) ;
2021-07-29 12:07:30 +00:00
minimize_rules ( ) ;
find_possible_parents ( ) ;
2021-08-15 15:43:13 +00:00
if ( isize ( important ) ! = N )
2021-08-19 11:19:01 +00:00
throw rulegen_retry ( " need more rules after minimize " ) ;
2021-08-04 16:35:04 +00:00
handle_distance_errors ( ) ;
2021-07-29 12:07:30 +00:00
}
void clear_tcell_data ( ) {
auto c = first_tcell ;
while ( c ) {
c - > is_solid = false ;
2021-08-04 16:35:04 +00:00
// c->dist = MYSTERY;
2021-07-29 12:07:30 +00:00
c - > parent_dir = MYSTERY ;
c - > code = MYSTERY ;
c - > distance_fixed = false ;
c = c - > next ;
}
2021-08-15 15:43:13 +00:00
in_fixing = false ; fix_queue = { } ;
2021-07-29 12:07:30 +00:00
}
void cleanup ( ) {
clear_tcell_data ( ) ;
analyzers . clear ( ) ;
code_to_id . clear ( ) ;
important . clear ( ) ;
2021-08-04 16:35:04 +00:00
shortcuts . clear ( ) ;
2021-08-17 15:43:24 +00:00
single_live_branch_close_to_root . clear ( ) ;
2021-07-29 12:07:30 +00:00
}
void clear_all ( ) {
treestates . clear ( ) ;
cleanup ( ) ;
}
EX void generate_rules ( ) {
2021-08-22 12:31:23 +00:00
auto t = SDL_GetTicks ( ) ;
2021-07-29 12:07:30 +00:00
delete_tmap ( ) ;
2021-07-29 14:15:40 +00:00
if ( ! arb : : in ( ) ) try {
arb : : convert : : convert ( ) ;
2021-08-22 12:31:30 +00:00
if ( flags & w_numerical ) arb : : convert : : activate ( ) ;
2021-07-29 14:15:40 +00:00
}
catch ( hr_exception & e ) {
throw rulegen_surrender ( " conversion failure " ) ;
}
2021-07-29 12:07:30 +00:00
clear_all ( ) ;
analyzers . clear ( ) ;
2021-08-21 21:38:36 +00:00
important . clear ( ) ;
treestates . clear ( ) ;
2021-08-22 12:30:52 +00:00
hard_parents = single_live_branches = double_live_branches = all_solid_errors = 0 ;
2021-07-29 12:07:30 +00:00
2021-07-30 13:18:32 +00:00
t_origin . clear ( ) ;
2021-08-22 12:30:24 +00:00
cell_to_tcell . clear ( ) ;
tcell_to_cell . clear ( ) ;
2021-08-23 13:49:48 +00:00
branch_conflicts_seen . clear ( ) ;
2021-08-22 17:42:25 +00:00
sidecache . clear ( ) ;
fix_queue = { } ; in_fixing = false ;
2021-08-22 12:30:24 +00:00
if ( flags & w_numerical ) {
start_game ( ) ;
cell * s = currentmap - > gamestart ( ) ;
tcell * c = gen_tcell ( shvid ( s ) ) ;
cell_to_tcell [ s ] = c ;
tcell_to_cell [ c ] = s ;
c - > dist = 0 ;
t_origin . push_back ( c ) ;
}
else if ( flags & w_single_origin ) {
2021-08-21 22:10:02 +00:00
tcell * c = gen_tcell ( 0 ) ;
c - > dist = 0 ;
t_origin . push_back ( c ) ;
}
else for ( auto & ts : arb : : current . shapes ) {
2021-07-30 13:18:32 +00:00
tcell * c = gen_tcell ( ts . id ) ;
c - > dist = 0 ;
t_origin . push_back ( c ) ;
}
2021-08-22 17:42:07 +00:00
bfs_queue = { } ;
if ( flags & w_bfs ) for ( auto c : t_origin ) bfs_queue . push ( c ) ;
2021-07-29 12:07:30 +00:00
try_count = 0 ;
2021-07-30 13:18:32 +00:00
important = t_origin ;
2021-07-29 12:07:30 +00:00
2021-08-18 23:21:44 +00:00
while ( true ) {
2021-08-21 22:12:22 +00:00
if ( SDL_GetTicks ( ) > t + 1000 * rulegen_timeout )
throw rulegen_surrender ( " timeout " ) ;
2021-08-18 23:21:44 +00:00
try {
rules_iteration ( ) ;
break ;
2021-07-29 12:07:30 +00:00
}
2021-08-18 23:21:44 +00:00
catch ( rulegen_retry & e ) {
if ( try_count > = max_retries ) throw ;
}
}
2021-07-29 12:07:30 +00:00
}
int reclevel ;
void build_test ( ) ;
/* == hrmap_rulegen == */
struct hrmap_rulegen : hrmap {
hrmap * base ;
heptagon * origin ;
heptagon * gen ( int s , int d , bool c7 ) {
int t = arb : : current . shapes [ treestates [ s ] . sid ] . size ( ) ;
heptagon * h = init_heptagon ( t ) ;
if ( c7 ) h - > c7 = newCell ( t , h ) ;
h - > distance = d ;
h - > fieldval = s ;
h - > zebraval = treestates [ s ] . sid ;
2021-07-29 12:46:33 +00:00
h - > s = hsA ;
2021-07-29 12:07:30 +00:00
return h ;
}
~ hrmap_rulegen ( ) {
clearfrom ( origin ) ;
}
hrmap_rulegen ( ) {
origin = gen ( rule_root , 0 , true ) ;
2021-07-29 12:46:33 +00:00
origin - > s = hsOrigin ;
2021-07-29 12:07:30 +00:00
}
hrmap_rulegen ( heptagon * h ) {
origin = h ;
}
heptagon * getOrigin ( ) override {
return origin ;
}
int get_rule ( heptspin hs ) {
int s = hs . at - > fieldval ;
return treestates [ s ] . rules [ hs . spin ] ;
}
static void hsconnect ( heptspin a , heptspin b ) {
a . at - > c . connect ( a . spin , b . at , b . spin , false ) ;
}
heptagon * create_step ( heptagon * h , int d ) override {
heptspin hs ( h , d ) ;
int r = get_rule ( hs ) ;
indenter ind ( 2 ) ;
if ( hlog . indentation > = 6000 )
throw rulegen_failure ( " failed to create_step " ) ;
if ( r > = 0 ) {
auto h1 = gen ( r , h - > distance + 1 , h - > c7 ) ;
auto hs1 = heptspin ( h1 , 0 ) ;
// verify_connection(hs, hs1);
hsconnect ( hs , hs1 ) ;
return h1 ;
}
else if ( r = = DIR_PARENT ) {
auto & hts = treestates [ h - > fieldval ] ;
auto & choices = hts . possible_parents ;
if ( choices . empty ( ) ) throw rulegen_failure ( " no possible parents " ) ;
auto selected = hrand_elt ( choices ) ;
auto h1 = gen ( selected . first , h - > distance - 1 , h - > c7 ) ;
auto hs1 = heptspin ( h1 , selected . second ) ;
hsconnect ( hs , hs1 ) ;
return h1 ;
}
else if ( r = = DIR_LEFT | | r = = DIR_RIGHT ) {
heptspin hs1 = hs ;
int delta = r = = DIR_LEFT ? - 1 : 1 ;
int rev = ( DIR_LEFT ^ DIR_RIGHT ^ r ) ;
hs1 + = delta ;
while ( true ) {
int r1 = get_rule ( hs1 ) ;
if ( r1 = = rev ) {
2021-08-17 12:30:36 +00:00
hsconnect ( hs , hs1 ) ;
2021-07-29 12:07:30 +00:00
return hs1 . at ;
}
else if ( r1 = = r | | r1 = = DIR_PARENT | | r1 > = 0 ) {
hs1 + = wstep ;
hs1 + = delta ;
}
else throw rulegen_failure ( " bad R1 " ) ;
}
}
else throw rulegen_failure ( " bad R " ) ;
throw rulegen_failure ( " impossible " ) ;
}
int get_arb_dir ( int s , int dir ) {
int sid = treestates [ s ] . sid ;
int N = arb : : current . shapes [ sid ] . size ( ) ;
return gmod ( dir + treestates [ s ] . parent_dir , N ) ;
}
transmatrix adj ( heptagon * h , int dir ) override {
2021-07-29 14:15:05 +00:00
if ( h - > fieldval = = - 1 )
2021-07-30 21:50:30 +00:00
return arb : : get_adj ( arb : : current_or_slided ( ) , h - > zebraval , dir , - 1 , - 1 ) ;
2021-07-29 14:15:05 +00:00
2021-07-29 12:07:30 +00:00
int s = h - > fieldval ;
int dir0 = get_arb_dir ( s , dir ) ;
int dir1 = - 1 ;
2021-07-30 21:50:30 +00:00
int sid1 = - 1 ;
2021-07-29 12:07:30 +00:00
if ( h - > c . move ( dir ) ) {
2021-07-30 21:50:30 +00:00
auto s1 = h - > c . move ( dir ) - > fieldval ;
2021-07-29 12:07:30 +00:00
dir1 = get_arb_dir ( s1 , h - > c . spin ( dir ) ) ;
2021-07-30 21:50:30 +00:00
sid1 = treestates [ s1 ] . sid ;
2021-07-29 12:07:30 +00:00
}
2021-07-30 21:50:30 +00:00
return arb : : get_adj ( arb : : current_or_slided ( ) , treestates [ s ] . sid , dir0 , sid1 , dir1 ) ;
2021-07-29 12:07:30 +00:00
}
int shvid ( cell * c ) override {
return c - > master - > zebraval ;
}
2021-07-30 01:01:43 +00:00
transmatrix relative_matrixh ( heptagon * h2 , heptagon * h1 , const hyperpoint & hint ) override {
return relative_matrix_recursive ( h2 , h1 ) ;
}
2021-08-01 09:28:45 +00:00
hyperpoint get_corner ( cell * c , int cid , ld cf ) override {
2021-07-29 12:07:30 +00:00
if ( c - > master - > fieldval = = - 1 ) {
auto & sh = arb : : current_or_slided ( ) . shapes [ c - > master - > zebraval ] ;
2021-07-31 12:37:53 +00:00
cid = gmod ( cid , sh . size ( ) ) ;
2021-07-29 12:07:30 +00:00
return normalize ( C0 + ( sh . vertices [ cid ] - C0 ) * 3 / cf ) ;
}
int s = c - > master - > fieldval ;
auto & sh = arb : : current_or_slided ( ) . shapes [ c - > master - > zebraval ] ;
auto dir = get_arb_dir ( s , cid ) ;
return normalize ( C0 + ( sh . vertices [ dir ] - C0 ) * 3 / cf ) ;
}
void find_cell_connection ( cell * c , int d ) override {
if ( c - > master - > cmove ( d ) = = & oob ) {
c - > c . connect ( d , & out_of_bounds , 0 , false ) ;
}
else hrmap : : find_cell_connection ( c , d ) ;
}
2021-08-01 09:28:45 +00:00
bool strict_tree_rules ( ) override { return true ; }
2021-07-29 12:07:30 +00:00
virtual bool link_alt ( heptagon * h , heptagon * alt , hstate firststate , int dir ) override {
auto & hts = treestates [ h - > fieldval ] ;
int psid = hts . sid ;
if ( firststate = = hsOrigin ) {
2021-07-29 12:46:33 +00:00
alt - > s = hsOrigin ;
2021-07-30 13:18:32 +00:00
for ( auto & ts : treestates ) if ( ts . sid = = psid & & ts . is_root ) {
2021-07-31 12:42:45 +00:00
alt - > fieldval = ts . id ;
// ts.parent_dir should be 0, but anyway
altmap : : relspin ( alt ) = gmod ( ts . parent_dir - hts . parent_dir , isize ( hts . rules ) ) ;
2021-07-30 13:18:32 +00:00
return true ;
}
return false ;
2021-07-29 12:07:30 +00:00
}
int odir = hts . parent_dir + dir ;
int cl = arb : : current . shapes [ psid ] . cycle_length ;
vector < int > choices ;
for ( auto & ts : treestates )
if ( ts . is_possible_parent & & ts . sid = = psid )
if ( gmod ( ts . parent_dir - odir , cl ) = = 0 )
choices . push_back ( ts . id ) ;
alt - > fieldval = hrand_elt ( choices , - 1 ) ;
2021-07-29 12:46:33 +00:00
alt - > s = hsA ;
2021-07-29 12:07:30 +00:00
if ( alt - > fieldval = = - 1 ) return false ;
altmap : : relspin ( alt ) = dir ;
return true ;
}
} ;
EX int get_arb_dir ( cell * c , int dir ) {
return ( ( hrmap_rulegen * ) currentmap ) - > get_arb_dir ( c - > master - > fieldval , dir ) ;
}
EX hrmap * new_hrmap_rulegen_alt ( heptagon * h ) {
return new hrmap_rulegen ( h ) ;
}
EX hrmap * new_hrmap_rulegen ( ) { return new hrmap_rulegen ( ) ; }
EX int get_state ( cell * c ) {
return c - > master - > fieldval ;
}
2021-07-29 14:15:40 +00:00
string rules_known_for = " unknown " ;
2021-07-29 12:07:30 +00:00
string rule_status ;
2021-07-29 14:15:40 +00:00
EX bool known ( ) {
2021-07-30 21:50:30 +00:00
return arb : : current . have_tree | | rules_known_for = = arb : : current . name ;
2021-07-29 14:15:40 +00:00
}
2021-07-30 10:25:17 +00:00
EX bool prepare_rules ( ) {
2021-07-29 14:15:40 +00:00
if ( known ( ) ) return true ;
2021-07-29 12:07:30 +00:00
try {
generate_rules ( ) ;
rules_known_for = arb : : current . name ;
2021-07-31 13:58:13 +00:00
rule_status = XLAT ( " rules generated successfully: %1 states using %2-%3 cells " ,
its ( isize ( treestates ) ) , its ( tcellcount ) , its ( tunified ) ) ;
2021-08-04 16:35:48 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , rule_status ) ;
2021-07-29 12:07:30 +00:00
return true ;
}
catch ( rulegen_retry & e ) {
rule_status = XLAT ( " too difficult: %1 " , e . what ( ) ) ;
}
catch ( rulegen_surrender & e ) {
rule_status = XLAT ( " too difficult: %1 " , e . what ( ) ) ;
}
catch ( rulegen_failure & e ) {
rule_status = XLAT ( " bug: %1 " , e . what ( ) ) ;
}
2021-08-04 16:35:48 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , rule_status ) ;
2021-07-29 12:07:30 +00:00
return false ;
}
2021-08-08 15:14:19 +00:00
# if CAP_COMMANDLINE
2021-07-29 12:07:30 +00:00
int args ( ) {
using namespace arg ;
if ( 0 ) ;
else if ( argis ( " -rulegen " ) ) {
PHASEFROM ( 3 ) ;
prepare_rules ( ) ;
}
else if ( argis ( " -rulegen-cleanup " ) )
cleanup ( ) ;
else if ( argis ( " -rulegen-play " ) ) {
PHASEFROM ( 3 ) ;
2021-07-29 14:15:40 +00:00
if ( prepare_rules ( ) ) {
stop_game ( ) ;
2021-07-30 10:25:17 +00:00
arb : : convert : : activate ( ) ;
2021-07-29 14:15:40 +00:00
start_game ( ) ;
}
2021-07-29 12:07:30 +00:00
}
2021-07-31 13:50:20 +00:00
else if ( argis ( " -d:rulegen " ) ) {
launch_dialog ( show ) ;
}
2021-07-29 12:07:30 +00:00
else return 1 ;
return 0 ;
}
2021-08-08 15:14:19 +00:00
auto hooks_arg =
addHook ( hooks_args , 100 , args ) ;
# endif
auto hooks = addHook ( hooks_configfile , 100 , [ ] {
2021-07-29 12:07:30 +00:00
param_i ( max_retries , " max_retries " ) ;
2021-07-31 13:50:20 +00:00
param_i ( max_tcellcount , " max_tcellcount " )
- > editable ( 0 , 16000000 , 100000 , " maximum cellcount " , " controls the max memory usage of conversion algorithm -- the algorithm fails if exceeded " , ' c ' ) ;
2021-07-29 12:07:30 +00:00
param_i ( max_adv_steps , " max_adv_steps " ) ;
param_i ( max_examine_branch , " max_examine_branch " ) ;
2021-08-19 22:48:09 +00:00
param_i ( max_getside , " max_getside " ) ;
2021-07-29 12:07:30 +00:00
param_i ( max_bdata , " max_bdata " ) ;
2021-08-21 22:12:22 +00:00
param_i ( rulegen_timeout , " rulegen_timeout " ) ;
2021-07-29 12:07:30 +00:00
} ) ;
2021-07-30 21:50:30 +00:00
EX void parse_treestate ( arb : : arbi_tiling & c , exp_parser & ep ) {
if ( ! c . have_tree ) {
c . have_tree = true ;
treestates . clear ( ) ;
rule_root = 0 ;
}
treestates . emplace_back ( ) ;
auto & ts = treestates . back ( ) ;
ts . id = isize ( treestates ) - 1 ;
ts . sid = ep . iparse ( ) ;
ts . parent_dir = 0 ;
if ( ! arb : : correct_index ( ts . sid , isize ( c . shapes ) ) )
throw hr_parse_exception ( " incorrect treestate index at " + ep . where ( ) ) ;
int N = c . shapes [ ts . sid ] . size ( ) ;
2021-08-01 01:00:24 +00:00
int qparent = 0 , sumparent = 0 ;
2021-07-30 21:50:30 +00:00
for ( int i = 0 ; i < N ; i + + ) {
ep . force_eat ( " , " ) ; ep . skip_white ( ) ;
if ( ep . eat ( " PARENT " ) ) ts . rules . push_back ( DIR_PARENT ) ;
else if ( ep . eat ( " LEFT " ) ) ts . rules . push_back ( DIR_LEFT ) ;
else if ( ep . eat ( " RIGHT " ) ) ts . rules . push_back ( DIR_RIGHT ) ;
else { int i = ep . iparse ( ) ; ts . rules . push_back ( i ) ; }
}
2021-08-01 01:00:24 +00:00
for ( int i = 0 ; i < N ; i + + ) {
if ( ts . rules [ i ] = = DIR_PARENT ) qparent + + , sumparent + = i ;
}
ts . is_root = qparent = = 0 ;
if ( qparent > 1 ) throw hr_parse_exception ( " multiple parent at " + ep . where ( ) ) ;
if ( qparent = = 1 ) {
ts . parent_dir = sumparent ;
println ( hlog , " before: " , ts . rules ) ;
std : : rotate ( ts . rules . begin ( ) , ts . rules . begin ( ) + sumparent , ts . rules . end ( ) ) ;
println ( hlog , " after : " , ts . rules ) ;
}
2021-07-30 21:50:30 +00:00
ep . force_eat ( " ) " ) ;
}
EX void verify_parsed_treestates ( ) {
2021-07-31 14:09:46 +00:00
if ( rule_root < 0 | | rule_root > = isize ( treestates ) )
throw hr_parse_exception ( " undefined treestate as root " ) ;
2021-07-30 21:50:30 +00:00
for ( auto & ts : treestates ) for ( auto & r : ts . rules ) {
2021-08-17 12:30:36 +00:00
if ( r < 0 & & ! among ( r , DIR_PARENT , DIR_LEFT , DIR_RIGHT ) )
2021-07-30 21:50:30 +00:00
throw hr_parse_exception ( " negative number in treestates " ) ;
if ( r > isize ( treestates ) )
throw hr_parse_exception ( " undefined treestate " ) ;
}
for ( auto & sh : arb : : current . shapes ) sh . cycle_length = sh . size ( ) ;
find_possible_parents ( ) ;
}
2021-07-31 13:50:20 +00:00
EX void show ( ) {
cmode = sm : : SIDE | sm : : MAYDARK ;
gamescreen ( 1 ) ;
dialog : : init ( XLAT ( " strict tree maps " ) ) ;
dialog : : addHelp ( XLAT (
2021-07-31 14:10:37 +00:00
" Strict tree maps are generated using a more powerful algorithm. \n \n This algorithms supports horocycles and knows the expansion rates of various "
" tessellations (contrary to the basic implementation of Archimedean, tes, and unrectified/warped/untruncated tessellations). \n \n You can convert mostly any "
" non-spherical periodic 2D tessellation to strict tree based. \n \n Switching the map format erases your map. " ) ) ;
2021-07-31 13:50:20 +00:00
if ( kite : : in ( ) ) {
dialog : : addInfo ( " not available in aperiodic tessellations " ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
else if ( WDIM = = 3 ) {
dialog : : addInfo ( " not available in 3D tessellations " ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
dialog : : addBoolItem ( XLAT ( " in tes internal format " ) , arb : : in ( ) , ' t ' ) ;
dialog : : add_action ( [ ] {
if ( ! arb : : in ( ) ) {
arb : : convert : : convert ( ) ;
arb : : convert : : activate ( ) ;
start_game ( ) ;
2021-07-31 13:58:13 +00:00
rule_status = XLAT ( " converted successfully -- %1 cell types " , its ( isize ( arb : : current . shapes ) ) ) ;
rules_known_for = " unknown " ;
2021-07-31 13:50:20 +00:00
}
else if ( arb : : convert : : in ( ) ) {
stop_game ( ) ;
geometry = arb : : convert : : base_geometry ;
variation = arb : : convert : : base_variation ;
start_game ( ) ;
}
else {
addMessage ( XLAT ( " cannot be disabled for this tiling " ) ) ;
}
} ) ;
dialog : : addBoolItem ( XLAT ( " strict tree based " ) , currentmap - > strict_tree_rules ( ) , ' s ' ) ;
dialog : : add_action ( [ ] {
if ( ! currentmap - > strict_tree_rules ( ) ) {
if ( prepare_rules ( ) ) {
println ( hlog , " prepare_rules returned true " ) ;
stop_game ( ) ;
arb : : convert : : activate ( ) ;
start_game ( ) ;
delete_tmap ( ) ;
}
}
else if ( arb : : current . have_tree ) {
addMessage ( XLAT ( " cannot be disabled for this tiling " ) ) ;
}
else {
rules_known_for = " unknown " ;
rule_status = " manually disabled " ;
stop_game ( ) ;
start_game ( ) ;
}
} ) ;
add_edit ( max_tcellcount ) ;
dialog : : addBreak ( 100 ) ;
2021-07-31 13:58:13 +00:00
dialog : : addHelp ( rule_status ) ;
dialog : : items . back ( ) . color = known ( ) ? 0x00FF00 : rules_known_for = = " unknown " ? 0xFFFF00 : 0xFF0000 ;
2021-07-31 13:50:20 +00:00
dialog : : addBreak ( 100 ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2021-07-29 12:07:30 +00:00
EX }
}