1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3 
4 #include "liferules.h"
5 #include "util.h"
6 #include <cstdlib>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <ctype.h>
10 
11 #if defined(WIN32) || defined(WIN64)
12 #define strncasecmp _strnicmp
13 #endif
14 
liferules()15 liferules::liferules() {
16    int i ;
17 
18    // base64 encoding characters
19    base64_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
20 
21    // all valid rule letters
22    valid_rule_letters = "012345678ceaiknjqrytwz-" ;
23 
24    // rule letters per neighbor count
25    rule_letters[0] = "ce" ;
26    rule_letters[1] = "ceaikn" ;
27    rule_letters[2] = "ceaiknjqry" ;
28    rule_letters[3] = "ceaiknjqrytwz" ;
29 
30    // isotropic neighborhoods per neighbor count
31    static int entry0[2] = { 1, 2 } ;
32    static int entry1[6] = { 5, 10, 3, 40, 33, 68 } ;
33    static int entry2[10] = { 69, 42, 11, 7, 98, 13, 14, 70, 41, 97 } ;
34    static int entry3[13] = { 325, 170, 15, 45, 99, 71, 106, 102, 43, 101, 105, 78, 108 } ;
35    rule_neighborhoods[0] = entry0 ;
36    rule_neighborhoods[1] = entry1 ;
37    rule_neighborhoods[2] = entry2 ;
38    rule_neighborhoods[3] = entry3 ;
39 
40    // bit offset for suvival part of rule
41    survival_offset = 9 ;
42 
43    // bit in letter bit mask indicating negative
44    negative_bit = 13 ;
45 
46    // maximum number of letters per neighbor count
47    max_letters[0] = 0 ;
48    max_letters[1] = (int) strlen(rule_letters[0]) ;
49    max_letters[2] = (int) strlen(rule_letters[1]) ;
50    max_letters[3] = (int) strlen(rule_letters[2]) ;
51    max_letters[4] = (int) strlen(rule_letters[3]) ;
52    max_letters[5] = max_letters[3] ;
53    max_letters[6] = max_letters[2] ;
54    max_letters[7] = max_letters[1] ;
55    max_letters[8] = max_letters[0] ;
56    for (i = 0 ; i < survival_offset ; i++) {
57       max_letters[i + survival_offset] = max_letters[i] ;
58    }
59 
60    // canonical letter order per neighbor count
61    static int order0[1] = { 0 } ;
62    static int order1[2] = { 0, 1 } ;
63    static int order2[6] = { 2, 0, 1, 3, 4, 5 } ;
64    static int order3[10] = { 2, 0, 1, 3, 6, 4, 5, 7, 8, 9 } ;
65    static int order4[13] = { 2, 0, 1, 3, 6, 4, 5, 7, 8, 10, 11, 9, 12 } ;
66    order_letters[0] = order0 ;
67    order_letters[1] = order1 ;
68    order_letters[2] = order2 ;
69    order_letters[3] = order3 ;
70    order_letters[4] = order4 ;
71    order_letters[5] = order3 ;
72    order_letters[6] = order2 ;
73    order_letters[7] = order1 ;
74    order_letters[8] = order0 ;
75    for (i = 0 ; i < survival_offset ; i++) {
76       order_letters[i + survival_offset] = order_letters[i] ;
77    }
78 
79    // initialize
80    initRule() ;
81 }
82 
~liferules()83 liferules::~liferules() {
84 }
85 
86 // returns a count of the number of bits set in given int
bitcount(int v)87 static int bitcount(int v) {
88    int r = 0 ;
89    while (v) {
90       r++ ;
91       v &= v - 1 ;
92    }
93    return r ;
94 }
95 
96 // initialize
initRule()97 void liferules::initRule() {
98    // default to Moore neighbourhood totalistic rule
99    neighbormask = MOORE ;
100    neighbors = 8 ;
101    wolfram = -1 ;
102    totalistic = true ;
103    using_map = false ;
104 
105    // one bit for each neighbor count
106    // s = survival, b = birth
107    // bit:     17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
108    // meaning: s8 s7 s6 s5 s4 s3 s2 s1 s0 b8 b7 b6 b5 b4 b3 b2 b1 b0
109    rulebits = 0 ;
110 
111    // one bit for each letter per neighbor count
112    // N = negative bit
113    // bit:     13 12 11 10  9  8  7  6  5  4  3  2  1  0
114    // meaning:  N  z  w  t  y  r  q  j  n  k  i  a  e  c
115    memset(letter_bits, 0, sizeof(letter_bits)) ;
116 
117    // two 4x4 rule maps (second used for B0-not-Smax rule emulation)
118    memset(rule0, 0, sizeof(rule0)) ;
119    memset(rule1, 0, sizeof(rule1)) ;
120 
121    // 3x3 rule map
122    memset(rule3x3, 0, sizeof(rule3x3)) ;
123 
124    // canonical rule string
125    memset(canonrule, 0, sizeof(canonrule)) ;
126 }
127 
128 // set 3x3 grid based on totalistic value
setTotalistic(int value,bool survival)129 void liferules::setTotalistic(int value, bool survival) {
130    int mask = 0 ;
131    int nbrs = 0 ;
132    int nhood = 0 ;
133    int i = 0 ;
134    int j = 0 ;
135    int offset = 0 ;
136 
137    // check if this value has already been processed
138    if (survival) {
139       offset = survival_offset ;
140    }
141    if ((rulebits & (1 << (value + offset))) == 0) {
142        // update the rulebits
143        rulebits |= 1 << (value + offset) ;
144 
145        // update the mask if survival
146        if (survival) {
147           mask = 0x10 ;
148        }
149 
150        // fill the array based on totalistic value
151        for (i = 0 ; i < ALL3X3 ; i += 32) {
152           for (j = 0 ; j < 16 ; j++) {
153              nbrs = 0 ;
154              nhood = (i+j) & neighbormask ;
155              while (nhood > 0) {
156                 nbrs += (nhood & 1) ;
157                 nhood >>= 1 ;
158              }
159              if (value == nbrs) {
160                 rule3x3[i+j+mask] = 1 ;
161              }
162           }
163       }
164    }
165 }
166 
167 // flip bits
flipBits(int x)168 int liferules::flipBits(int x) {
169    return ((x & 0x07) << 6) | ((x & 0x1c0) >> 6) | (x & 0x38) ;
170 }
171 
172 // rotate 90
rotateBits90Clockwise(int x)173 int liferules::rotateBits90Clockwise(int x) {
174    return ((x & 0x4) << 6) | ((x & 0x20) << 2) | ((x & 0x100) >> 2)
175                         | ((x & 0x2) << 4) | (x & 0x10) | ((x & 0x80) >> 4)
176                         | ((x & 0x1) << 2) | ((x & 0x8) >> 2) | ((x & 0x40) >> 6) ;
177 }
178 
179 // set symmetrical neighborhood into 3x3 map
setSymmetrical512(int x,int b)180 void liferules::setSymmetrical512(int x, int b) {
181    int y = x ;
182    int i = 0 ;
183 
184    // process each of the 4 rotations
185    for (i = 0 ; i < 4 ; i++) {
186       rule3x3[y] = (char) b ;
187       y = rotateBits90Clockwise(y) ;
188    }
189 
190    // flip
191    y = flipBits(y) ;
192 
193    // process each of the 4 rotations
194    for (i = 0 ; i < 4 ; i++) {
195       rule3x3[y] = (char) b ;
196       y = rotateBits90Clockwise(y) ;
197    }
198 }
199 
200 // set symmetrical neighborhood
setSymmetrical(int value,bool survival,int lindex,int normal)201 void liferules::setSymmetrical(int value, bool survival, int lindex, int normal) {
202    int xorbit = 0 ;
203    int nindex = value - 1 ;
204    int x = 0 ;
205    int offset = 0 ;
206 
207    // check for homogeneous bits
208    if (value == 0 || value == 8) {
209       setTotalistic(value, survival) ;
210    }
211    else {
212       // update the rulebits
213       if (survival) {
214          offset = survival_offset ;
215       }
216       rulebits |= 1 << (value + offset) ;
217 
218       // reflect the index if in second half
219       if (nindex > 3) {
220          nindex = 6 - nindex ;
221          xorbit = 0x1ef ;
222       }
223 
224       // update the letterbits
225       letter_bits[value + offset] |= 1 << lindex ;
226 
227       if (!normal) {
228          // set the negative bit
229          letter_bits[value + offset] |= 1 << negative_bit ;
230       }
231 
232       // lookup the neighborhood
233       x = rule_neighborhoods[nindex][lindex] ^ xorbit ;
234       if (survival) {
235          x |= 0x10 ;
236       }
237       setSymmetrical512(x, normal) ;
238    }
239 }
240 
241 // set totalistic birth or survival rule from a string
setTotalisticRuleFromString(const char * rule,bool survival)242 void liferules::setTotalisticRuleFromString(const char *rule, bool survival) {
243    char current ;
244 
245    // process each character in the rule string
246    while ( *rule ) {
247       current = *rule ;
248       rule++ ;
249 
250       // convert the digit to an integer
251       current -= '0' ;
252 
253       // set totalistic
254       setTotalistic(current, survival) ;
255    }
256 }
257 
258 // set rule from birth or survival string
setRuleFromString(const char * rule,bool survival)259 void liferules::setRuleFromString(const char *rule, bool survival) {
260    // current and next character
261    char current ;
262    char next ;
263 
264    // whether character normal or inverted
265    int normal = 1 ;
266 
267    // letter index
268    char *letterindex = 0 ;
269    int lindex = 0 ;
270    int nindex = 0 ;
271 
272    // process each character
273    while ( *rule ) {
274       current = *rule ;
275       rule++ ;
276 
277       // find the index in the valid character list
278       letterindex = strchr((char*) valid_rule_letters, current) ;
279       lindex = letterindex ? int(letterindex - valid_rule_letters) : -1 ;
280 
281       // check if it is a digit
282       if (lindex >= 0 && lindex <= 8) {
283          // determine what follows the digit
284          next = *rule ;
285          nindex = -1 ;
286          if (next) {
287             letterindex = strchr((char*) rule_letters[3], next) ;
288             if (letterindex) {
289                nindex = int(letterindex - rule_letters[3]) ;
290             }
291          }
292 
293          // is the next character a digit or minus?
294          if (nindex == -1) {
295             setTotalistic(lindex, survival) ;
296          }
297 
298          // check for inversion
299          normal = 1 ;
300          if (next == '-') {
301             rule++ ;
302             next = *rule ;
303 
304             // invert following character meanings
305             normal = 0 ;
306          }
307 
308          // process non-totalistic characters
309          if (next) {
310             letterindex = strchr((char*) rule_letters[3], next) ;
311             nindex = -1 ;
312             if (letterindex) {
313                nindex = int(letterindex - rule_letters[3]) ;
314             }
315             while (nindex >= 0) {
316                // set symmetrical
317                setSymmetrical(lindex, survival, nindex, normal) ;
318 
319                // get next character
320                rule++ ;
321                next = *rule ;
322                nindex = -1 ;
323                if (next) {
324                   letterindex = strchr((char*) rule_letters[3], next) ;
325                   if (letterindex) {
326                      nindex = int(letterindex - rule_letters[3]) ;
327                   }
328                }
329             }
330          }
331       }
332    }
333 }
334 
335 // create the rule map from Wolfram number
createWolframMap()336 void liferules::createWolframMap() {
337    int i = 0 ;
338 
339    // clear the rule array
340    memset(rule3x3, 0, ALL3X3) ;
341 
342    // set in the 3x3 map
343    for (i = 0 ; i < ALL3X3 ; i++) {
344       if ((wolfram & (1 << (i & 7))) || (i & 16)) {
345          rule3x3[i] = 1 ;
346       }
347    }
348 }
349 
350 // create the rule map from the base64 encoded map
createRuleMapFromMAP(const char * base64)351 void liferules::createRuleMapFromMAP(const char *base64) {
352    // set the number of characters to read
353    int power2 = 1 << (neighbors + 1) ;
354    int fullchars = power2 / 6 ;
355    int remainbits = power2 % 6 ;
356 
357    // create an array to read the MAP bits
358    char bits[ALL3X3] ;
359 
360    // decode the base64 string
361    int i = 0 ;
362    char c = 0 ;
363    int j = 0 ;
364    const char *index = 0 ;
365 
366    for (i = 0 ; i < fullchars ; i++) {
367       // convert character to base64 index
368       index = strchr(base64_characters, *base64) ;
369       base64++ ;
370       c = index ? (char)(index - base64_characters) : 0 ;
371 
372       // decode the character
373       bits[j] = c >> 5 ;
374       j++ ;
375       bits[j] = (c >> 4) & 1 ;
376       j++ ;
377       bits[j] = (c >> 3) & 1 ;
378       j++ ;
379       bits[j] = (c >> 2) & 1 ;
380       j++ ;
381       bits[j] = (c >> 1) & 1 ;
382       j++ ;
383       bits[j] = c & 1 ;
384       j++ ;
385    }
386 
387    // decode remaining bits from final character
388    if (remainbits > 0) {
389       index = strchr(base64_characters, *base64) ;
390       c = index ? (char)(index - base64_characters) : 0 ;
391       int b = 5 ;
392 
393       while (remainbits > 0) {
394           bits[j] = (c >> b) & 1 ;
395           b-- ;
396           j++ ;
397           remainbits-- ;
398       }
399    }
400 
401    // copy into rule array using the neighborhood mask
402    int k, m ;
403    for (i = 0 ; i < ALL3X3 ; i++) {
404       k = 0 ;
405       m = neighbors ;
406       for (j = 8 ; j >= 0 ; j--) {
407          if (neighbormask & (1 << j)) {
408              if (i & (1 << j)) {
409                 k |= (1 << m) ;
410              }
411              m-- ;
412          }
413       }
414       rule3x3[i] = bits[k] ;
415    }
416 }
417 
418 // create the rule map from birth and survival strings
createRuleMap(const char * birth,const char * survival)419 void liferules::createRuleMap(const char *birth, const char *survival) {
420    // clear the rule array
421    memset(rule3x3, 0, ALL3X3) ;
422 
423    // check for totalistic rule
424    if (totalistic) {
425       // set the totalistic birth rule
426       setTotalisticRuleFromString(birth, false) ;
427 
428       // set the totalistic surivival rule
429       setTotalisticRuleFromString(survival, true) ;
430    }
431    else {
432       // set the non-totalistic birth rule
433       setRuleFromString(birth, false) ;
434 
435       // set the non-totalistic survival rule
436       setRuleFromString(survival, true) ;
437    }
438 }
439 
440 // add canonical letter representation
addLetters(int count,int p)441 int liferules::addLetters(int count, int p) {
442    int bits ;            // bitmask of letters defined at this count
443    int negative = 0 ;    // whether negative
444    int setbits ;         // how many bits are defined
445    int maxbits ;         // maximum number of letters at this count
446    int letter = 0 ;
447    int j ;
448 
449    // check if letters are defined for this neighbor count
450    if (letter_bits[count]) {
451       // get the bit mask
452       bits = letter_bits[count] ;
453 
454       // check for negative
455       if (bits & (1 << negative_bit)) {
456          // letters are negative
457          negative = 1 ;
458          bits &= ~(1 << negative_bit) ;
459       }
460 
461       // compute the number of bits set
462       setbits = bitcount(bits) ;
463 
464       // get the maximum number of allowed letters at this neighbor count
465       maxbits = max_letters[count] ;
466 
467       // do not invert if not negative and seven letters
468       if (!(!negative && setbits == 7 && maxbits == 13)) {
469          // if maximum letters minus number used is greater than number used then invert
470          if (setbits + negative > (maxbits >> 1)) {
471             // invert maximum letters for this count
472             bits = ~bits & ((1 << maxbits) - 1) ;
473             if (bits) {
474                negative = !negative ;
475             }
476          }
477       }
478 
479       // if negative and no letters then remove neighborhood count
480       if (negative && !bits) {
481          canonrule[p] = 0 ;
482          p-- ;
483       }
484       else {
485          // check whether to output minus
486          if (negative) {
487             canonrule[p++] = '-' ;
488          }
489 
490          // add defined letters
491          for (j = 0 ; j < maxbits ; j++) {
492             // lookup the letter in order
493             letter = order_letters[count][j] ;
494             if (bits & (1 << letter)) {
495                canonrule[p++] = rule_letters[3][letter] ;
496             }
497          }
498       }
499    }
500 
501    return p ;
502 }
503 
504 // AKT: store valid rule in canonical format for getrule()
createCanonicalName(lifealgo * algo,const char * base64)505 void liferules::createCanonicalName(lifealgo *algo, const char *base64) {
506    int p = 0 ;
507    int np = 0 ;
508    int i = 0 ;
509 
510    // the canonical version of a rule containing letters
511    // might be simply totalistic
512    bool stillnontotalistic = false ;
513 
514    // check for wolfram rule
515    if (wolfram >= 0) {
516       sprintf(canonrule, "W%d", wolfram) ;
517       while (canonrule[p]) p++ ;
518    }
519    else {
520       // check for map rule
521       if (using_map) {
522          // output map header
523          canonrule[p++] = 'M' ;
524          canonrule[p++] = 'A' ;
525          canonrule[p++] = 'P' ;
526 
527          // compute number of base64 characters
528          int power2 = 1 << (neighbors + 1) ;
529          int fullchars = power2 / 6 ;
530          int remainbits = power2 % 6 ;
531 
532          // copy base64 part
533          for (i = 0 ; i < fullchars ; i++) {
534             if (*base64) {
535                canonrule[p++] = *base64 ;
536                base64++ ;
537             }
538          }
539 
540          // copy final bits of last character
541          if (*base64) {
542             const char *index = strchr(base64_characters, *base64) ;
543             int c = index ? (char)(index - base64_characters) : 0 ;
544             int k = 0 ;
545             int m = 5 ;
546             for (i = 0 ; i < remainbits ; i++) {
547                k |= c & (1 << m) ;
548                m-- ;
549             }
550             canonrule[p++] = base64_characters[c] ;
551          }
552       }
553       else {
554          // output birth part
555          canonrule[p++] = 'B' ;
556          for (i = 0 ; i <= neighbors ; i++) {
557             if (rulebits & (1 << i)) {
558                canonrule[p++] = '0' + (char)i ;
559 
560                // check for non-totalistic
561                if (!totalistic) {
562                   // add any defined letters
563                   np = addLetters(i, p) ;
564 
565                   // check if letters were added
566                   if (np != p) {
567                      if (np > p) {
568                         stillnontotalistic = true ;
569                      }
570                      p = np ;   // confident?
571                   }
572                }
573             }
574          }
575 
576          // add slash
577          canonrule[p++] = '/' ;
578 
579          // output survival part
580          canonrule[p++] = 'S' ;
581          for (i = 0 ; i <= neighbors ; i++) {
582             if (rulebits & (1 << (survival_offset+i))) {
583                canonrule[p++] = '0' + (char)i ;
584 
585                // check for non-totalistic
586                if (!totalistic) {
587                   // add any defined letters
588                   np = addLetters(survival_offset + i, p) ;
589 
590                   // check if letters were added
591                   if (np != p) {
592                      if (np > p) {
593                         stillnontotalistic = true ;
594                      }
595                      p = np ;
596                   }
597                }
598             }
599          }
600 
601          // check if non-totalistic became totalistic
602          if (!totalistic && !stillnontotalistic) {
603             totalistic = true ;
604          }
605 
606          // add neighborhood
607          if (neighbormask == HEXAGONAL) canonrule[p++] = 'H' ;
608          if (neighbormask == VON_NEUMANN) canonrule[p++] = 'V' ;
609       }
610    }
611 
612    // check for bounded grid
613    if (algo->gridwd > 0 || algo->gridht > 0) {
614       // algo->setgridsize() was successfully called above, so append suffix
615       const char* bounds = algo->canonicalsuffix() ;
616       i = 0 ;
617       while (bounds[i]) canonrule[p++] = bounds[i++] ;
618    }
619 
620    // null terminate
621    canonrule[p] = 0 ;
622 }
623 
624 // convert the 3x3 map to the 4x4 map
convertTo4x4Map(char * which)625 void liferules::convertTo4x4Map(char *which) {
626    int i = 0 ;
627    int v = 0 ;
628 
629    // create every possible cell combination for 4x4
630    for (i = 0 ; i < ALL4X4 ; i ++) {
631       // perform 4 lookups in the 3x3 map to create the 4x4 entry
632       // 15 14 13  x       7  6  5
633       // 11 10  9  x  ->  11 10  9  ->  10' x 0 0 x x
634       //  7  6  5  x      15 14 13
635       //  x  x  x  x
636       v = rule3x3[((i & 57344) >> 13) | ((i & 3584) >> 6) | ((i & 224) << 1)] << 5 ;
637 
638       //  x 14 13 12       6  5  4
639       //  x 10  9  8  ->  10  9  8  ->  x 9' 0 0 x x
640       //  x  6  5  4      14 13 12
641       //  x  x  x  x
642       v |= rule3x3[((i & 28672) >> 12) | ((i & 1792) >> 5) | ((i & 112) << 2)] << 4 ;
643 
644       //  x  x  x  x
645       // 11 10  9  x  ->   3  2  1  ->  x x 0 0 6' x
646       //  7  6  5  x       7  6  5
647       //  3  2  1  x      11 10  9
648       v |= rule3x3[((i & 3584) >> 9) | ((i & 224) >> 2) | ((i & 14) << 5)] << 1 ;
649 
650       //  x  x  x  x
651       //  x 10  9  8  ->   2  1  0  ->  x x 0 0 x 5'
652       //  x  6  5  4       6  5  4
653       //  x  2  1  0      10  9  8
654       v |= rule3x3[((i & 1792) >> 8) | ((i & 112) >> 1) | ((i & 7) << 6)] ;
655 
656       // save the entry
657       which[i] = (char) v ;
658    }
659 }
660 
661 // save the rule (and handle B0)
saveRule()662 void liferules::saveRule() {
663    int i = 0 ;
664    char tmp ;
665 
666    if (wolfram == -1) {
667       // check for B0
668       if (rule3x3[0]) {
669          // check for Smax
670          if (rule3x3[ALL3X3 - 1]) {
671             // B0 with Smax: rule -> NOT(reverse(bits))
672             for (i = 0 ; i < ALL3X3 / 2 ; i++) {
673                tmp = rule3x3[i] ;
674                rule3x3[i] = 1 - rule3x3[ALL3X3 - i - 1] ;
675                rule3x3[ALL3X3 - i - 1] = 1 - tmp ;
676             }
677          }
678          else {
679             // B0 without Smax needs two rules: one for odd and one for even generations
680             alternate_rules = true ;
681 
682             // odd rule -> reverse(bits)
683             for (i = 0 ; i < ALL3X3 / 2 ; i++) {
684                tmp = rule3x3[i] ;
685                rule3x3[i] = rule3x3[ALL3X3 - i - 1] ;
686                rule3x3[ALL3X3 - i - 1] = tmp ;
687             }
688             convertTo4x4Map(rule1) ;
689 
690             // even rule -> NOT(bits)
691             for (i = 0 ; i < ALL3X3 / 2 ; i++) {
692                tmp = rule3x3[i] ;
693                // need to reverse then invert due to even rule above
694                rule3x3[i] = 1 - rule3x3[ALL3X3 - i - 1] ;
695                rule3x3[ALL3X3 - i - 1] = 1 - tmp ;
696             }
697          }
698       }
699    }
700 
701    // convert to 4x4 map
702    convertTo4x4Map(rule0) ;
703 }
704 
705 // remove character from a string in place
removeChar(char * string,char skip)706 void liferules::removeChar(char *string, char skip) {
707    int src = 0 ;
708    int dst = 0 ;
709    char c = string[src++] ;
710 
711    // copy characters other than skip
712    while ( c ) {
713       if (c != skip) {
714          string[dst++] = c ;
715       }
716       c = string[src++] ;
717    }
718 
719    // ensure null terminated
720    string[dst] = 0 ;
721 }
722 
723 // check whether non-totalistic letters are valid for defined neighbor counts
lettersValid(const char * part)724 bool liferules::lettersValid(const char *part) {
725    char c ;
726    int nindex = 0 ;
727    int currentCount = -1 ;
728 
729    // get next character
730    while ( *part ) {
731       c = *part ;
732       if (c >= '0' && c <= '8') {
733          currentCount = c - '0' ;
734          nindex = currentCount - 1 ;
735          if (nindex > 3) {
736             nindex = 6 - nindex ;
737          }
738       }
739       else {
740          // ignore minus
741          if (c != '-') {
742             // not valid if 0 or 8
743             if (currentCount == 0 || currentCount == 8) {
744                return false ;
745             }
746 
747             // check against valid rule letters for this neighbor count
748             if (strchr((char*) rule_letters[nindex], c) == 0) {
749                return false ;
750             }
751          }
752       }
753       part++ ;
754    }
755 
756    return true ;
757 }
758 
759 // set rule
setrule(const char * rulestring,lifealgo * algo)760 const char *liferules::setrule(const char *rulestring, lifealgo *algo) {
761    char *r = (char *)rulestring ;
762    char tidystring[MAXRULESIZE] ;  // tidy version of rule string
763    char *t = (char *)tidystring ;
764    char *end = r + strlen(r) ;     // end of rule string
765    char c ;
766    char *charpos = 0 ;
767    int digit ;
768    int maxdigit = 0 ;              // maximum digit value found
769    char *colonpos = 0 ;            // position of colon
770    char *slashpos = 0 ;            // position of slash
771    char *underscorepos = 0 ;       // position of underscore
772    char *bpos = 0 ;                // position of b
773    char *spos = 0 ;                // position of s
774 
775    // initialize rule type
776    initRule() ;
777 
778    // we might need to emulate B0 rule by using two different rules for odd/even gens
779    alternate_rules = false ;
780 
781    // check if rule is too long
782    if (strlen(rulestring) > (size_t) MAXRULESIZE) {
783       return "Rule name is too long." ;
784    }
785 
786    // check for colon
787    colonpos = strchr(r, ':') ;
788    if (colonpos) {
789       // only process up to the colon
790       end = colonpos ;
791    }
792 
793    // skip any whitespace
794    while (*r == ' ') {
795       r++ ;
796    }
797 
798    // check for map
799    if (strncasecmp(r, "map", 3) == 0) {
800       // attempt to decode map
801       r += 3 ;
802       bpos = r ;
803 
804       // terminate at the colon if one is present
805       if (colonpos) *colonpos = 0 ;
806 
807       // check the length of the map
808       int maplen = (int) strlen(r) ;
809 
810       // replace the colon if one was present
811       if (colonpos) *colonpos = ':' ;
812 
813       // check if there is base64 padding
814       if (maplen > 2 && !strncmp(r + maplen - 2, "==", 2)) {
815          // remove padding
816          maplen -= 2 ;
817       }
818 
819       // check if the map length is valid for Moore, Hexagonal or von Neumann neighborhoods
820       if (!(maplen == MAP512LENGTH || maplen == MAP128LENGTH || maplen == MAP32LENGTH)) {
821           return "MAP rule needs 6, 22 or 86 base64 characters." ;
822       }
823 
824       // validate the characters
825       spos = r + maplen ;
826       while (r < spos) {
827          if (!strchr(base64_characters, *r)) {
828              return "MAP contains illegal base64 character." ;
829          }
830          r++ ;
831       }
832 
833       // set the neighborhood based on the map length
834       if (maplen == MAP128LENGTH) {
835          neighbormask = HEXAGONAL ;
836          neighbors = 6 ;
837       } else {
838          if (maplen == MAP32LENGTH) {
839             neighbormask = VON_NEUMANN ;
840             neighbors = 4 ;
841          }
842       }
843 
844       // map looks valid
845       using_map = true ;
846    }
847    else {
848       // create lower case version of rule name without spaces
849       while (r < end) {
850          // get the next character and convert to lowercase
851          c = (char) tolower(*r) ;
852 
853          // process the character
854          switch (c) {
855          // birth
856          case 'b':
857             if (bpos) {
858                // multiple b found
859                return "Only one B allowed." ;
860             }
861             bpos = t ;
862             *t = c ;
863             t++ ;
864             break ;
865 
866          // survival
867          case 's':
868             if (spos) {
869                // multiple s found
870                return "Only one S allowed." ;
871             }
872             spos = t ;
873             *t = c ;
874             t++ ;
875             break ;
876 
877          // slash
878          case '/':
879             if (slashpos) {
880                // multiple slashes found
881                return "Only one slash allowed." ;
882             }
883             slashpos = t ;
884             *t = c ;
885             t++ ;
886             break ;
887 
888          // underscore
889          case '_':
890             if (underscorepos) {
891                // multiple underscores found
892                return "Only one underscore allowed." ;
893             }
894             underscorepos = t ;
895             *t = c ;
896             t++ ;
897             break ;
898 
899          // hex
900          case 'h':
901             if (neighbormask != MOORE || wolfram != -1) {
902                // multiple neighborhoods specified
903                return "Only one neighborhood allowed." ;
904             }
905             neighbormask = HEXAGONAL ;
906             neighbors = 6 ;
907             *t = c ;
908             t++ ;
909             break ;
910 
911          // von neumann
912          case 'v':
913             if (neighbormask != MOORE || wolfram != -1) {
914                // multiple neighborhoods specified
915                return "Only one neighborhood allowed." ;
916             }
917             neighbormask = VON_NEUMANN ;
918             neighbors = 4 ;
919             *t = c ;
920             t++ ;
921             break ;
922 
923          // wolfram
924          case 'w':
925             // check if at beginning of string
926             if (t == tidystring) {
927                if (neighbormask != MOORE || wolfram != -1) {
928                   // multiple neighborhoods specified
929                   return "Only one neighborhood allowed." ;
930                }
931                wolfram = 0 ;
932             }
933             else {
934                // copy character
935                *t = c ;
936                t++ ;
937                totalistic = false ;
938             }
939             break ;
940 
941          // minus
942          case '-':
943             // check if previous character is a digit
944             if (t == tidystring || *(t-1) < '0' || *(t-1) > '8') {
945                // minus can only follow a digit
946                return "Minus can only follow a digit." ;
947             }
948             *t = c ;
949             t++ ;
950             totalistic = false ;
951             break ;
952 
953          // other characters
954          default:
955             // ignore space
956             if (c != ' ') {
957                // check character is valid
958                charpos = strchr((char*) valid_rule_letters, c) ;
959                if (charpos) {
960                   // copy character
961                   *t = c ;
962                   t++ ;
963 
964                   // check if totalistic (i.e. found a valid non-digit)
965                   digit = int(charpos - valid_rule_letters) ;
966                   if (digit > 8) {
967                      totalistic = false ;
968                   }
969                   else {
970                      // update maximum digit found
971                      if (digit > maxdigit) {
972                         maxdigit = digit ;
973                      }
974                   }
975                }
976                else if (wolfram == 0 && c == '9') {
977                   *t = c ;
978                   t++ ;
979                }
980                else {
981                   return "Bad character found.";
982                }
983             }
984             break ;
985          }
986 
987          // next character
988          r++ ;
989       }
990 
991       // ensure null terminated
992       *t = 0 ;
993 
994       // don't allow empty rule string
995       t = tidystring ;
996       if (*t == 0) {
997          return "Rule cannot be empty string." ;
998       }
999 
1000       // can't have slash and underscore
1001       if (underscorepos && slashpos) {
1002          return "Can't have slash and underscore." ;
1003       }
1004 
1005       // underscore only valid for non-totalistic rules
1006       if (underscorepos && totalistic) {
1007          return "Underscore not valid for totalistic rules, use slash." ;
1008       }
1009 
1010       // if underscore defined then set the slash position
1011       if (underscorepos) {
1012          slashpos = underscorepos ;
1013       }
1014 
1015       // check for Wolfram
1016       if (wolfram == 0) {
1017          // parse Wolfram 1D rule
1018          while (*t >= '0' && *t <= '9') {
1019             wolfram = 10 * wolfram + *t - '0' ;
1020             t++ ;
1021          }
1022          if (wolfram < 0 || wolfram > 254 || wolfram & 1) {
1023             return "Wolfram rule must be an even number from 0 to 254." ;
1024          }
1025          if (*t) {
1026             return "Bad character in Wolfram rule." ;
1027          }
1028       }
1029       else {
1030          // if neighborhood specified then must be last character
1031          if (neighbormask != MOORE) {
1032             size_t len = strlen(t) ;
1033             if (len) {
1034                c = t[len - 1] ;
1035                if (!((c == 'h') || (c == 'v'))) {
1036                   return "Neighborhood must be at end of rule." ;
1037                }
1038                // remove character
1039                t[len - 1] = 0 ;
1040             }
1041          }
1042 
1043          // at least one of slash, b or s must be present
1044          if (!(slashpos || bpos || spos)) {
1045             return "Rule must contain a slash or B or S." ;
1046          }
1047 
1048          // digits can not be greater than the number of neighbors for the defined neighborhood
1049          if (maxdigit > neighbors) {
1050             return "Digit greater than neighborhood allows." ;
1051          }
1052 
1053          // if slash present and both b and s then one must be each side of the slash
1054          if (slashpos && bpos && spos) {
1055             if ((bpos < slashpos && spos < slashpos) || (bpos > slashpos && spos > slashpos)) {
1056                return "B and S must be either side of slash." ;
1057             }
1058          }
1059 
1060          // check if there was a slash to divide birth from survival
1061          if (!slashpos) {
1062             // check if both b and s exist
1063             if (bpos && spos) {
1064                // determine whether b or s is first
1065                if (bpos < spos) {
1066                   // skip b and cut the string using s
1067                   bpos++ ;
1068                   *spos = 0 ;
1069                   spos++ ;
1070                }
1071                else {
1072                   // skip s and cut the string using b
1073                   spos++ ;
1074                   *bpos = 0 ;
1075                   bpos++ ;
1076                }
1077             }
1078             else {
1079                // just bpos
1080                if (bpos) {
1081                   bpos = t ;
1082                   removeChar(bpos, 'b') ;
1083                   spos = bpos + strlen(bpos) ;
1084                }
1085                else {
1086                   // just spos
1087                   spos = t ;
1088                   removeChar(spos, 's') ;
1089                   bpos = spos + strlen(spos) ;
1090                }
1091             }
1092          }
1093          else {
1094             // slash exists so set determine which part is b and which is s
1095             *slashpos = 0 ;
1096 
1097             // check if b or s are defined
1098             if (bpos || spos) {
1099                // check for birth first
1100                if ((bpos && bpos < slashpos) || (spos && spos > slashpos)) {
1101                   // birth then survival
1102                   bpos = t ;
1103                   spos = slashpos + 1 ;
1104                }
1105                else {
1106                   // survival then birth
1107                   bpos = slashpos + 1 ;
1108                   spos = t ;
1109                }
1110 
1111                // remove b or s from rule parts
1112                removeChar(bpos, 'b') ;
1113                removeChar(spos, 's') ;
1114             }
1115             else {
1116                // no b or s so survival first
1117                spos = t ;
1118                bpos = slashpos + 1 ;
1119             }
1120          }
1121 
1122          // if not totalistic and a part exists it must start with a digit
1123          if (!totalistic) {
1124             // check birth
1125             c = *bpos ;
1126             if (c && (c < '0' || c > '8')) {
1127                return "Non-totalistic birth must start with a digit." ;
1128             }
1129             // check survival
1130             c = *spos ;
1131             if (c && (c < '0' || c > '8')) {
1132                return "Non-totalistic survival must start with a digit." ;
1133             }
1134          }
1135 
1136          // if not totalistic then neighborhood must be Moore
1137          if (!totalistic && neighbormask != MOORE) {
1138             return "Non-totalistic only supported with Moore neighborhood." ;
1139          }
1140 
1141          // validate letters used against each specified neighbor count
1142          if (!lettersValid(bpos)) {
1143             return "Letter not valid for birth neighbor count." ;
1144          }
1145          if (!lettersValid(spos)) {
1146             return "Letter not valid for survival neighbor count." ;
1147          }
1148       }
1149    }
1150 
1151    // AKT: check for rule suffix like ":T200,100" to specify a bounded universe
1152    if (colonpos) {
1153       const char* err = algo->setgridsize(colonpos) ;
1154       if (err) return err ;
1155    } else {
1156       // universe is unbounded
1157       algo->gridwd = 0 ;
1158       algo->gridht = 0 ;
1159    }
1160 
1161    // create the rule map
1162    if (wolfram >= 0) {
1163       // create the 3x3 map from the wolfram code
1164       createWolframMap() ;
1165    }
1166    else {
1167       // check for map
1168       if (using_map) {
1169          // generate the 3x3 map from the supplied MAP string
1170          createRuleMapFromMAP(bpos) ;
1171       }
1172       else {
1173          // generate the 3x3 map from the birth and survival rules
1174          createRuleMap(bpos, spos) ;
1175       }
1176    }
1177 
1178    // save the canonical rule name
1179    createCanonicalName(algo, bpos) ;
1180 
1181    // save the rule
1182    saveRule() ;
1183 
1184    // exit with success
1185    return 0 ;
1186 }
1187 
getrule()1188 const char* liferules::getrule() {
1189    return canonrule ;
1190 }
1191 
1192 // B3/S23 -> (1 << 3) + (1 << (9 + 2)) + (1 << (9 + 3)) = 0x1808
isRegularLife()1193 bool liferules::isRegularLife() {
1194    return (neighbormask == MOORE && totalistic && rulebits == 0x1808 && wolfram < 0) ;
1195 }
1196