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