1+
2+
3+
4+ export function isValidLifeString ( lifeString : string , errorOutput ?: ( error : string ) => any ) {
5+ const error = getLifeStringError ( lifeString ) ;
6+ if ( error . length > 0 ) {
7+ errorOutput ?.( error )
8+ return false ;
9+ }
10+ return true ;
11+ }
12+
13+ export function getLifeStringError ( lifeString : string ) : string {
14+ const sides = lifeString . split ( "/" ) ;
15+ if ( sides . length !== 2 ) {
16+ return "Error: Not able to split string into birth and survival counts, format must include a forward slash B<NUMS>/S<NUMS> "
17+ } else if ( sides [ 0 ] . charAt ( 0 ) !== "B" || sides [ 1 ] . charAt ( 0 ) !== "S" ) {
18+ return "Error: B and S are backwards, please switch to B<NUMS>/S<NUMS> "
19+ } else if ( sides [ 0 ] . substring ( 1 ) . split ( '' ) . some ( ( char : string ) => isNaN ( Number . parseInt ( char ) ) ) || sides [ 1 ] . substring ( 1 ) . split ( '' ) . some ( ( char : string ) => isNaN ( Number . parseInt ( char ) ) ) ) {
20+ return "Error: Must include numbers after B and after /S B<NUMS>/S<NUMS> "
21+ } else if ( new Set < string > ( sides [ 0 ] . substring ( 1 ) . split ( '' ) ) . size !== sides [ 0 ] . length - 1 || new Set < string > ( sides [ 1 ] . substring ( 1 ) . split ( '' ) ) . size !== sides [ 1 ] . length - 1 ) {
22+ return "Error: Replicate number on one side of B<NUMS>/S<NUMS> "
23+ }
24+
25+ return "" ;
26+ }
27+
28+ function getCanMakeLifeStringError ( survivalNums : number [ ] , birthNums : number [ ] ) : string {
29+ if ( survivalNums . some ( num => num < 0 || num > 8 ) ) {
30+ return "Survival neighborhood rules must be between 0 and 8" ;
31+ }
32+ if ( birthNums . some ( num => num < 0 || num > 8 ) ) {
33+ return "Birth neighborhood rules must be between 0 and 8" ;
34+ }
35+ if ( survivalNums . length > 8 ) {
36+ return "Can only have 8 maximum survival rules" ;
37+ }
38+ if ( birthNums . length > 8 ) {
39+ return "Can only have 8 maximum birth rules" ;
40+ }
41+ if ( survivalNums . length !== new Set < number > ( survivalNums ) . size ) {
42+ return "Not all survival rules are unique" ;
43+ }
44+ if ( birthNums . length !== new Set < number > ( birthNums ) . size ) {
45+ return "Not all birth rules are unique" ;
46+ }
47+
48+
49+ return "" ;
50+ }
51+
52+ function canMakeLifeString ( survivalNums : number [ ] , birthNums : number [ ] ) : boolean {
53+ return getCanMakeLifeStringError ( survivalNums , birthNums ) === ""
54+ }
55+
56+ export function createLifeString ( birthNums : number [ ] , survivalNums : number [ ] ) : string {
57+ if ( ! canMakeLifeString ( survivalNums , birthNums ) ) {
58+ throw new Error ( `Cannot make new life string from ${ survivalNums } and ${ birthNums } : ${ getCanMakeLifeStringError ( survivalNums , birthNums ) } ` ) ;
59+ }
60+
61+ return "B" . concat ( birthNums . join ( "" ) ) . concat ( '/S' ) . concat ( survivalNums . join ( "" ) ) ;
62+ }
63+
64+ export function parseLifeLikeString ( lifeString : string ) : LifeRuleData {
65+ let lifeData : LifeRuleData = { birth : [ ] , survival : [ ] } ;
66+ if ( ! isValidLifeString ( lifeString ) ) {
67+ return lifeData ;
68+ }
69+
70+ const [ birth , survival ] = lifeString . split ( "/" ) ;
71+
72+ for ( let i = 1 ; i < birth . length ; i ++ ) {
73+ const num : number = Number . parseInt ( birth . charAt ( i ) ) ;
74+ lifeData . birth . push ( num ) ;
75+ }
76+
77+
78+ for ( let i = 1 ; i < survival . length ; i ++ ) {
79+ const num : number = Number . parseInt ( survival . charAt ( i ) ) ;
80+ lifeData . survival . push ( num ) ;
81+ }
82+
83+ return lifeData ;
84+ }
0 commit comments