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 ;
2022-02-01 15:56:44 +00:00
EX int max_shortcut_length = 1200 ;
2022-02-01 15:57:13 +00:00
EX int first_restart_on = 512 ;
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 ;
2021-12-28 10:34:50 +00:00
const int MYSTERY_LARGE = 31999999 ;
2021-07-29 12:07:30 +00:00
# endif
/* === 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 */
2023-08-23 17:44:37 +00:00
static constexpr flagtype w_numerical = Flag ( 1 ) ; /*< build trees numerically */
static constexpr flagtype w_near_solid = Flag ( 2 ) ; /*< solid's pre-parent is also solid */
static constexpr flagtype w_no_shortcut = Flag ( 3 ) ; /*< generate no shortcuts */
static constexpr flagtype w_no_restart = Flag ( 4 ) ; /*< do not restart at powers of two */
static constexpr flagtype w_no_sidecache = Flag ( 5 ) ; /*< do not cache get_side */
static constexpr flagtype w_no_relative_distance = Flag ( 6 ) ; /*< do not build relative distances into codes */
static constexpr flagtype w_examine_once = Flag ( 7 ) ; /*< restart after first conflict found in analysis */
static constexpr flagtype w_examine_all = Flag ( 8 ) ; /*< focus on all conflicts found in analysis even if we know them */
static constexpr flagtype w_conflict_all = Flag ( 9 ) ; /*< full extension in case of conflicts */
static constexpr flagtype w_parent_always = Flag ( 10 ) ; /*< always consider the full parent rule */
static constexpr flagtype w_parent_reverse = Flag ( 11 ) ; /*< reverse paths in parent_dir */
static constexpr flagtype w_parent_side = Flag ( 12 ) ; /*< allow side paths in parent_dir */
static constexpr flagtype w_parent_never = Flag ( 13 ) ; /*< never consider the full parent rule */
static constexpr flagtype w_always_clean = Flag ( 14 ) ; /*< restart following phases after any distance errors */
static constexpr flagtype w_single_origin = Flag ( 15 ) ; /*< consider only one origin */
static constexpr flagtype w_slow_side = Flag ( 16 ) ; /*< do not try get_side optimization */
static constexpr flagtype w_bfs = Flag ( 17 ) ; /*< compute distances using BFS */
static constexpr flagtype w_numerical_fix = Flag ( 18 ) ; /*< when doing numerical, find out filled vertices */
static constexpr flagtype w_known_structure = Flag ( 19 ) ; /*< do flagless first, then use the known distances from there (handled in ruletest) */
static constexpr flagtype w_known_distances = Flag ( 20 ) ; /*< with, use the actual distances */
static constexpr flagtype w_no_smart_shortcuts = Flag ( 21 ) ; /*< disable the 'smart shortcut' optimization */
static constexpr flagtype w_less_smart_retrace = Flag ( 22 ) ; /*< stop early when examining smart shortcut retraction */
static constexpr flagtype w_less_smart_advance = Flag ( 23 ) ; /*< stop early when examining smart shortcut advancement */
static constexpr flagtype w_no_queued_extensions = Flag ( 24 ) ; /*< consider extensions one by one */
static constexpr flagtype w_no_branch_skipping = Flag ( 24 ) ; /*< do not skip branches */
2022-08-06 23:57:06 +00:00
2024-03-19 17:53:09 +00:00
/* extra */
static constexpr flagtype w_optimize2 = Flag ( 25 ) ; /*< optimize in 2D */
2022-08-06 23:57:06 +00:00
/* for 3D honeycombs */
2023-08-23 17:44:37 +00:00
static constexpr flagtype w_skip_transducers = Flag ( 32 ) ; /*< skip the transducer test */
static constexpr flagtype w_skip_transducer_loops = Flag ( 33 ) ; /*< skip loops during the transducer test */
static constexpr flagtype w_skip_transducer_terminate = Flag ( 34 ) ; /*< skip termination during the transducer test */
static constexpr flagtype w_r3_all_errors = Flag ( 35 ) ; /*< consider all errors for R3 */
static constexpr flagtype w_r3_no_road_shortcuts = Flag ( 36 ) ; /*< consider all errors for R3 */
static constexpr flagtype w_ignore_transducer_dist = Flag ( 37 ) ; /*< ignore distance errors while testing the transducers */
2021-08-22 12:28:54 +00:00
# endif
2021-08-21 22:10:02 +00:00
2022-08-17 15:33:36 +00:00
/** these control the output */
EX flagtype rdebug_flags ;
2021-08-21 22:10:02 +00:00
EX flagtype flags = 0 ;
2021-07-29 12:07:30 +00:00
2021-11-01 16:14:01 +00:00
EX int64_t movecount ;
2021-12-29 03:27:17 +00:00
EX int current_getside , current_examine_branch ;
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 */
2021-12-28 10:34:50 +00:00
int code ;
2021-07-29 12:07:30 +00:00
/** direction to the parent in the tree */
short parent_dir ;
2021-12-25 20:31:36 +00:00
/** direction to the OLD parent in the tree */
short old_parent_dir ;
2021-08-24 01:36:50 +00:00
/** direction to anyone closer */
short any_nearer ;
2021-07-29 12:07:30 +00:00
/** 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 ;
2021-12-29 02:35:31 +00:00
/** is side info cached? */
unsigned long long known_sides ;
/** which side is it */
unsigned long long which_side ;
2021-07-29 12:07:30 +00:00
/** 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 ; }
2021-11-01 16:14:01 +00:00
connection_table < tcell > c ;
tcell * & move ( int d ) { movecount + + ; return c . move ( d ) ; }
tcell * & modmove ( int d ) { movecount + + ; return c . modmove ( d ) ; }
tcell * cmove ( int d ) { movecount + + ; return tmove ( this , d ) ; }
tcell * cmodmove ( int d ) { movecount + + ; return tmove ( this , c . fix ( d ) ) ; }
2021-07-29 12:07:30 +00:00
tcell ( ) { }
} ;
inline void print ( hstream & hs , tcell * h ) { print ( hs , " P " , index_pointer ( h ) ) ; }
using twalker = walker < tcell > ;
# endif
2022-07-11 10:09:00 +00:00
EX hookset < void ( int , twalker ) > hooks_gen_tcell ;
2021-07-29 12:07:30 +00:00
queue < reaction_t > fix_queue ;
2021-08-17 18:06:48 +00:00
void push_unify ( twalker a , twalker b ) {
2022-07-12 10:16:47 +00:00
if ( WDIM = = 3 & & a ! = b ) {
println ( hlog , " pushing unify of " , tie ( a , b ) ) ;
throw hr_exception ( " bad unify " ) ;
}
2021-08-17 18:06:48 +00:00
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-25 10:56:56 +00:00
// sometimes the standard x+wstep returns nullptr because of unification
2021-08-18 23:27:26 +00:00
twalker addstep ( twalker x ) {
x . cpeek ( ) ;
ufind ( x ) ;
return x + wstep ;
}
2022-07-13 17:31:24 +00:00
EX int less_states ;
2022-07-17 10:51:10 +00:00
EX int number_of_types ( ) {
2022-09-14 20:31:12 +00:00
if ( arb : : in ( ) | | WDIM = = 2 ) return isize ( arb : : current . shapes ) ;
2023-04-11 15:10:04 +00:00
# if CAP_MAXMDIM >= 4
2022-07-13 17:31:24 +00:00
if ( WDIM = = 3 ) return gcd ( reg3 : : quotient_count_sub ( ) , less_states ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 14:22:39 +00:00
throw hr_exception ( " unknown number_of_types " ) ;
}
2022-08-06 23:45:26 +00:00
EX int get_id ( cell * c ) {
2022-09-14 20:31:12 +00:00
if ( arb : : in ( ) | | WDIM = = 2 ) return shvid ( c ) ;
2023-04-11 15:10:04 +00:00
# if CAP_MAXMDIM >= 4
2022-09-14 20:31:12 +00:00
if ( WDIM = = 3 ) return zgmod ( reg3 : : get_aid ( c ) , less_states ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 10:18:58 +00:00
throw hr_exception ( " unknown get_id " ) ;
}
int shape_size ( int id ) {
2022-09-14 20:31:12 +00:00
if ( arb : : in ( ) | | WDIM = = 2 ) return isize ( arb : : current . shapes [ id ] . connections ) ;
2023-04-11 15:10:04 +00:00
# if CAP_MAXMDIM >= 4
2022-09-14 20:31:12 +00:00
if ( WDIM = = 3 ) return reg3 : : get_size_of_aid ( id ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 10:18:58 +00:00
throw hr_exception ( " unknown shape_size " ) ;
}
int cycle_size ( int id ) {
2022-09-14 20:31:12 +00:00
if ( arb : : in ( ) | | WDIM = = 2 ) return arb : : current . shapes [ id ] . cycle_length ;
2023-04-11 15:10:04 +00:00
# if CAP_MAXMDIM >= 4
2022-09-14 20:31:12 +00:00
if ( WDIM = = 3 ) return reg3 : : get_size_of_aid ( id ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 10:18:58 +00:00
throw hr_exception ( " unknown shape size " ) ;
}
2021-07-29 12:07:30 +00:00
tcell * gen_tcell ( int id ) {
2022-07-11 10:18:58 +00:00
int d = shape_size ( id ) ;
2021-07-29 12:07:30 +00:00
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 ;
2021-12-28 10:34:50 +00:00
c - > code = MYSTERY_LARGE ;
2021-07-29 12:07:30 +00:00
c - > parent_dir = MYSTERY ;
2021-12-25 20:31:36 +00:00
c - > old_parent_dir = MYSTERY ;
2021-12-29 02:35:31 +00:00
c - > known_sides = 0 ;
c - > which_side = 0 ;
2021-07-29 12:07:30 +00:00
first_tcell = c ;
// println(hlog, c, " is a new tcell of id ", id);
tcellcount + + ;
return c ;
}
2022-07-11 15:32:27 +00:00
EX map < cell * , tcell * > cell_to_tcell ;
EX map < tcell * , cell * > tcell_to_cell ;
2021-08-22 12:30:24 +00:00
2021-11-01 16:24:54 +00:00
void numerical_fix ( twalker 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 ;
vector < twalker > deb = { pwb } ;
while ( true ) {
if ( ! pwb . peek ( ) ) break ;
pwb = pwb + wstep - 1 ;
deb . push_back ( pwb ) ;
steps + + ;
if ( pwb = = pwf ) {
if ( steps = = valence ) return ; /* that's great, we already know this loop */
else {
debuglist = deb ;
println ( hlog , " deb = " , deb ) ;
throw rulegen_failure ( " vertex valence too small " ) ;
}
}
if ( steps = = valence ) {
println ( hlog , " steps = " , steps , " valence = " , valence , " (D) " ) ;
debuglist = deb ;
println ( hlog , " deb = " , deb ) ;
throw rulegen_failure ( " incorrect looping " ) ;
}
}
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 rulegen_failure ( " vertex valence too small " ) ;
}
if ( steps = = valence ) {
println ( hlog , " steps = " , steps , " valence = " , valence , " (C) " ) ;
debuglist = deb ;
println ( hlog , " deb = " , deb ) ;
throw rulegen_failure ( " incorrect looping " ) ;
}
}
if ( steps = = valence - 1 ) {
pwb . at - > c . connect ( pwb . spin , pwf . at , pwf . spin , false ) ;
2021-11-03 08:42:21 +00:00
fix_distances ( pwb . at ) ;
2021-11-01 16:24:54 +00:00
}
}
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-11-01 16:24:54 +00:00
if ( c - > c . move ( d ) ) return c - > c . move ( d ) ;
if ( flags & ( w_numerical | w_known_structure ) ) {
indenter ind ( 2 ) ;
if ( flags & w_known_structure ) swap_treestates ( ) ;
2021-08-22 12:30:24 +00:00
cell * oc = tcell_to_cell [ c ] ;
2021-11-01 16:24:54 +00:00
int d1 = d ;
if ( flags & w_known_structure ) {
d1 = gmod ( d1 - treestates [ oc - > master - > fieldval ] . parent_dir , oc - > type ) ;
}
cell * oc1 = oc - > cmove ( d1 ) ;
2021-08-22 12:30:24 +00:00
auto & c1 = cell_to_tcell [ oc1 ] ;
if ( ! c1 ) {
2022-07-11 10:18:58 +00:00
c1 = gen_tcell ( get_id ( oc1 ) ) ;
2021-08-22 12:30:24 +00:00
tcell_to_cell [ c1 ] = oc1 ;
2021-11-01 16:24:54 +00:00
if ( flags & w_known_distances )
c1 - > dist = oc1 - > master - > distance ;
}
int d2 = oc - > c . spin ( d1 ) ;
if ( flags & w_known_structure ) {
d2 = gmod ( d2 + treestates [ oc1 - > master - > fieldval ] . parent_dir , oc1 - > type ) ;
}
c - > c . connect ( d , cell_to_tcell [ oc1 ] , d2 , false ) ;
/* if(arb::current.shapes[c->id].connections[d].eid != d2)
throw hr_exception ( " Wrong type! " ) ; */
if ( flags & w_known_structure )
swap_treestates ( ) ;
2021-11-07 08:59:44 +00:00
if ( ! ( flags & w_known_distances ) )
fix_distances ( c ) ;
2021-11-01 16:24:54 +00:00
ensure_shorter ( c1 ) ;
if ( flags & w_numerical_fix ) {
numerical_fix ( twalker ( c , d ) ) ;
numerical_fix ( twalker ( c , d ) + wstep ) ;
2021-08-22 12:30:24 +00:00
}
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 ) ;
2022-07-11 10:09:00 +00:00
c1 - > c . connect ( co . eid , cd . at , cd . spin , false ) ;
callhooks ( hooks_gen_tcell , 1 , twalker ( c1 , co . eid ) ) ;
2021-07-29 12:07:30 +00:00
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 ) {
2022-07-11 15:32:27 +00:00
if ( GDIM = = 3 ) throw hr_exception ( " check_loops called " ) ;
2021-07-29 12:07:30 +00:00
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 ) {
2022-07-11 10:09:00 +00:00
callhooks ( hooks_gen_tcell , 2 , pwb ) ;
2021-07-29 12:07:30 +00:00
connect_and_check ( pwb , pwf ) ;
2021-11-01 16:18:16 +00:00
fix_distances ( pwb . at ) ;
2021-07-29 12:07:30 +00:00
}
}
2021-11-01 16:18:06 +00:00
EX void connect_and_check ( twalker p1 , twalker p2 ) {
2022-07-12 10:16:47 +00:00
if ( GDIM = = 3 ) throw hr_exception ( " connect_and_check called " ) ;
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 ;
2022-07-12 10:16:47 +00:00
if ( GDIM = = 3 ) throw hr_exception ( " unify called " ) ;
2022-07-11 10:09:00 +00:00
callhooks ( hooks_gen_tcell , 3 , pw1 ) ;
callhooks ( hooks_gen_tcell , 4 , pw2 ) ;
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 " ) ;
2022-07-11 10:18:58 +00:00
if ( ( pw1 . spin - pw2 . spin ) % cycle_size ( pw1 . at - > id ) )
2021-08-17 18:06:48 +00:00
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 ) ;
2022-07-11 10:18:58 +00:00
for ( int i = 0 ; i < pw1 . at - > type ; i + + ) {
2021-07-29 12:07:30 +00:00
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-08-24 01:36:50 +00:00
fix_distances ( pw1 . at ) ;
2021-07-29 12:07:30 +00:00
}
2021-12-24 22:08:14 +00:00
EX vector < twalker > t_origin ;
2021-07-29 12:07:30 +00:00
2021-11-01 16:15:23 +00:00
EX void delete_tmap ( ) {
2021-12-29 02:33:25 +00:00
clean_analyzers ( ) ;
2021-07-29 12:07:30 +00:00
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 ;
2021-12-24 22:11:33 +00:00
EX vector < twalker > solid_errors_list ;
2021-07-29 12:07:30 +00:00
/* === 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 */
2022-08-06 23:45:26 +00:00
EX int solid_errors ;
2021-07-29 12:07:30 +00:00
2021-08-22 12:30:52 +00:00
/** total solid errors */
EX int all_solid_errors ;
2021-12-28 10:35:37 +00:00
/** the next distance to warn about */
EX int next_distance_warning ;
/** current distance warnings */
EX int distance_warnings ;
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-12-25 20:32:08 +00:00
int last_dir ;
2021-08-04 16:35:04 +00:00
} ;
2021-08-15 15:26:38 +00:00
# endif
2021-08-04 16:35:04 +00:00
2021-12-27 19:19:18 +00:00
EX vector < vector < unique_ptr < shortcut > > > shortcuts ;
2021-08-04 16:35:04 +00:00
2021-12-29 03:20:19 +00:00
vector < reaction_t > skipped_branches ;
using branch_check = tuple < int , int , int > ;
set < branch_check > checks_to_skip ;
2021-12-24 22:08:14 +00:00
vector < int > root_path ( twalker & cw ) {
2021-08-04 16:35:04 +00:00
cw + = wstep ;
vector < int > res ;
while ( true ) {
2021-12-24 22:08:14 +00:00
if ( cw . at - > dist = = 0 ) {
int j = cw . to_spin ( 0 ) ;
res . push_back ( j ) ;
return res ;
}
else {
auto cwd = get_parent_dir ( cw ) ;
int j = cw . to_spin ( cwd . spin ) ;
res . push_back ( j ) ;
cw = cwd + wstep ;
}
2021-08-04 16:35:04 +00:00
}
}
2021-12-24 22:11:50 +00:00
EX void calc_distances ( tcell * c ) ;
2021-08-24 01:36:50 +00:00
EX void shortcut_found ( tcell * c , tcell * alt , vector < twalker > & walkers , vector < twalker > & walkers2 , const vector < int > & walkerdir , const vector < int > & walkerdir2 , int wpos ) {
2021-08-04 16:35:04 +00:00
vector < int > pre ;
2021-08-24 01:36:50 +00:00
for ( int i = wpos ; i > = 1 ; i - - ) pre . push_back ( walkerdir [ i ] ) ;
reverse ( pre . begin ( ) , pre . end ( ) ) ;
2021-08-04 16:35:04 +00:00
vector < int > post ;
2021-08-24 01:36:50 +00:00
for ( int i = isize ( walkers2 ) - 1 ; i > = 1 ; i - - ) post . push_back ( walkerdir2 [ i ] ) ;
2021-08-04 16:35:04 +00:00
reverse ( post . begin ( ) , post . end ( ) ) ;
2021-08-24 01:36:50 +00:00
int delta = walkers [ wpos ] . to_spin ( walkers2 . back ( ) . spin ) ;
2021-08-04 16:35:04 +00:00
2021-08-19 11:12:26 +00:00
for ( auto & s : shortcuts [ c - > id ] ) if ( s - > pre = = pre & & s - > post = = post ) {
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 16 )
2021-08-24 01:36:50 +00:00
println ( hlog , " already knew that " , pre , " ~ " , post ) ;
2021-08-19 11:12:26 +00:00
return ;
}
2021-08-04 16:35:04 +00:00
if ( debugflags & DF_GEOM )
2021-08-24 01:36:50 +00:00
println ( hlog , " new shortcut found, pre = " , pre , " post = " , post , " pre reaches " , walkers [ wpos ] , " post reaches " , walkers2 . back ( ) , " of type " , walkers [ wpos ] . at - > id , " sample = " , c ) ;
2021-08-04 16:35:04 +00:00
2022-02-01 15:56:44 +00:00
if ( isize ( pre ) > max_shortcut_length ) {
2021-08-17 17:27:02 +00:00
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 ;
2021-12-25 20:32:08 +00:00
sh - > last_dir = c - > any_nearer ;
2021-08-15 15:26:38 +00:00
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-24 01:36:50 +00:00
EX void find_new_shortcuts ( tcell * c , int d , tcell * alt , int newdir , int delta ) {
2021-08-21 22:10:02 +00:00
2021-12-24 22:11:33 +00:00
if ( ! solid_errors ) debuglist = { } ;
solid_errors_list . push_back ( c ) ;
2021-08-22 12:30:52 +00:00
solid_errors + + ;
all_solid_errors + + ;
2021-08-25 10:57:14 +00:00
check_timeout ( ) ; /* may freeze no this */
2021-08-21 22:10:02 +00:00
if ( flags & w_no_shortcut ) return ;
2021-11-01 16:24:54 +00:00
if ( flags & w_known_distances ) return ;
2021-08-21 22:10:02 +00:00
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 ) ;
2021-08-24 01:36:50 +00:00
2021-08-25 10:57:14 +00:00
if ( newdir = = c - > any_nearer ) {
if ( debugflags & DF_GEOM )
println ( hlog , " same direction " ) ;
return ;
}
2021-08-24 01:36:50 +00:00
/* {
throw rulegen_failure ( " direction did not change " ) ;
} */
2021-08-19 11:12:26 +00:00
if ( c - > dist = = MYSTERY )
2021-08-19 11:13:59 +00:00
throw rulegen_failure ( " find_new_shortcuts with MYSTERY distance " ) ;
2021-08-24 01:36:50 +00:00
map < tcell * , int > seen ;
2021-08-19 11:12:26 +00:00
vector < twalker > walkers ;
vector < int > walkerdir = { - 1 } ;
2021-08-24 01:36:50 +00:00
seen [ c ] = 0 ;
2021-08-19 11:12:26 +00:00
walkers . push_back ( c ) ;
2021-08-24 01:36:50 +00:00
2021-08-19 11:12:26 +00:00
for ( int j = 0 ; j < isize ( walkers ) ; j + + ) {
auto w = walkers [ j ] ;
2021-08-24 01:36:50 +00:00
if ( w . at - > dist = = 0 ) break ;
2021-08-19 11:12:26 +00:00
for ( int s = 0 ; s < w . at - > type ; s + + ) {
twalker w1 = w + s ;
2021-08-24 01:36:50 +00:00
if ( w1 . peek ( ) & & w1 . spin = = w . at - > any_nearer & & ! seen . count ( w1 . peek ( ) ) ) {
seen [ w1 . peek ( ) ] = isize ( walkers ) ;
2021-08-19 11:12:26 +00:00
walkers . push_back ( w1 + wstep ) ;
walkerdir . push_back ( s ) ;
}
}
}
2021-08-24 01:36:50 +00:00
set < tcell * > seen2 ; /* prevent loops */
2021-08-19 11:12:26 +00:00
c - > dist = d ;
2021-08-24 01:36:50 +00:00
c - > any_nearer = gmod ( newdir , c - > type ) ;
fix_distances ( c ) ;
2021-08-19 11:12:26 +00:00
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-25 10:58:02 +00:00
if ( w . at - > dist = = 0 ) break ;
2021-08-19 11:12:26 +00:00
for ( int s = 0 ; s < w . at - > type ; s + + ) {
twalker w1 = w + s ;
2021-08-24 01:36:50 +00:00
ufind ( w1 ) ;
if ( w1 . spin ! = w . at - > any_nearer ) continue ;
2021-08-19 11:12:26 +00:00
if ( ! w1 . peek ( ) ) continue ;
2021-08-24 01:36:50 +00:00
if ( seen2 . count ( w1 . peek ( ) ) ) break ;
seen2 . insert ( w1 . peek ( ) ) ;
if ( true ) {
2021-08-19 11:12:26 +00:00
walkers2 . push_back ( w1 + wstep ) ;
walkerdir2 . push_back ( s ) ;
if ( seen . count ( w1 . peek ( ) ) ) {
2021-08-24 01:36:50 +00:00
shortcut_found ( c , alt , walkers , walkers2 , walkerdir , walkerdir2 , seen [ w1 . peek ( ) ] ) ;
return ;
2021-08-19 11:12:26 +00:00
}
}
}
}
}
2021-08-16 22:24:38 +00:00
EX void remove_parentdir ( tcell * c ) {
2021-12-29 02:33:35 +00:00
if ( c - > parent_dir ! = MYSTERY ) {
clear_sidecache_and_codes ( ) ;
c - > old_parent_dir = c - > parent_dir ;
}
2021-08-16 22:24:38 +00:00
c - > parent_dir = MYSTERY ;
2021-12-28 10:34:50 +00:00
c - > code = MYSTERY_LARGE ;
2021-08-16 22:24:38 +00:00
for ( int i = 0 ; i < c - > type ; i + + ) if ( c - > move ( i ) ) {
2021-12-25 20:31:36 +00:00
if ( c - > move ( i ) - > parent_dir ) c - > move ( i ) - > old_parent_dir = c - > move ( i ) - > parent_dir ;
2021-08-16 22:24:38 +00:00
c - > move ( i ) - > parent_dir = MYSTERY ;
2021-12-28 10:34:50 +00:00
c - > move ( i ) - > code = MYSTERY_LARGE ;
2021-08-16 22:24:38 +00:00
}
}
2021-08-22 17:42:07 +00:00
queue < tcell * > bfs_queue ;
2021-08-24 01:36:50 +00:00
EX void fix_distances ( tcell * c ) {
2021-08-22 17:42:07 +00:00
if ( flags & w_bfs ) while ( true ) {
2021-08-25 10:57:33 +00:00
if ( in_fixing ) return ;
2021-08-22 17:42:07 +00:00
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 ;
2021-11-01 16:24:54 +00:00
if ( flags & w_known_distances ) return ;
2021-07-29 12:07:30 +00:00
vector < tcell * > q = { c } ;
for ( int qi = 0 ; qi < isize ( q ) ; qi + + ) {
c = q [ qi ] ;
restart :
for ( int i = 0 ; i < c - > type ; i + + ) {
2021-08-17 18:08:06 +00:00
if ( ! c - > move ( i ) ) continue ;
2021-08-04 16:35:04 +00:00
ufindc ( c ) ;
2021-12-25 00:35:48 +00:00
auto process_edge = [ & ] ( twalker tgtw , twalker srcw ) {
tcell * tgt = tgtw . at ;
tcell * src = srcw . at ;
auto & tgt_d = tgt - > dist ;
int new_d = src - > dist + 1 ;
if ( tgt_d > new_d ) {
if ( tgt - > is_solid )
find_new_shortcuts ( tgt , new_d , tgt , tgtw . spin , 0 ) ;
ufind ( tgtw ) ; tgt = tgtw . at ;
2021-12-27 12:03:15 +00:00
remove_parentdir ( tgt ) ;
2021-12-25 00:35:48 +00:00
tgt_d = new_d ;
tgt - > any_nearer = tgtw . spin ;
2021-12-28 10:35:37 +00:00
if ( new_d > = next_distance_warning ) {
if ( new_d > = MYSTERY - 1 ) throw rulegen_failure ( " distance limit exceeded " ) ;
2021-12-29 03:27:17 +00:00
if ( next_distance_warning < 10000 ) next_distance_warning * = 2 ;
else if ( next_distance_warning < 20000 ) next_distance_warning = 20000 ;
else next_distance_warning = new_d ; distance_warnings + + ;
2021-12-28 10:35:37 +00:00
}
2021-12-25 00:35:48 +00:00
return true ;
}
return false ;
} ;
twalker ci1 ( c - > cmove ( i ) , c - > c . spin ( i ) ) ;
twalker ci ( c , i ) ;
if ( process_edge ( ci , ci1 ) ) goto restart ;
if ( process_edge ( ci1 , ci ) ) q . push_back ( ci1 . at ) ;
2021-07-29 12:07:30 +00:00
}
}
}
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-24 01:36:50 +00:00
if ( c1 - > is_solid & & d ! = d1 ) { solid_errors + + ; find_new_shortcuts ( c1 , d , c2 , c2 - > any_nearer - delta , + delta ) ; remove_parentdir ( c1 ) ; }
2021-08-23 22:33:24 +00:00
if ( d ! = d1 ) fix_distances ( c1 ) ;
2021-07-29 12:07:30 +00:00
c1 - > dist = d ;
2021-08-24 01:36:50 +00:00
if ( c2 - > is_solid & & d ! = d2 ) { solid_errors + + ; find_new_shortcuts ( c2 , d , c1 , c1 - > any_nearer + delta , - delta ) ; remove_parentdir ( c2 ) ; }
2021-08-23 22:33:24 +00:00
if ( d ! = d2 ) 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-12-27 12:03:15 +00:00
clear_sidecache_and_codes ( ) ;
2021-08-21 22:10:02 +00:00
if ( flags & w_always_clean ) clean_data ( ) ;
2021-12-24 22:11:33 +00:00
debuglist = solid_errors_list ;
solid_errors_list = { } ;
2021-12-29 03:20:19 +00:00
checks_to_skip . clear ( ) ;
2021-08-05 09:56:49 +00:00
throw hr_solid_error ( ) ;
}
2021-12-28 10:35:37 +00:00
b = distance_warnings ;
distance_warnings = 0 ;
if ( b & & ! no_errors ) {
clean_parents ( ) ;
2021-12-29 03:20:19 +00:00
checks_to_skip . clear ( ) ;
2021-12-28 10:35:37 +00:00
throw rulegen_retry ( " distance exceeded " ) ;
}
2021-07-29 12:07:30 +00:00
}
/** make sure that we know c->dist */
2022-07-13 17:31:24 +00:00
EX void be_solid ( tcell * c ) {
2021-07-29 12:07:30 +00:00
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-25 10:58:02 +00:00
if ( c - > dist > 0 & & ! ( flags & w_near_solid ) & & c - > any_nearer > = 0 & & c - > any_nearer < c - > type ) {
2021-08-24 10:55:56 +00:00
tcell * c1 = c - > move ( c - > any_nearer ) ;
if ( c1 ) be_solid ( c1 ) ;
}
2021-07-29 12:07:30 +00:00
}
2021-08-15 15:26:38 +00:00
EX void look_for_shortcuts ( tcell * c , shortcut & sh ) {
if ( c - > dist < = 0 ) return ;
2021-12-24 22:13:05 +00:00
if ( ! ( flags & w_no_smart_shortcuts ) ) {
twalker tw0 ( c , 0 ) ;
twalker tw ( c , 0 ) ;
ufind ( tw ) ;
ufind ( tw0 ) ;
for ( auto & v : sh . pre ) {
tw + = v ;
2021-12-24 22:13:57 +00:00
if ( ! tw . peek ( ) & & ! ( flags & w_less_smart_retrace ) ) return ;
2021-12-24 22:13:05 +00:00
ufind ( tw ) ;
tw + = wstep ;
calc_distances ( tw . at ) ;
}
2021-12-25 20:32:08 +00:00
int more_steps = isize ( sh . post ) ;
2022-07-11 10:18:58 +00:00
int d = cycle_size ( c - > id ) ;
2021-12-25 20:32:08 +00:00
if ( sh . last_dir % d < c - > any_nearer % d ) more_steps - - ;
2021-12-24 22:13:05 +00:00
tw + = sh . delta ;
for ( auto it = sh . post . rbegin ( ) ; it ! = sh . post . rend ( ) ; it + + ) {
auto & v = * it ;
ufind ( tw ) ;
2021-12-25 20:32:08 +00:00
if ( ! tw . peek ( ) & & tw . at - > dist + more_steps > c - > dist & & ! ( flags & w_less_smart_advance ) ) return ;
2021-12-24 22:13:05 +00:00
tw + = wstep ;
calc_distances ( tw . at ) ;
2021-12-25 20:32:08 +00:00
more_steps - - ;
2021-12-24 22:13:05 +00:00
tw - = v ;
}
2021-12-25 20:32:08 +00:00
process_fix_queue ( ) ;
2021-12-24 22:13:05 +00:00
if ( tw . at - > dist < c - > dist ) {
2021-12-25 00:36:25 +00:00
if ( debugflags & DF_GEOM )
println ( hlog , " smart shortcut updated " , c - > dist , " to " , tw . at - > dist ) ;
2021-12-24 22:13:05 +00:00
}
2021-12-25 20:32:08 +00:00
push_unify ( tw , tw0 ) ;
2021-12-24 22:13:05 +00:00
process_fix_queue ( ) ;
}
else {
2021-08-15 15:26:38 +00:00
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-11-01 16:22:50 +00:00
EX void ensure_shorter ( twalker cw ) {
/* if cw.peek() has shorter dist, ensure it exists */
/* only with w_known_distances */
if ( flags & w_known_distances ) {
swap_treestates ( ) ;
int d1 = cw . spin ;
auto oc = tcell_to_cell [ cw . at ] ;
d1 = gmod ( d1 - treestates [ oc - > master - > fieldval ] . parent_dir , oc - > type ) ;
cell * c1 = oc - > cmove ( d1 ) ;
// println(hlog, "cw=", cw, " oc=", oc, " c1=", c1, " d=", oc->master->distance, "=", cw.at->dist, " vs ", c1->master->distance);
bool ok = c1 - > master - > distance < cw . at - > dist ;
swap_treestates ( ) ;
if ( ok )
cw . at - > cmove ( cw . spin ) ;
}
}
2021-12-26 16:31:42 +00:00
void trace_root_path ( vector < int > & rp , twalker cw ) {
auto d = cw . peek ( ) - > dist ;
cw + = wstep ; auto scw = cw ;
2021-08-17 12:18:54 +00:00
2021-12-26 16:31:42 +00:00
bool side = ( flags & w_parent_side ) ;
2021-08-21 22:10:02 +00:00
2021-12-26 16:31:42 +00:00
next :
if ( d > 0 ) {
ufind ( cw ) ;
2021-08-19 23:35:19 +00:00
handle_distance_errors ( ) ;
2021-12-26 16:31:42 +00:00
auto cwd = get_parent_dir ( cw ) ;
for ( int i = 0 ; i < cw . at - > type ; i + + ) {
if ( ( ! side ) & & ( cw + i ) ! = cwd ) continue ;
tcell * c1 = cwd . peek ( ) ;
if ( ! c1 ) continue ;
be_solid ( c1 ) ;
handle_distance_errors ( ) ;
if ( c1 - > dist < d ) {
rp . push_back ( i ) ;
cw + = i ;
cw + = wstep ;
d - - ;
goto next ;
}
}
2021-11-01 16:22:50 +00:00
}
2021-12-26 16:31:42 +00:00
if ( d > 0 ) {
debuglist = { scw } ;
throw rulegen_failure ( " should not happen [trace] " ) ;
}
rp . push_back ( cw . to_spin ( 0 ) ) ;
if ( flags & w_parent_reverse ) reverse ( rp . begin ( ) , rp . end ( ) ) ;
2021-08-17 12:18:54 +00:00
}
2021-12-25 20:32:34 +00:00
EX int parent_updates ;
2021-07-29 12:07:30 +00:00
/** which neighbor will become the parent of c */
2021-12-24 22:08:14 +00:00
EX twalker get_parent_dir ( twalker & cw ) {
tcell * & c = cw . at ;
if ( c - > parent_dir ! = MYSTERY ) return twalker ( c , c - > parent_dir ) ;
2021-07-29 12:07:30 +00:00
int bestd = - 1 ;
vector < int > bestrootpath ;
be_solid ( c ) ;
2021-12-24 22:08:14 +00:00
auto oc = c ;
2021-07-29 12:07:30 +00:00
if ( c - > dist > 0 ) {
2022-07-11 10:18:58 +00:00
int n = c - > type ;
int k = cycle_size ( c - > id ) ;
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-11-01 16:25:50 +00:00
return true ;
/* if(old < i) old += n;
return old < = i + n / 2 ; */
2021-08-15 16:06:23 +00:00
} ;
int d = c - > dist ;
for ( int i = 0 ; i < n ; i + + ) {
2021-12-24 22:08:14 +00:00
ensure_shorter ( cw + i ) ;
2021-08-15 16:06:23 +00:00
tcell * c1 = c - > cmove ( i ) ;
be_solid ( c1 ) ;
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 16 ) 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-12-24 22:08:14 +00:00
ufind ( cw ) ; if ( d ! = cw . at - > dist | | oc ! = cw . at ) return get_parent_dir ( cw ) ;
2021-08-15 16:06:23 +00:00
}
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 16 ) println ( hlog , " nearer = " , nearer , " n= " , n , " k= " , k ) ;
2021-08-17 12:20:54 +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 ;
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 16 ) for ( auto ne : nearer ) println ( hlog , " beats " , tie ( ne , bestd ) , " = " , beats ( ne , bestd ) ) ;
2021-08-17 12:20:54 +00:00
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 + + ;
2021-12-26 16:31:42 +00:00
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 ;
}
2021-08-21 22:10:02 +00:00
2021-12-26 16:31:42 +00:00
bestd = bestfor ;
2021-08-21 22:10:02 +00:00
}
2021-08-15 16:06:23 +00:00
if ( bestd = = - 1 ) {
2021-08-24 01:37:09 +00:00
debuglist = { c } ;
2021-08-15 16:06:23 +00:00
throw rulegen_failure ( " should not happen " ) ;
2021-07-29 12:07:30 +00:00
}
}
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 16 ) println ( hlog , " set parent_dir to " , bestd ) ;
2021-07-29 12:07:30 +00:00
c - > parent_dir = bestd ;
2021-12-25 20:31:36 +00:00
if ( c - > old_parent_dir ! = MYSTERY & & c - > old_parent_dir ! = bestd & & c = = oc ) {
c - > any_nearer = c - > old_parent_dir ;
find_new_shortcuts ( c , c - > dist , c , bestd , 0 ) ;
}
2021-12-26 09:09:13 +00:00
parent_updates + + ;
2021-12-24 22:08:14 +00:00
return twalker ( c , bestd ) ;
2021-07-29 12:07:30 +00:00
}
/** determine states for tcells */
# if HDR
using aid_t = pair < int , int > ;
2022-07-13 15:25:04 +00:00
/* for leaves, id equals MYSTERY and dir equals treestate ID for this code */
2021-12-27 12:03:15 +00:00
struct analyzer_state {
int analyzer_id ;
int id , dir ;
2022-07-11 15:32:27 +00:00
map < int , analyzer_state * > substates ;
2022-07-13 15:25:04 +00:00
analyzer_state ( ) { id = MYSTERY ; dir = MYSTERY_LARGE ; } // for(int i=0; i<10; i++) substates[i] = nullptr; }
2021-12-27 12:03:15 +00:00
vector < twalker > inhabitants ;
2021-07-29 12:07:30 +00:00
} ;
2021-12-27 12:03:15 +00:00
2021-07-29 12:07:30 +00:00
# endif
2021-12-27 12:03:15 +00:00
int next_analyzer_id ;
2021-08-04 16:35:04 +00:00
2021-12-27 19:19:18 +00:00
EX vector < vector < analyzer_state * > > analyzers ;
2021-12-27 12:03:15 +00:00
EX vector < analyzer_state * > all_analyzers ;
analyzer_state * alloc_analyzer ( ) {
auto a = new analyzer_state ;
a - > analyzer_id = next_analyzer_id + + ;
all_analyzers . push_back ( a ) ;
return a ;
}
2021-07-29 12:07:30 +00:00
EX aid_t get_aid ( twalker cw ) {
ufind ( cw ) ;
auto ide = cw . at - > id ;
2022-07-11 10:18:58 +00:00
return { ide , gmod ( cw . to_spin ( 0 ) , cycle_size ( ide ) ) } ;
2021-07-29 12:07:30 +00:00
}
2022-08-14 17:26:13 +00:00
vector < int > gen_rule ( twalker cwmain , int id ) ;
2021-12-27 12:03:15 +00:00
void extend_analyzer ( twalker cwmain , int z , twalker giver ) {
ufind ( giver ) ;
ufind ( cwmain ) ;
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
vector < twalker > giver_sprawl , main_sprawl , sub_sprawl ;
vector < analyzer_state * > giver_states , main_states , sub_states ;
2022-08-14 17:26:13 +00:00
2021-12-27 12:03:15 +00:00
id_at_spin ( cwmain , main_sprawl , main_states ) ;
2022-08-14 17:26:13 +00:00
2021-12-27 12:03:15 +00:00
id_at_spin ( ( cwmain + z ) + wstep , sub_sprawl , sub_states ) ;
2022-08-14 17:26:13 +00:00
2021-12-27 12:03:15 +00:00
id_at_spin ( ( giver + z ) + wstep , giver_sprawl , giver_states ) ;
int currently_at = 1 + z ;
vector < int > idlist ;
for ( int i = 0 ; ; i + + ) {
2022-08-13 21:13:22 +00:00
if ( i = = isize ( sub_states ) | | i = = isize ( giver_states ) ) {
2021-12-27 12:30:14 +00:00
/* may happen if something changed but not updated */
2022-08-13 21:13:22 +00:00
cwmain . at - > code = MYSTERY_LARGE ;
giver . at - > code = MYSTERY_LARGE ;
( cwmain + z + wstep ) . at - > code = MYSTERY_LARGE ;
( giver + z + wstep ) . at - > code = MYSTERY_LARGE ;
2021-12-27 12:30:14 +00:00
throw rulegen_retry ( " reached the end " ) ;
2022-08-13 21:13:22 +00:00
}
2021-12-27 12:03:15 +00:00
if ( giver_states [ i ] ! = sub_states [ i ] ) {
i - - ;
while ( i ! = 0 ) {
idlist . push_back ( i ) ;
i = giver_states [ i ] - > id ;
2021-07-29 12:07:30 +00:00
}
2021-12-27 12:03:15 +00:00
break ;
2021-07-29 12:07:30 +00:00
}
}
2021-12-27 12:03:15 +00:00
reverse ( idlist . begin ( ) , idlist . end ( ) ) ;
auto v = main_states . back ( ) ;
auto v1 = v ;
int new_id = isize ( main_states ) - 1 ;
for ( auto l : idlist ) {
/* check if already tested */
for ( int u = 1 ; u < isize ( main_states ) ; u + + )
if ( main_states [ u ] - > id = = currently_at & & main_states [ u ] - > dir = = sub_states [ l ] - > dir ) {
currently_at = u ;
goto next_l ;
}
v - > id = currently_at ;
v - > dir = sub_states [ l ] - > dir ;
2022-07-12 08:41:23 +00:00
for ( auto p : sub_states [ l ] - > substates ) {
int i = p . first ;
if ( sub_states [ l ] - > substates [ i ] = = sub_states [ l + 1 ] ) {
v = v - > substates [ i ] = alloc_analyzer ( ) ;
currently_at = new_id + + ;
goto next_l ;
}
2021-12-27 12:03:15 +00:00
}
next_l : ;
2021-12-26 21:33:33 +00:00
}
2021-12-27 12:03:15 +00:00
update_all_codes ( v1 ) ;
2021-07-29 12:07:30 +00:00
}
# if HDR
struct treestate {
int id ;
bool known ;
vector < int > rules ;
twalker giver ;
int sid ;
int parent_dir ;
2021-12-27 22:09:37 +00:00
int astate ;
2021-12-24 22:08:14 +00:00
twalker where_seen ;
2021-07-29 12:07:30 +00:00
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 ;
} ;
2023-08-23 17:44:37 +00:00
static constexpr int C_IGNORE = 0 ;
static constexpr int C_CHILD = 1 ;
static constexpr int C_UNCLE = 2 ;
static constexpr int C_EQUAL = 4 ;
static constexpr int C_NEPHEW = 6 ;
static constexpr int C_PARENT = 8 ;
2021-07-29 12:07:30 +00:00
# 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 ) {
2021-12-24 22:08:14 +00:00
auto cwd = get_parent_dir ( cw ) ;
if ( cw = = cwd ) cw = addstep ( cw ) ;
2021-08-17 21:05:56 +00:00
else {
2021-08-18 23:27:26 +00:00
auto cw1 = addstep ( cw ) ;
2021-12-24 22:08:14 +00:00
auto cwd = get_parent_dir ( cw1 ) ;
if ( cwd = = cw1 ) cw = cw1 ;
2021-08-17 21:05:56 +00:00
}
2021-08-17 15:04:17 +00:00
cw + = delta ;
}
2021-12-29 02:35:31 +00:00
EX vector < tcell * > sidecaches_to_clear ;
void clear_sidecache ( ) {
if ( sidecaches_to_clear . size ( ) ) {
for ( auto c : sidecaches_to_clear )
c - > which_side = c - > known_sides = 0 ;
sidecaches_to_clear . clear ( ) ;
}
}
void set_sidecache ( twalker what , int side ) {
auto c = what . at ;
if ( c - > known_sides = = 0 ) sidecaches_to_clear . push_back ( c ) ;
unsigned long long bit = 1ll < < what . spin ;
c - > known_sides | = bit ;
if ( side > 0 )
c - > which_side | = bit ;
}
int get_sidecache ( twalker what ) {
auto c = what . at ;
unsigned long long bit = 1ll < < what . spin ;
if ( c - > known_sides & bit )
return ( c - > which_side & bit ) ? 1 : - 1 ;
return 0 ;
}
2021-08-18 23:24:01 +00:00
2021-08-17 12:24:32 +00:00
int get_side ( twalker what ) {
2022-07-11 15:32:27 +00:00
if ( WDIM = = 3 ) throw hr_exception ( " called get_side " ) ;
2021-08-21 22:10:02 +00:00
bool side = ! ( flags & w_no_sidecache ) ;
2021-12-29 03:20:58 +00:00
bool fast = ( flags & w_slow_side ) ;
2021-08-21 22:10:02 +00:00
if ( side ) {
2021-12-29 02:35:31 +00:00
auto w = get_sidecache ( what ) ;
if ( w ) return w ;
2021-08-21 22:10:02 +00:00
}
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 ) {
2021-12-24 22:08:14 +00:00
cw = get_parent_dir ( cw ) ;
if ( cw . peek ( ) - > dist > = cw . at - > dist ) {
2021-08-21 22:10:02 +00:00
handle_distance_errors ( ) ;
if ( debugflags & DF_GEOM )
2021-12-24 22:08:14 +00:00
println ( hlog , " get_parent_dir error at " , cw , " and " , cw . at - > move ( cw . spin ) , " : " , cw . at - > dist , " :: " , cw . at - > move ( cw . spin ) - > dist ) ;
2021-08-21 22:10:02 +00:00
throw rulegen_failure ( " get_parent_dir error " ) ;
}
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-17 00:32:03 +00:00
2021-12-24 22:08:14 +00:00
if ( w . at - > dist & & ! single_live_branch_close_to_root . count ( w . at ) ) {
twalker wd = get_parent_dir ( w ) ;
ufind ( tw ) ;
res = wd . to_spin ( w . spin ) - wd . to_spin ( tw . spin ) ;
2021-08-21 22:10:02 +00:00
}
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-12-29 02:35:31 +00:00
static vector < twalker > lstack = { nullptr } , rstack = { nullptr } ;
lstack . resize ( 1 ) ; rstack . resize ( 1 ) ;
2021-08-21 22:10:02 +00:00
while ( res = = 99 ) {
2021-08-18 23:24:01 +00:00
handle_distance_errors ( ) ;
2021-12-29 03:27:17 +00:00
steps + + ; if ( steps > current_getside ) {
2021-08-18 23:24:01 +00:00
debuglist = { what , to_what , wl , wr } ;
2021-12-29 03:20:19 +00:00
checks_to_skip . clear ( ) ;
2021-12-25 20:32:34 +00:00
if ( parent_updates ) throw rulegen_retry ( " xsidefreeze " ) ;
2021-12-29 03:27:17 +00:00
else if ( steps > max_getside ) {
throw rulegen_failure ( " xsidefreeze " ) ;
}
else {
current_getside * = 2 ;
throw rulegen_retry ( " xsidefreeze double " ) ;
}
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 ) {
2022-02-01 15:57:34 +00:00
if ( side & & get_sidecache ( wl ) = = 1 ) wl + = wstep ;
2021-08-19 22:44:24 +00:00
treewalk ( wl , - 1 ) ;
2021-08-21 22:10:02 +00:00
if ( wl = = to_what ) { res = 1 ; }
2022-02-01 15:57:34 +00:00
if ( ! side ) ;
else if ( lstack . back ( ) = = wl + wstep ) {
2021-12-29 02:35:31 +00:00
set_sidecache ( lstack . back ( ) , 1 ) ;
set_sidecache ( wl , - 1 ) ;
lstack . pop_back ( ) ;
}
else if ( wl . at - > parent_dir ! = wl . spin & & ( wl + wstep ) . at - > parent_dir ! = ( wl + wstep ) . spin ) lstack . push_back ( wl ) ;
2021-08-19 22:44:24 +00:00
}
if ( gr ) {
2022-02-01 15:57:34 +00:00
if ( side & & get_sidecache ( wr ) = = - 1 ) wr + = wstep ;
2021-08-19 22:44:24 +00:00
treewalk ( wr , + 1 ) ;
2021-08-21 22:10:02 +00:00
if ( wr = = to_what ) { res = - 1 ; }
2022-02-01 15:57:34 +00:00
if ( ! side ) ;
else if ( rstack . back ( ) = = wr + wstep ) {
2021-12-29 02:35:31 +00:00
set_sidecache ( rstack . back ( ) , - 1 ) ;
set_sidecache ( wr , + 1 ) ;
rstack . pop_back ( ) ;
}
else if ( wr . at - > parent_dir ! = wr . spin & & ( wr + wstep ) . at - > parent_dir ! = ( wr + wstep ) . spin ) rstack . push_back ( wr ) ;
2021-08-19 22:44:24 +00:00
}
2021-07-29 12:07:30 +00:00
}
2021-08-21 22:10:02 +00:00
2021-12-29 02:35:31 +00:00
if ( side & & res )
set_sidecache ( what , res ) , set_sidecache ( what + wstep , - res ) ;
2021-08-21 22:10:02 +00:00
return res ;
2021-07-29 12:07:30 +00:00
}
2022-02-01 15:57:47 +00:00
EX int move_code ( twalker cs ) {
2021-12-27 12:03:15 +00:00
bool child = false ;
if ( cs . at - > dist ) {
auto csd = get_parent_dir ( cs ) ;
child = cs = = csd ;
}
if ( child )
return C_CHILD ;
else {
auto cs2 = cs + wstep ;
be_solid ( cs . at ) ; ufind ( cs ) ; ufind ( cs2 ) ; be_solid ( cs2 . at ) ;
fix_distances ( cs . at ) ;
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-07-11 15:32:27 +00:00
if ( WDIM = = 3 ) {
if ( cs2 . at - > parent_dir = = cs2 . spin ) return C_PARENT ;
2022-08-13 21:13:37 +00:00
else return get_roadsign ( cs + wstep ) ;
2022-07-11 15:32:27 +00:00
}
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 15:32:27 +00:00
2022-08-17 15:32:15 +00:00
int y = cs . at - > dist - cs . peek ( ) - > dist ;
int x ;
2021-12-27 12:03:15 +00:00
if ( ! ( flags & w_no_relative_distance ) ) x = C_EQUAL ;
else if ( y = = 1 ) x = C_NEPHEW ;
else if ( y = = 0 ) x = C_EQUAL ;
else if ( y = = - 1 ) x = C_UNCLE ;
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 ) ) ;
auto gs = get_side ( cs ) ;
if ( gs = = 0 & & x = = C_UNCLE ) x = C_PARENT ;
if ( gs > 0 ) x + + ;
return x ;
}
}
EX void id_at_spin ( twalker cw , vector < twalker > & sprawl , vector < analyzer_state * > & states ) {
2021-07-29 12:07:30 +00:00
ufind ( cw ) ;
2021-12-27 12:03:15 +00:00
auto aid = get_aid ( cw ) ;
2021-12-27 19:19:18 +00:00
auto a_ptr = & ( analyzers [ aid . first ] [ aid . second ] ) ;
2021-12-27 12:03:15 +00:00
sprawl = { cw } ;
states = { nullptr } ;
indenter ind ( 2 ) ;
while ( true ) {
auto & a = * a_ptr ;
if ( ! a ) {
a = alloc_analyzer ( ) ;
2021-07-29 12:07:30 +00:00
}
2021-12-27 12:03:15 +00:00
states . push_back ( a ) ;
2022-08-19 15:43:16 +00:00
if ( isize ( sprawl ) < = cw . at - > type ) {
2021-12-27 12:03:15 +00:00
a - > id = 0 , a - > dir = isize ( sprawl ) - 1 ;
// println(hlog, "need to go in direction ", a->dir);
2021-07-29 12:07:30 +00:00
}
2021-12-27 12:03:15 +00:00
if ( a - > id = = MYSTERY ) {
return ;
}
2022-07-12 08:41:23 +00:00
if ( a - > id > = isize ( sprawl ) ) {
println ( hlog , sprawl ) ;
println ( hlog , " id = " , a - > id ) ;
throw hr_exception ( " sprawl error " ) ;
}
2021-12-27 12:03:15 +00:00
auto t = sprawl [ a - > id ] ;
twalker tw = t + a - > dir ;
ufind ( tw ) ;
tw . cpeek ( ) ;
ufind ( tw ) ;
int mc = move_code ( tw + wstep ) ;
sprawl . push_back ( tw + wstep ) ;
a_ptr = & ( a - > substates [ mc ] ) ;
2021-07-29 12:07:30 +00:00
}
}
2021-12-24 22:08:14 +00:00
EX pair < int , int > get_code ( twalker & cw ) {
tcell * c = cw . at ;
2021-12-28 10:34:50 +00:00
if ( c - > code ! = MYSTERY_LARGE & & 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 ) ;
2021-12-24 22:08:14 +00:00
twalker cd = c - > dist = = 0 ? twalker ( c , 0 ) : get_parent_dir ( cw ) ;
if ( cd . at ! = c ) ufind ( cw ) ;
2021-07-29 12:07:30 +00:00
indenter ind ( 2 ) ;
2021-12-27 12:03:15 +00:00
static vector < twalker > sprawl ;
static vector < analyzer_state * > states ;
id_at_spin ( cd , sprawl , states ) ;
auto v = states . back ( ) ;
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
v - > inhabitants . push_back ( cw ) ;
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
cd . at - > code = v - > analyzer_id ;
return { cd . spin , v - > analyzer_id } ;
}
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
EX pair < int , int > get_treestate_id ( twalker & cw ) {
auto co = get_code ( cw ) ;
auto v = all_analyzers [ co . second ] ;
2022-07-13 15:25:04 +00:00
if ( v - > dir = = MYSTERY_LARGE ) {
2021-12-27 12:03:15 +00:00
int id = isize ( treestates ) ;
v - > dir = id ;
treestates . emplace_back ( ) ;
auto & nts = treestates . back ( ) ;
nts . id = id ;
nts . where_seen = cw ;
nts . known = false ;
nts . is_live = true ;
2021-12-27 22:09:37 +00:00
nts . astate = co . second ;
2021-12-27 12:03:15 +00:00
}
co . second = v - > dir ;
return co ;
2021-07-29 12:07:30 +00:00
}
/* == rule generation == */
EX int rule_root ;
vector < int > gen_rule ( twalker cwmain ) ;
EX int try_count ;
2021-12-24 22:08:14 +00:00
EX vector < twalker > important ;
2021-07-29 12:07:30 +00:00
2021-12-24 22:08:14 +00:00
vector < twalker > cq ;
2021-07-29 12:07:30 +00:00
# if HDR
/* special codes */
2023-08-23 17:44:37 +00:00
static constexpr int DIR_UNKNOWN = - 1 ;
static constexpr int DIR_LEFT = - 4 ;
static constexpr int DIR_RIGHT = - 5 ;
static constexpr int DIR_PARENT = - 6 ;
2021-07-29 12:07:30 +00:00
# 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 ;
2021-12-24 22:08:14 +00:00
twalker c1 = front + wstep ;
be_solid ( c1 . at ) ;
2021-07-29 12:07:30 +00:00
if ( a = = 0 & & cwmain . at - > dist ) { cids . push_back ( DIR_PARENT ) ; continue ; }
2021-12-24 22:08:14 +00:00
if ( c1 . at - > dist < = cwmain . at - > dist ) { cids . push_back ( DIR_UNKNOWN ) ; continue ; }
2021-12-27 12:03:15 +00:00
auto co = get_treestate_id ( c1 ) ;
2021-07-29 12:07:30 +00:00
auto & d1 = co . first ;
auto & id1 = co . second ;
2021-12-24 22:08:14 +00:00
if ( c1 . at - > cmove ( d1 ) ! = cwmain . at | | c1 . at - > c . spin ( d1 ) ! = front . spin ) {
2021-07-29 12:07:30 +00:00
cids . push_back ( DIR_UNKNOWN ) ; continue ;
}
cids . push_back ( id1 ) ;
}
2021-08-19 23:36:14 +00:00
2022-07-11 15:32:27 +00:00
if ( WDIM ! = 3 ) for ( int i = 0 ; i < isize ( cids ) ; i + + ) if ( cids [ i ] = = DIR_UNKNOWN )
2021-12-27 12:03:15 +00:00
cids [ i ] = get_side ( cwmain + i ) < 0 ? DIR_RIGHT : DIR_LEFT ;
2021-08-19 23:36:14 +00:00
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-07-11 15:32:27 +00:00
if ( WDIM = = 3 ) for ( int i = 0 ; i < isize ( cids ) ; i + + ) if ( cids [ i ] = = DIR_UNKNOWN )
cids [ i ] = get_roadsign ( cwmain + i ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-07-11 15:32:27 +00:00
2021-07-29 12:07:30 +00:00
return cids ;
}
2021-12-26 21:33:33 +00:00
vector < reaction_t > queued_extensions ;
void handle_queued_extensions ( ) {
if ( queued_extensions . empty ( ) ) return ;
for ( auto & r : queued_extensions ) r ( ) ;
throw rulegen_retry ( " mismatch error " ) ;
}
2022-08-06 23:45:26 +00:00
EX void rules_iteration_for ( twalker & cw ) {
2021-07-29 12:07:30 +00:00
indenter ri ( 2 ) ;
2021-12-24 22:08:14 +00:00
ufind ( cw ) ;
2021-12-27 12:03:15 +00:00
auto co = get_treestate_id ( cw ) ;
2021-07-29 12:07:30 +00:00
auto & d = co . first ;
auto & id = co . second ;
2021-12-24 22:08:14 +00:00
twalker cwmain ( cw . at , d ) ;
2021-07-29 12:07:30 +00:00
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-12-24 22:08:14 +00:00
ts . is_root = cw . at - > 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 ) ;
}
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 } ;
2022-08-17 15:33:52 +00:00
cwmain . at - > code = MYSTERY_LARGE ;
ts . giver . at - > code = MYSTERY_LARGE ;
2021-12-27 19:20:10 +00:00
throw rulegen_retry ( " neg rule mismatch " ) ;
2021-08-19 23:36:14 +00:00
}
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
auto tg = ts . giver ;
2021-07-29 12:07:30 +00:00
2021-12-27 12:03:15 +00:00
if ( ! ( flags & w_no_queued_extensions ) ) {
queued_extensions . push_back ( [ cwmain , z , tg ] {
extend_analyzer ( cwmain , z , tg ) ;
} ) ;
return ;
2021-07-29 12:07:30 +00:00
}
2021-12-27 12:03:15 +00:00
extend_analyzer ( cwmain , z , tg ) ;
mismatches + + ;
debuglist = { cwmain , ts . giver } ;
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 + + ) {
2024-03-19 17:53:37 +00:00
if ( treestates [ id ] . giver . at = = nullptr ) { new_id [ id ] = new_ids + + ; println ( hlog , " no giver in minimize_rules for state " , id ) ; continue ; }
2021-07-29 12:07:30 +00:00
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 ] ;
}
}
}
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 > ;
2021-12-24 22:08:14 +00:00
tsinfo get_tsinfo ( twalker & tw ) {
2021-12-27 12:03:15 +00:00
auto co = get_treestate_id ( tw ) ;
2021-08-17 15:04:17 +00:00
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-24 01:37:09 +00:00
if ( ts . rules . empty ( ) ) throw rulegen_retry ( " empty rule " ) ;
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 ) {
2021-12-24 22:08:14 +00:00
auto tw1 = tw + wstep ;
2021-12-27 12:03:15 +00:00
auto co = get_treestate_id ( tw1 ) ;
2021-12-24 22:08:14 +00:00
if ( co . second ! = id | | co . first ! = tw1 . 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
}
2021-12-27 22:09:37 +00:00
bool examine_branch ( int id , int left , int right ) {
2022-07-11 15:32:27 +00:00
if ( WDIM = = 3 ) return true ;
2021-07-29 12:07:30 +00:00
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 )
2021-11-01 16:22:50 +00:00
println ( hlog , " need to examine branches " , tie ( left , right ) , " of " , id , " starting from " , rg , " step = " , rg + left + wstep , " vs " , rg + right + wstep ) ;
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-12-29 03:27:17 +00:00
if ( steps > current_examine_branch ) {
2021-08-17 15:04:17 +00:00
debuglist = { rg + left , wl , wr } ;
2021-12-29 02:34:31 +00:00
if ( skipped_branches . size ( ) ) {
checks_to_skip . clear ( ) ;
throw rulegen_retry ( " max_examine_branch exceeded after a skipped check " ) ;
}
else if ( branch_conflicts_seen . size ( ) )
2021-11-03 09:20:29 +00:00
/* may be not a real problem, but caused by incorrect detection of live branches */
throw rulegen_retry ( " max_examine_branch exceeded after a conflict " ) ;
2021-12-29 03:27:17 +00:00
else if ( steps > max_examine_branch )
2021-11-03 09:20:29 +00:00
throw rulegen_failure ( " max_examine_branch exceeded " ) ;
2021-12-29 03:27:17 +00:00
else {
current_examine_branch * = 2 ;
throw rulegen_retry ( " max_examine_branch exceeded, doubling " ) ;
}
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
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 32 )
2021-11-01 16:22:50 +00:00
println ( hlog , " wl = " , wl , " -> " , wl + wstep , " R " , rl , " wr = " , wr , " -> " , wr + wstep , " R " , rr , " lstack = " , lstack , " rstack = " , rstack ) ;
2021-08-17 15:04:17 +00:00
if ( rl = = DIR_RIGHT & & rr = = DIR_LEFT & & lstack . empty ( ) & & rstack . empty ( ) ) {
vector < tsinfo > hash ;
push_deadstack ( hash , wl , tsl , - 1 ) ;
2021-11-01 16:20:08 +00:00
hash . emplace_back ( - 1 , wl . at - > dist - wr . at - > dist ) ;
2021-08-17 15:04:17 +00:00
push_deadstack ( hash , wr , tsr , + 1 ) ;
2022-08-17 15:43:25 +00:00
if ( rdebug_flags & 32 )
2021-11-01 16:22:50 +00:00
println ( hlog , " got hash: " , hash ) ;
if ( verified_branches . count ( hash ) ) {
2021-12-27 22:09:37 +00:00
return true ;
2021-11-01 16:22:50 +00:00
}
2021-08-17 15:04:17 +00:00
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-12-27 22:09:37 +00:00
return false ;
2021-08-21 22:10:02 +00:00
}
2021-07-29 12:07:30 +00:00
}
/* == main algorithm == */
2021-12-27 12:03:15 +00:00
bool need_clear_codes ;
2022-07-13 17:31:24 +00:00
EX void clear_codes ( ) {
2021-12-27 12:03:15 +00:00
need_clear_codes = false ;
for ( auto a : all_analyzers ) {
2021-12-28 10:34:50 +00:00
for ( auto tw : a - > inhabitants ) tw . at - > code = MYSTERY_LARGE ;
2021-12-27 12:03:15 +00:00
a - > inhabitants . clear ( ) ;
}
2021-07-29 12:07:30 +00:00
}
2021-12-24 22:08:14 +00:00
void find_single_live_branch ( twalker & at ) {
2021-08-17 00:32:03 +00:00
handle_distance_errors ( ) ;
2021-12-24 22:08:14 +00:00
rules_iteration_for ( at ) ;
2021-12-27 12:03:15 +00:00
handle_queued_extensions ( ) ;
int id = get_treestate_id ( at ) . second ;
2021-08-17 00:32:03 +00:00
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 ) ;
2021-12-24 22:08:14 +00:00
if ( ! treestates [ r [ i ] ] . is_live | | q = = 1 ) {
auto at1 = at + i + wstep ;
find_single_live_branch ( at1 ) ;
}
2021-08-17 00:32:03 +00:00
}
}
2021-12-27 12:03:15 +00:00
EX void clean_analyzers ( ) {
2021-12-28 10:38:45 +00:00
for ( auto a : all_analyzers ) for ( auto tw : a - > inhabitants ) tw . at - > code = MYSTERY_LARGE ;
2021-12-27 12:03:15 +00:00
for ( auto a : all_analyzers ) delete a ;
2021-12-27 19:19:18 +00:00
for ( auto & av : analyzers ) for ( auto & a : av ) a = nullptr ;
2021-12-27 12:03:15 +00:00
all_analyzers . clear ( ) ;
next_analyzer_id = 0 ;
}
EX void clean_data ( ) {
clean_analyzers ( ) ;
2021-12-28 10:36:44 +00:00
checks_to_skip . clear ( ) ;
2021-08-19 11:19:11 +00:00
important = t_origin ;
}
2021-12-27 12:03:15 +00:00
EX void clear_sidecache_and_codes ( ) {
2021-12-29 02:35:31 +00:00
clear_sidecache ( ) ;
2021-12-27 12:03:15 +00:00
need_clear_codes = true ;
}
EX void update_all_codes ( analyzer_state * a ) {
vector < twalker > old ;
swap ( old , a - > inhabitants ) ;
for ( auto tw : old ) {
ufind ( tw ) ;
if ( tw . at - > code = = a - > analyzer_id )
2021-12-28 10:34:50 +00:00
tw . at - > code = MYSTERY_LARGE ;
2021-12-27 12:03:15 +00:00
}
}
2021-08-19 11:19:11 +00:00
EX void clean_parents ( ) {
2021-12-27 12:03:15 +00:00
clear_sidecache_and_codes ( ) ;
2021-08-19 11:19:11 +00:00
clean_data ( ) ;
auto c = first_tcell ;
while ( c ) { c - > parent_dir = MYSTERY ; c = c - > next ; }
}
2022-08-17 15:33:36 +00:00
int qshortcuts ( ) {
int res = 0 ;
for ( auto & sh : shortcuts ) res + = isize ( sh ) ;
return res ;
}
2021-12-27 12:03:15 +00:00
void clear_treestates ( ) {
treestates . clear ( ) ;
for ( auto a : all_analyzers )
2022-07-13 15:25:04 +00:00
if ( a - > id = = MYSTERY ) a - > dir = MYSTERY_LARGE ;
2021-12-27 12:03:15 +00:00
}
2021-08-18 23:21:44 +00:00
EX void rules_iteration ( ) {
try_count + + ;
2021-08-23 22:33:38 +00:00
debuglist = { } ;
2021-08-18 23:21:44 +00:00
2021-12-26 21:33:33 +00:00
queued_extensions . clear ( ) ;
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-12-25 20:32:45 +00:00
clean_parents ( ) ;
2021-08-18 23:21:44 +00:00
}
2022-08-17 15:33:36 +00:00
if ( rdebug_flags & 1 ) println ( hlog , " attempt: " , try_count , " important = " , isize ( important ) , " cells = " , tcellcount , " shortcuts = " , qshortcuts ( ) ) ;
2021-08-18 23:21:44 +00:00
2021-12-25 20:32:34 +00:00
parent_updates = 0 ;
2021-12-27 12:03:15 +00:00
clear_treestates ( ) ;
if ( need_clear_codes ) clear_codes ( ) ;
2021-07-29 12:07:30 +00:00
cq = important ;
2022-08-17 15:33:36 +00:00
if ( rdebug_flags & 2 )
2021-07-29 12:07:30 +00:00
println ( hlog , " important = " , cq ) ;
for ( int i = 0 ; i < isize ( cq ) ; i + + ) {
rules_iteration_for ( cq [ i ] ) ;
}
2021-12-26 21:33:33 +00:00
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-12-27 12:03:15 +00:00
rule_root = get_treestate_id ( 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-12-26 21:33:33 +00:00
handle_queued_extensions ( ) ;
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 + + ;
}
2022-08-17 15:33:36 +00:00
if ( rdebug_flags & 4 )
2021-07-29 12:07:30 +00:00
println ( hlog , " deadend states found: " , new_deadends ) ;
}
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
2022-08-19 15:39:15 +00:00
if ( WDIM = = 2 ) for ( int id = 0 ; id < isize ( treestates ) ; id + + ) if ( treestates [ id ] . is_root & & ! treestates [ id ] . is_live ) {
2021-08-23 13:50:08 +00:00
auto r = treestates [ id ] . rules ;
for ( int i = 0 ; i < isize ( r ) ; i + + ) if ( r [ i ] > = 0 ) {
examine_branch ( id , i , i ) ;
break ;
}
}
2021-12-26 21:33:33 +00:00
handle_queued_extensions ( ) ;
2021-12-29 02:34:31 +00:00
skipped_branches . clear ( ) ;
2021-12-27 22:09:37 +00:00
auto examine_or_skip_branch = [ & ] ( int id , int fb , int sb ) {
2021-12-29 03:20:19 +00:00
if ( flags & w_no_branch_skipping ) {
examine_branch ( id , fb , sb ) ;
return ;
}
2021-12-27 22:09:37 +00:00
auto b = branch_check { treestates [ id ] . astate , fb , sb } ;
if ( checks_to_skip . count ( b ) ) {
skipped_branches . emplace_back ( [ id , fb , sb ] { examine_branch ( id , fb , sb ) ; } ) ;
return ;
}
if ( examine_branch ( id , fb , sb ) ) checks_to_skip . insert ( b ) ;
} ;
2024-03-19 17:53:09 +00:00
if ( WDIM = = 2 & & ( flags & w_optimize2 ) ) optimize ( ) ;
2022-08-19 15:39:15 +00:00
if ( WDIM = = 2 ) 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 )
2021-12-27 22:09:37 +00:00
examine_or_skip_branch ( id , last_live_branch , i ) ;
2021-07-29 12:07:30 +00:00
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-12-29 03:20:58 +00:00
if ( ( flags & w_slow_side ) & & 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 } ;
2021-12-27 12:03:15 +00:00
clear_sidecache_and_codes ( ) ;
2021-08-17 00:32:03 +00:00
throw rulegen_retry ( " single live branch " ) ;
}
2021-12-27 22:09:37 +00:00
if ( treestates [ id ] . is_root )
examine_or_skip_branch ( id , last_live_branch , first_live_branch ) ;
2021-07-29 12:07:30 +00:00
}
2021-08-22 12:31:13 +00:00
2021-12-27 22:09:37 +00:00
after_branches :
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-12-26 21:33:33 +00:00
handle_queued_extensions ( ) ;
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
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-07-13 17:31:24 +00:00
if ( WDIM = = 3 ) {
check_road_shortcuts ( ) ;
2022-08-19 15:41:16 +00:00
optimize ( ) ;
N = isize ( important ) ;
check_validity_3d ( ) ;
2022-07-13 17:31:24 +00:00
}
2023-04-11 15:10:04 +00:00
# endif
2022-07-13 17:31:24 +00:00
2021-12-27 22:09:37 +00:00
if ( skipped_branches . size ( ) ) {
checks_to_skip . clear ( ) ;
for ( auto sb : skipped_branches ) sb ( ) ;
skipped_branches . clear ( ) ;
goto after_branches ;
}
2022-08-19 15:39:15 +00:00
if ( WDIM = = 2 ) minimize_rules ( ) ;
2021-07-29 12:07:30 +00:00
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 ;
2021-12-28 10:34:50 +00:00
c - > code = MYSTERY_LARGE ;
2021-07-29 12:07:30 +00:00
c - > distance_fixed = false ;
c = c - > next ;
}
2021-10-01 06:11:50 +00:00
in_fixing = false ; fix_queue = std : : queue < reaction_t > { } ;
2021-07-29 12:07:30 +00:00
}
2021-11-01 16:17:16 +00:00
EX void cleanup ( ) {
2021-07-29 12:07:30 +00:00
clear_tcell_data ( ) ;
2021-12-27 12:03:15 +00:00
clean_analyzers ( ) ;
2021-07-29 12:07:30 +00:00
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 ( ) ;
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-07-11 15:32:27 +00:00
cleanup3 ( ) ;
2023-04-11 15:10:04 +00:00
# endif
2021-07-29 12:07:30 +00:00
}
2021-11-01 16:17:16 +00:00
EX void clear_all ( ) {
2021-07-29 12:07:30 +00:00
treestates . clear ( ) ;
cleanup ( ) ;
}
2021-08-23 22:33:52 +00:00
EX int origin_id ;
2021-08-25 10:57:50 +00:00
EX unsigned start_time ;
EX void check_timeout ( ) {
if ( SDL_GetTicks ( ) > start_time + 1000 * rulegen_timeout )
throw rulegen_surrender ( " timeout " ) ;
}
2021-07-29 12:07:30 +00:00
EX void generate_rules ( ) {
2021-08-25 10:57:50 +00:00
start_time = SDL_GetTicks ( ) ;
2021-07-29 12:07:30 +00:00
delete_tmap ( ) ;
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-08-26 10:27:57 +00:00
if ( WDIM = = 3 & & reg3 : : in_hrmap_rule_or_subrule ( ) ) {
2022-07-11 15:32:27 +00:00
stop_game ( ) ;
2022-08-26 10:27:57 +00:00
reg3 : : consider_rules = 0 ;
2022-07-11 15:32:27 +00:00
flags | = w_numerical ;
start_game ( ) ;
}
2022-08-26 11:21:36 +00:00
else if ( WDIM = = 3 ) {
flags | = w_numerical ;
}
2023-04-11 15:10:04 +00:00
else
# endif
if ( ! arb : : in ( ) ) try {
2021-07-29 14:15:40 +00:00
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-12-28 10:35:37 +00:00
hard_parents = single_live_branches = double_live_branches = all_solid_errors = solid_errors = 0 ;
2022-02-01 15:57:13 +00:00
next_distance_warning = first_restart_on ;
current_getside = first_restart_on ;
current_examine_branch = first_restart_on ;
2021-07-29 12:07:30 +00:00
2022-07-11 14:22:39 +00:00
int NS = number_of_types ( ) ;
2021-12-27 19:19:18 +00:00
shortcuts . resize ( NS ) ;
analyzers . resize ( NS ) ;
2022-07-11 10:18:58 +00:00
for ( int i = 0 ; i < NS ; i + + ) analyzers [ i ] . resize ( cycle_size ( i ) ) ;
2021-12-27 19:19:18 +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 ( ) ;
2022-02-01 15:57:34 +00:00
sidecaches_to_clear . clear ( ) ;
2021-12-27 12:03:15 +00:00
clear_sidecache_and_codes ( ) ;
2021-10-01 06:11:50 +00:00
fix_queue = queue < reaction_t > ( ) ; ; in_fixing = false ;
2021-08-22 12:30:24 +00:00
2021-11-01 16:24:54 +00:00
if ( flags & ( w_numerical | w_known_structure ) ) {
if ( flags & w_known_structure ) swap_treestates ( ) ;
stop_game ( ) ;
2021-08-22 12:30:24 +00:00
start_game ( ) ;
cell * s = currentmap - > gamestart ( ) ;
2022-07-11 10:18:58 +00:00
tcell * c = gen_tcell ( get_id ( s ) ) ;
2021-08-22 12:30:24 +00:00
cell_to_tcell [ s ] = c ;
tcell_to_cell [ c ] = s ;
c - > dist = 0 ;
2021-12-24 22:08:14 +00:00
t_origin . push_back ( twalker ( c , 0 ) ) ;
2021-11-01 16:24:54 +00:00
2022-07-11 10:18:58 +00:00
if ( ! ( flags & w_single_origin ) )
add_other_origins ( NS ) ;
2021-11-01 16:24:54 +00:00
if ( flags & w_known_structure ) swap_treestates ( ) ;
2021-08-22 12:30:24 +00:00
}
else if ( flags & w_single_origin ) {
2021-08-23 22:33:52 +00:00
tcell * c = gen_tcell ( origin_id ) ;
2021-08-21 22:10:02 +00:00
c - > dist = 0 ;
2021-12-24 22:08:14 +00:00
t_origin . push_back ( twalker ( c , 0 ) ) ;
2021-08-21 22:10:02 +00:00
}
else for ( auto & ts : arb : : current . shapes ) {
2021-07-30 13:18:32 +00:00
tcell * c = gen_tcell ( ts . id ) ;
c - > dist = 0 ;
2021-12-24 22:08:14 +00:00
t_origin . push_back ( twalker ( c , 0 ) ) ;
2021-07-30 13:18:32 +00:00
}
2021-08-22 17:42:07 +00:00
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-08-06 23:52:51 +00:00
if ( GDIM = = 3 ) build_cycle_data ( ) ;
2023-04-11 15:10:04 +00:00
# endif
2022-08-06 23:52:51 +00:00
2021-10-01 06:11:50 +00:00
bfs_queue = queue < tcell * > ( ) ;
2021-12-24 22:08:14 +00:00
if ( flags & w_bfs ) for ( auto c : t_origin ) bfs_queue . push ( c . at ) ;
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
2022-07-17 10:51:10 +00:00
rule_iterations ( ) ;
}
EX void rule_iterations ( ) {
2021-08-18 23:21:44 +00:00
while ( true ) {
2021-08-25 10:57:50 +00:00
check_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 ) {
2022-08-17 15:33:36 +00:00
if ( rdebug_flags & 8 )
2021-12-26 21:34:15 +00:00
println ( hlog , " result " , try_count , " : " , e . what ( ) ) ;
2021-08-18 23:21:44 +00:00
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 ;
2021-11-03 08:42:40 +00:00
vector < heptagon * > extra_origins ;
2021-07-29 12:07:30 +00:00
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 ;
}
2022-07-11 10:18:58 +00:00
2022-07-12 11:27:35 +00:00
cell * gen_extra_origin ( int fv ) override {
2022-07-11 10:18:58 +00:00
heptagon * extra_origin = gen ( fv , 0 , true ) ;
extra_origin - > s = hsOrigin ;
extra_origins . push_back ( extra_origin ) ;
return extra_origin - > c7 ;
}
2021-07-29 12:07:30 +00:00
~ hrmap_rulegen ( ) {
2021-11-03 08:42:40 +00:00
clearfrom ( origin ) ;
for ( auto eo : extra_origins ) clearfrom ( eo ) ;
2021-07-29 12:07:30 +00:00
}
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 )
2022-10-07 23:53:02 +00:00
return arb : : get_adj ( arb : : current_or_slided ( ) , h - > zebraval , dir ) ;
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
}
2022-10-07 23:53:02 +00:00
return arb : : get_adj ( arb : : current_or_slided ( ) , treestates [ s ] . sid , dir0 , sid1 , dir1 , false ) ;
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
2023-08-21 17:23:48 +00:00
bool link_alt ( heptagon * h , heptagon * alt , hstate firststate , int dir ) override {
2021-07-29 12:07:30 +00:00
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 ;
2022-07-11 10:18:58 +00:00
int cl = cycle_size ( psid ) ;
2021-07-29 12:07:30 +00:00
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 ;
}
} ;
2021-11-01 16:24:54 +00:00
EX vector < treestate > alt_treestates ;
EX void swap_treestates ( ) {
swap ( treestates , alt_treestates ) ;
}
2022-07-11 10:18:58 +00:00
EX void add_other_origins ( int qty ) {
for ( int i = 1 ; i < qty ; i + + ) {
cell * s = currentmap - > gen_extra_origin ( i ) ;
tcell * c = gen_tcell ( get_id ( s ) ) ;
2021-11-01 16:24:54 +00:00
cell_to_tcell [ s ] = c ;
tcell_to_cell [ c ] = s ;
c - > dist = 0 ;
2021-12-24 22:08:14 +00:00
t_origin . push_back ( twalker ( c , 0 ) ) ;
2021-11-01 16:24:54 +00:00
}
println ( hlog , " t_origin size = " , isize ( t_origin ) ) ;
}
2021-07-29 12:07:30 +00:00
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-11-01 16:17:16 +00:00
EX 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 " ) ;
2022-02-01 15:56:44 +00:00
param_i ( max_shortcut_length , " max_shortcut_length " ) ;
2021-08-21 22:12:22 +00:00
param_i ( rulegen_timeout , " rulegen_timeout " ) ;
2022-02-01 15:57:13 +00:00
param_i ( first_restart_on , " first_restart_on " ) ;
2023-04-11 15:10:04 +00:00
# if MAXMDIM >= 4
2022-08-06 23:52:51 +00:00
param_i ( max_ignore_level_pre , " max_ignore_level_pre " ) ;
param_i ( max_ignore_level_post , " max_ignore_level_post " ) ;
param_i ( max_ignore_time_pre , " max_ignore_time_pre " ) ;
param_i ( max_ignore_time_post , " max_ignore_time_post " ) ;
2023-04-11 15:10:04 +00:00
# endif
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 ;
2022-09-14 15:26:22 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " before: " , ts . rules ) ;
2021-08-01 01:00:24 +00:00
std : : rotate ( ts . rules . begin ( ) , ts . rules . begin ( ) + sumparent , ts . rules . end ( ) ) ;
2022-09-14 15:26:22 +00:00
if ( debugflags & DF_GEOM ) println ( hlog , " after : " , ts . rules ) ;
2021-08-01 01:00:24 +00:00
}
2021-07-30 21:50:30 +00:00
ep . force_eat ( " ) " ) ;
}
2022-04-24 19:40:29 +00:00
EX void verify_parsed_treestates ( arb : : arbi_tiling & c ) {
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 " ) ;
}
2022-04-24 19:40:29 +00:00
for ( auto & sh : c . shapes ) sh . cycle_length = sh . size ( ) ;
2021-07-30 21:50:30 +00:00
find_possible_parents ( ) ;
}
2021-07-31 13:50:20 +00:00
EX void show ( ) {
cmode = sm : : SIDE | sm : : MAYDARK ;
2022-07-05 09:51:06 +00:00
gamescreen ( ) ;
2021-07-31 13:50:20 +00:00
dialog : : init ( XLAT ( " strict tree maps " ) ) ;
dialog : : addHelp ( XLAT (
2023-12-27 09:40:04 +00:00
" Strict tree maps are generated using a more powerful algorithm. \n \n This algorithm supports horocycles and knows the expansion rates of various "
2021-07-31 14:10:37 +00:00
" 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
2024-11-14 17:21:49 +00:00
if ( closed_manifold ) {
dialog : : addInfo ( " not available (and not necessary) in closed manifolds " ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
return ;
}
else if ( aperiodic ) {
2021-07-31 13:50:20 +00:00
dialog : : addInfo ( " not available in aperiodic tessellations " ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
2024-11-14 17:21:49 +00:00
return ;
}
else if ( bt : : in ( ) ) {
dialog : : addInfo ( " not available in binary-like tilings " ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
return ;
2021-07-31 13:50:20 +00:00
}
else if ( WDIM = = 3 ) {
2024-11-14 17:21:49 +00:00
if ( reg3 : : in ( ) & & reg3 : : variation_rule_available ( ) )
dialog : : addInfo ( " precomputed rule used (for specific variation) " ) ;
else if ( reg3 : : in ( ) & & reg3 : : pure_rule_available ( ) )
dialog : : addInfo ( " precomputed rule used (for regular honeycomb) " ) ;
else if ( euc : : in ( ) )
dialog : : addInfo ( " no rule needed for Euclidean " ) ;
else if ( reg3 : : in ( ) ) {
dialog : : addInfo ( " not available in this regular honeycomb " ) ;
dialog : : addInfo ( " fallback implementation used " ) ;
}
else
dialog : : addInfo ( " not available in this 3D tessellation " ) ;
2021-07-31 13:50:20 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
2024-11-14 17:21:49 +00:00
return ;
2021-07-31 13:50:20 +00:00
}
dialog : : addBoolItem ( XLAT ( " in tes internal format " ) , arb : : in ( ) , ' t ' ) ;
dialog : : add_action ( [ ] {
if ( ! arb : : in ( ) ) {
2024-05-28 11:05:07 +00:00
try {
arb : : convert : : convert ( ) ;
arb : : convert : : activate ( ) ;
start_game ( ) ;
rule_status = XLAT ( " converted successfully -- %1 cell types " , its ( isize ( arb : : current . shapes ) ) ) ;
rules_known_for = " unknown " ;
}
catch ( hr_parse_exception & ex ) {
println ( hlog , " failed: " , ex . s ) ;
rule_status = XLAT ( " failed to convert: " ) + ex . s ;
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 " ) ) ;
}
} ) ;
2022-09-14 20:53:39 +00:00
dialog : : addBoolItem ( XLAT ( " extended football colorability " ) , arb : : extended_football , ' f ' ) ;
dialog : : add_action ( [ ] {
arb : : extended_football = ! arb : : extended_football ;
rules_known_for = " unknown " ;
rule_status = " manually disabled " ;
if ( arb : : convert : : in ( ) ) {
stop_game ( ) ;
arb : : convert : : convert ( ) ;
arb : : convert : : activate ( ) ;
start_game ( ) ;
}
else if ( arb : : in ( ) ) {
stop_game ( ) ;
try {
arb : : load ( arb : : tes ) ;
}
catch ( hr_parse_exception & ex ) {
println ( hlog , " failed: " , ex . s ) ;
}
start_game ( ) ;
}
} ) ;
2022-06-23 08:29:49 +00:00
add_edit ( arb : : convert : : minimize_on_convert ) ;
2023-12-03 13:29:50 +00:00
dialog : : addBoolItem ( XLAT ( " strict tree maps " ) , currentmap - > strict_tree_rules ( ) , ' s ' ) ;
2021-07-31 13:50:20 +00:00
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 ( ) ;
}
2022-07-13 16:59:04 +00:00
# if CAP_COMMANDLINE
int readRuleArgs ( ) {
using namespace arg ;
if ( 0 ) ;
else if ( argis ( " -ruleflag " ) ) {
shift ( ) ;
rulegen : : flags ^ = Flag ( argi ( ) ) ;
}
2023-03-16 13:47:04 +00:00
else if ( argis ( " -origin-id " ) ) {
shift ( ) ; origin_id = argi ( ) ;
}
2022-08-17 15:33:36 +00:00
else if ( argis ( " -ruledflags " ) ) {
shift ( ) ;
rulegen : : rdebug_flags = argi ( ) ;
}
2022-07-13 16:59:04 +00:00
else return 1 ;
return 0 ;
}
auto hook = addHook ( hooks_args , 100 , readRuleArgs ) ;
# endif
2021-07-29 12:07:30 +00:00
EX }
}