Life tag testing

For scripts to aid with computation or simulation in cellular automata.
Locked
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Nathaniel wrote:
rowett wrote:Nathaniel - sorry I found a bug with LifeViewer on the Wiki! Please will you upload build 207.
Done!
Brilliant, thanks!
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 208 is now available on the lazyslug server

Enhancements in this build:
  • #CXRLE support
    • added support for #CXRLE Pos - test
    • #CXRLE Pos and Gen only display in help information if defined
    • shift and hotkey "g" only toggles relative generation display if #CXRLE Gen defined
  • script errors are now displayed on thumbnails (rather than just the error header) - test
  • Klein bottles without a twist specification now default to horizontal twist
Fixes in this build:
  • Viewers with a height too small to display the navigation menu were not correctly respositioning the adjacent UI controls
  • bounded grids that wrap (Torus, Klein, Sphere and Cross-Surface) failed when grid edges were on tile boundaries
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 209 is now available on the lazyslug server

Enhancements in this build:
  • new script command [[ RANDOMSEED <string> ]] to set the pseudo-random number generator seed
    • used by [[ MAP ]] and [[ FILL ]] script commands - test
    • if not specified then a seed based on the system time is used
    • seed is displayed in help information
    • Stars uses its own pseudo random number stream
  • rule names are now canonical
    • rule names are shown as aliases if an alias exists for the rule
    • rule alias is shown in help information if defined
  • tooltips
    • added tooltip for rule name and rule alias
    • added tooltips for population statistics, elapsed time, coordinate state value, and the info bar
Fixes in this build:
  • Track mode details were sometimes incorrect in help information
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

I took a quick look at the resurrected Life32 project. He uses some unique notation for neighbourhoods. Dave, do you know if it's commonly used or used in any pattern collections?

Basically he prefixes a B/S rule with the neighbourhood letter and a colon: e.g. H:B23/S24 for Hex etc. Also he supports an octal number e.g. 757 to specify an arbitrary neighbourhood.
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:I took a quick look at the resurrected Life32 project. He uses some unique notation for neighbourhoods. Dave, do you know if it's commonly used or used in any pattern collections?

Basically he prefixes a B/S rule with the neighbourhood letter and a colon: e.g. H:B23/S24 for Hex etc. Also he supports an octal number e.g. 757 to specify an arbitrary neighbourhood.
I don't know of any pattern collections of any size that use either of those syntax conventions. However, as a way of defending my limited remaining sanity, I don't pay much attention to hex-rule pattern collections. There might well be something like that out there.

As far as I can recall, nobody has ever complained about not being able to open such files in Golly. So unless we get a new spike in Life32 users now for some reason, I think it's safe to not worry about supporting those odd Life32 syntax variants in LifeViewer.
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Just for fun...
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:I'd certainly like to see isotropic rule support find its way into Golly 2.8, if that's possible -- just because it's hard to know when the next Golly release will end up being, and it would be really nice to get Golly to match LifeViewer on that point. Let me know if there's anything I can do to help there.
The first test build of these changes (in Molly) is here. I had to rewrite most of the liferules module so it certainly needs some testing!

Note there is debugging code in liferules.cpp so I could see what has happening when a rule failed to decode. Whilst it was easy to build Molly it's a pig to debug. Specifically if the rule is invalid it doesn't exit with an error, rather it puts the reason for the failure in the canonical rule name so I can see it on the Rule= part of the Molly GUI.

Also Alan certainly needs a big credit since a some of this was based on his applet Java code.

gollybase/liferules.cpp

Code: Select all

                        /*** /

This file is part of Golly, a Game of Life Simulator.
Copyright (C) 2013 Andrew Trevorrow and Tomas Rokicki.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

 Web site:  http://sourceforge.net/projects/golly
 Authors:   rokicki@gmail.com  andrew@trevorrow.com

                        / ***/
#include "liferules.h"
#include "util.h"
#include <cstdlib>
#include <string.h>
#include <stdlib.h>

liferules::liferules() {
   valid_rule_letters = "012345678ceaiknjqrytwz-" ;
   rule_letters[0] = "ce" ;
   rule_letters[1] = "ceaikn" ;
   rule_letters[2] = "ceaiknjqry" ;
   rule_letters[3] = "ceaiknjqrytwz" ;
   static int entry0[2] = { 1, 2 } ;
   static int entry1[6] = { 5, 10, 3, 40, 33, 68 } ;
   static int entry2[10] = { 69, 42, 11, 7, 98, 13, 14, 70, 41, 97 } ;
   static int entry3[13] = { 325, 170, 15, 45, 99, 71, 106, 102, 43, 101, 105, 78, 108 } ;
   rule_neighborhoods[0] = entry0 ;
   rule_neighborhoods[1] = entry1 ;
   rule_neighborhoods[2] = entry2 ;
   rule_neighborhoods[3] = entry3 ;
   survival_offset = 9 ;

   // initialize
   initRule() ;
}

liferules::~liferules() {
}

// initialise
void liferules::initRule() {
   alternate_rules = 0 ;
   neighbormask = MOORE ;
   neighbors = 8 ;
   wolfram = -1 ;
   totalistic = true ;
   rulebits = 0 ;
   memset(letter_bits, 0, sizeof(letter_bits));
   memset(neg_letter_bits, 0, sizeof(letter_bits));
   alternate_rules = false ;
   memset(rule0, 0, sizeof(rule0)) ;
   memset(rule1, 0, sizeof(rule1)) ;
   memset(rule3x3, 0, sizeof(rule3x3)) ;
   memset(canonrule, 0, sizeof(canonrule)) ;
}

// set 3x3 grid based on totalistic value
void liferules::setTotalistic(int value, bool survival) {
   int mask = 0 ;
   int nbrs = 0 ;
   int nhood = 0 ;
   int i = 0 ;
   int j = 0 ;
   int offset = 0 ;

   // update the rulebits
   if (survival) {
      offset = survival_offset ;
   }
   rulebits |= 1 << (value + offset) ;

   // update the mask if survival
   if (survival) {
      mask = 0x10 ;
   }

   // fill the array based on totalistic value
   for (i = 0; i < ALL3X3; i += 32) {
      for (j = 0; j < 16; j++) {
         nbrs = 0 ;
         nhood = (i+j) & neighbormask ;
         while (nhood > 0) {
            nbrs += (nhood & 1) ;
            nhood >>= 1 ;
         }
         if (value == nbrs) {
            rule3x3[i+j+mask] = 1 ;
         }
      }
   }
}

// flip bits
int liferules::flipBits(int x) {
   return ((x & 0x07) << 6) | ((x & 0x1c0) >> 6) | (x & 0x38) ;
}

// rotate 90
int liferules::rotateBits90Clockwise(int x) {
   return ((x & 0x4) << 6) | ((x & 0x20) << 2) | ((x & 0x100) >> 2)
                        | ((x & 0x2) << 4) | (x & 0x10) | ((x & 0x80) >> 4)
                        | ((x & 0x1) << 2) | ((x & 0x8) >> 2) | ((x & 0x40) >> 6) ;
}

// set symmetrical neighborhood into 3x3 map
void liferules::setSymmetrical512(int x, int b) {
   int y = x ;
   int i = 0 ;

   // process each of the 4 rotations
   for (i=0; i<4; i++) {
      rule3x3[y] = b ;
      y = rotateBits90Clockwise(y) ;
   }

   // flip
   y = flipBits(y) ;

   // process each of the 4 rotations
   for (i=0; i<4; i++) {
      rule3x3[y] = b ;
      y = rotateBits90Clockwise(y) ;
   }
}

// set symmetrical neighborhood
void liferules::setSymmetrical(int value, bool survival, int lindex, int normal) {
   int xorbit = 0 ;
   int nindex = value - 1 ;
   int x = 0 ;
   int offset = 0 ;

   // check for homogeneous bits
   if (value == 0 || value == 8) {
      setTotalistic(value, survival) ;
   }
   else {
      // update the rulebits
      if (survival) {
         offset = survival_offset ;
      }
      rulebits |= 1 << (value + offset) ;

      // reflect the index if in second half
      if (nindex > 3) {
         nindex = 6 - nindex ;
         xorbit = 0x1ef ;
      }

      // update the letterbits
      if (normal) {
         letter_bits[value + offset] |= 1 << lindex ;
      }
      else {
         neg_letter_bits[value + offset] |= 1 << lindex ;
      }

      // lookup the neighborhood
      x = rule_neighborhoods[nindex][lindex] ^ xorbit ;
      if (survival) {
         x |= 0x10 ;
      }
      setSymmetrical512(x, normal) ;
   }
}

// set totalistic birth or survival rule from a string
void liferules::setTotalisticRuleFromString(const char *rule, bool survival) {
   char current ;

   // process each character in the rule string
   while ( (current = *rule) ) {
      rule++ ;

      // convert the digit to an integer
      current -= '0' ;

      // set totalistic
      setTotalistic(current, survival) ;
   }
}

// set rule from birth or survival string
void liferules::setRuleFromString(const char *rule, bool survival) {
   // current and nexr character
   char current ;
   char next ;

   // whether character normal or inverted
   int normal = 1 ;

   // letter index
   char *letterindex = 0 ;
   int lindex = 0 ;
   int nindex = 0 ;

   // process each character
   while ( (current = *rule) ) {
      rule++ ;

      // find the index in the valid character list
      letterindex = strchr(valid_rule_letters, current) ;
      lindex = letterindex - valid_rule_letters ;

      // check if it is a digit
      if ((lindex > 0 && lindex <= 8) || (lindex == 0 && survival)) {
         // determine what follows the digit
         next = *rule ;
         nindex = -1 ;
         if (next) {
            letterindex = strchr(rule_letters[3], next) ;
            if (letterindex) {
               nindex = letterindex - rule_letters[3] ;
            }
         }

         // is the next character another digit?
         if (nindex == -1) {
            // yes so set totalitic with the current digit
            setTotalistic(lindex, survival) ;
         }

         // check for inversion
         normal = 1 ;
         if (next == '-') {
            rule++ ;
            next = *rule ;

            // invert following character meanings
            normal = 0 ;
         }

         // process non-totalistic characters
         if (next) {
            letterindex = strchr(rule_letters[3], next) ;
            nindex = -1 ;
            if (letterindex) {
               nindex = letterindex - rule_letters[3] ;
            }
            while (nindex >= 0) {
               // set symmetrical
               setSymmetrical(lindex, survival, nindex, normal) ;

               // get next character
               rule++ ;
               next = *rule ;
               nindex = -1 ;
               if (next) {
                  letterindex = strchr(rule_letters[3], next) ;
                  if (letterindex) {
                     nindex = letterindex - rule_letters[3] ;
                  }
               }
            }
         }
      }
   }
}

// create the rule map from Wolfram number
void liferules::createWolframMap() {
   int i = 0 ;

   // clear the rule array
   memset(rule3x3, 0, ALL3X3) ;

   // set in the 3x3 map
   for (i = 0; i < ALL3X3; i+= 1) {
      if ((wolfram & (1 << (i & 7))) || (i & 16)) {
         rule3x3[i] = 1 ;
      }
   }
}

// create the rule map from birth and survival strings
void liferules::createRuleMap(const char *birth, const char *survival) {
   // clear the rule array
   memset(rule3x3, 0, ALL3X3) ;

   // check for totalistic rule
   if (totalistic) {
      // set the totalistic birth rule
      setTotalisticRuleFromString(birth, false) ;

      // set the totalistic surivival rule
      setTotalisticRuleFromString(survival, true) ;
   }
   else {
      // set the non-totalistic birth rule
      setRuleFromString(birth, false) ;

      // set the non-totalistic survival rule
      setRuleFromString(survival, true) ;
   }
}

// AKT: store valid rule in canonical format for getrule()
void liferules::createCanonicalName(lifealgo *algo) {
   int p = 0 ;
   int i = 0 ;
   int j = 0 ;

   // check for wolfram rule
   if (wolfram >= 0) {
      sprintf(canonrule, "W%d", wolfram) ;
      while (canonrule[p]) p++ ;
   } else {
      // output birth part
      canonrule[p++] = 'B' ;
      for (i=0; i<=neighbors; i++) {
         if (rulebits & (1 << i)) {
            canonrule[p++] = '0' + (char)i ;

            // check for non-totalistic
            if (letter_bits[i]) {
               for (j=0; j<13; j++) {
                  if (letter_bits[i] & (1 << j)) {
                     canonrule[p++] = rule_letters[3][j] ;
                  }
               }
            }

            if (neg_letter_bits[i]) {
               canonrule[p++] = '-' ;
               for (j=0; j<13; j++) {
                  if (neg_letter_bits[i] & (1 << j)) {
                     canonrule[p++] = rule_letters[3][j] ;
                  }
               }
            }
         }
      }

      // add slash
      canonrule[p++] = '/' ;

      // output survival part
      canonrule[p++] = 'S' ;
      for (i=0; i<=neighbors; i++) {
         if (rulebits & (1 << (survival_offset+i))) {
            canonrule[p++] = '0' + (char)i ;

            // check for non-totalistic
            if (letter_bits[survival_offset+i]) {
               for (j=0; j<13; j++) {
                  if (letter_bits[survival_offset+i] & (1 << j)) {
                     canonrule[p++] = rule_letters[3][j] ;
                  }
               }
            }

            if (neg_letter_bits[survival_offset+i]) {
               canonrule[p++] = '-' ;
               for (j=0; j<13; j++) {
                  if (neg_letter_bits[survival_offset+i] & (1 << j)) {
                     canonrule[p++] = rule_letters[3][j] ;
                  }
               }
            }
         }
      }

      // add neighborhood
      if (neighbormask == HEXAGONAL) canonrule[p++] = 'H' ;
      if (neighbormask == VON_NEUMANN) canonrule[p++] = 'V' ;
   }

   // check for bounded grid
   if (algo->gridwd > 0 || algo->gridht > 0) {
      // algo->setgridsize() was successfully called above, so append suffix
      const char* bounds = algo->canonicalsuffix() ;
      int i = 0 ;
      while (bounds[i]) canonrule[p++] = bounds[i++] ;
   }

   // null terminate
   canonrule[p] = 0 ;
}

// convert the 3x3 map to the 4x4 map
void liferules::convertTo4x4Map(char *which) {
   int i = 0 ;
   int v = 0 ;

   // create every possible cell combination for 4x4
   for (i = 0; i < 65536; i += 1) {
      // perform 4 lookups in the 3x3 map to create the 4x4 entry
      // 15 14 13  x       7  6  5
      // 11 10  9  x  ->  11 10  9  ->  10' x 0 0 x x
      //  7  6  5  x      15 14 13
      //  x  x  x  x
      v = rule3x3[((i & 57344) >> 13) | ((i & 3584) >> 6) | ((i & 224) << 1)] << 5 ;
     
      //  x 14 13 12       6  5  4
      //  x 10  9  8  ->  10  9  8  ->  x 9' 0 0 x x
      //  x  6  5  4      14 13 12
      //  x  x  x  x
      v |= rule3x3[((i & 28672) >> 12) | ((i & 1792) >> 5) | ((i & 112) << 2)] << 4 ;

      //  x  x  x  x
      // 11 10  9  x  ->   3  2  1  ->  x x 0 0 6' x
      //  7  6  5  x       7  6  5
      //  3  2  1  x      11 10  9
      v |= rule3x3[((i & 3584) >> 9) | ((i & 224) >> 2) | ((i & 14) << 5)] << 1 ;

      //  x  x  x  x
      //  x 10  9  8  ->   2  1  0  ->  x x 0 0 x 5' 
      //  x  6  5  4       6  5  4
      //  x  2  1  0      10  9  8
      v |= rule3x3[((i & 1792) >> 8) | ((i & 112) >> 1) | ((i & 7) << 6)] ;

      // save the entry
      which[i] = v ;
   }
}

/*
    Following routines emulate B0 rules using David Eppstein's idea to change
    the current rule depending on generation parity.
    
    If original rule has B0 but not S8:

    For even generations, whenever the original rule has a Bx or Sx, omit that 
    bit from the modified rule, and whenever the original rule is missing a
    Bx or Sx, add that bit to the modified rule.
    eg. B03/S23 => B1245678/S0145678.
    
    For odd generations, use Bx if and only if the original rule has S(8-x)
    and use Sx if and only if the original rule has B(8-x).
    eg. B03/S23 => B56/S58.
    
    If original rule has B0 and S8:
    
    Such rules don't strobe, so we just want to invert all the cells.
    The trick is to do both changes: invert the bits, and swap Bx for S(8-x).
    eg. B03/S238 => B123478/S0123467 (for ALL gens).
*/

// create the B0 (no Smax) even generation rule map
void liferules::createB0SmaxRuleMap(const char *birth, const char *survival) {
   int b = 0 ;
   int s = 0 ;
   int i = 0 ;
   char newbirth[10] ;
   char newsurvival[10] ;

   // invert neighbor counts and Bx->S(max-x) and Sx->B(max-x)
   for (i = 0 ; i <= neighbors; i++) {
      // check if the digit is in the birth part
      if (strchr(birth, '0' + i) == 0) {
         // compute Smax-x and add to survival part
         newsurvival[s++] = '0' + (neighbors - i) ;
      }

      // check if the digit is in the survival part
      if (strchr(survival, '0' + i) == 0) {
         // compute Smax-x and add to birth part
         newbirth[b++] = '0' + (neighbors - i) ;
      }
   }

   // terminate strings
   newbirth[b] = 0 ;
   newsurvival[s] = 0 ;

   // create the rule map
   createRuleMap(newbirth, newsurvival) ;
}

// create the B0 (no Smax) odd generation rule map
void liferules::createB0OddRuleMap(const char *birth, const char *survival) {
   int b = 0 ;
   int s = 0 ;
   int i = 0 ;
   char newbirth[10] ;
   char newsurvival[10] ;

   // Bx->S(max-x) and Sx->B(max-x)
   for (i = 0 ; i <= neighbors; i++) {
      // check if the digit is in the birth part
      if (strchr(birth, '0' + i) != 0) {
         // Sx->B(max-x)
         newsurvival[s++] = '0' + (neighbors - i) ;
      }

      // check if the digit is in the survival part
      if (strchr(survival, '0' + i) != 0) {
         // Bx->S(max-x)
         newbirth[b++] = '0' + (neighbors - i) ;
      }
   }

   // terminate strings
   newbirth[b] = 0 ;
   newsurvival[s] = 0 ;

   // create the rule map
   createRuleMap(newbirth, newsurvival) ;
}

// create the B0 Smax rule map
void liferules::createB0EvenRuleMap(const char *birth, const char *survival) {
   int b = 0 ;
   int s = 0 ;
   int i = 0 ;
   char newbirth[10] ;
   char newsurvival[10] ;

   // invert neighbor counts
   for (i = 0 ; i <= neighbors; i++) {
      // check if the digit is in the birth part
      if (strchr(birth, '0' + i) == 0) {
         // add to birth part
         newbirth[b++] = '0' + i ;
      }

      // check if the digit is in the survival part
      if (strchr(survival, '0' + i) == 0) {
         // add to survival part
         newsurvival[s++] = '0' + i ;
      }
   }

   // terminate strings
   newbirth[b] = 0 ;
   newsurvival[s] = 0 ;

   // create the rule map
   createRuleMap(newbirth, newsurvival) ;
}

// remove character from a string in place
void liferules::removeChar(char *string, char skip) {
   int src = 0 ;
   int dst = 0 ;
   int c ;

   // copy characters other than skip
   while( (c = string[src++]) ) {
      if (c != skip) {
         string[dst++] = c ;
      }
   }

   // ensure null terminated
   string[dst] = 0 ;
}

// check whether non-totalistic letters are valid for defined neighbor counts
bool liferules::lettersValid(const char *part) {
   char c ;
   int nindex = 0 ;
   int currentCount = -1 ;

   // get next character
   while ( (c = *part) ) {
      if (c >= '0' && c <= '8') {
         currentCount = c - '0' ;
         nindex = currentCount - 1;
         if (nindex > 3) {
            nindex = 6 - nindex ;
         }
      }
      else {
         // ignore minus
         if (c != '-') {
            // not valid if 0 or 8
            if (currentCount == 0 || currentCount == 8) {
               return false ;
            }

            // check against valid rule letters for this neighbor count
            if (strchr(rule_letters[nindex], c) == 0) {
               return false ;
            }
         }
      }
      part++ ;
   }

   return true ;
}

// set rule
const char *liferules::setrule(const char *rulestring, lifealgo *algo) {
   char *r = (char *)rulestring ;
   char tidystring[MAXRULESIZE] ;  // tidy version of rule string
   char *t = (char *)tidystring ;
   char *end = r + strlen(r) ;     // end of rule string
   char len = 0 ;                  // length of tidy string
   char c ;
   char *charpos = 0 ;
   int digit ;
   int maxdigit = 0 ;              // maximum digit value found
   char *colonpos = 0 ;            // position of colon
   char *slashpos = 0 ;            // position of slash
   char *underscorepos = 0 ;       // position of underscore
   char *bpos = 0 ;                // position of b
   char *spos = 0 ;                // position of s

   // initialise rule type
   initRule() ;

   // we might need to emulate B0 rule by using two different rules for odd/even gens
   alternate_rules = false ;

   // check for colon
   colonpos = strchr(r, ':') ;
   if (colonpos) {
      // only process up to the colon
      end = colonpos ;
   }

   // create lower case version of rule name without spaces
   while (r < end) {
      // get the next character and convert to lowercase
      c = tolower(*r) ;

      // process the character
      switch (c) {
      // birth
      case 'b':
         if (bpos) {
            // multiple b found
            return "Only one B allowed." ;
         }
         bpos = t ;
         *t = c ;
         t++ ;
         break ;

      // survival
      case 's':
         if (spos) {
            // multiple s found
            return "Only one S allowed." ;
         }
         spos = t ;
         *t = c ;
         t++ ;
         break ;

      // slash
      case '/':
         if (slashpos) {
            // multiple slashes found
            return "Only one slash allowed." ;
         }
         slashpos = t ;
         *t = c ;
         t++ ;
         break ;
        
      // underscore
      case '_':
         if (underscorepos) {
            // multiple underscores found
            return "Only one underscore allowed." ;
         }
         underscorepos = t ;
         *t = c ;
         t++ ;
         break ;

      // hex
      case 'h':
         if (neighbormask != MOORE || wolfram != -1) {
            // multiple neighborhoods specified
            return "Only one neighborhood allowed." ;
         }
         neighbormask = HEXAGONAL ;
         neighbors = 6 ;
         *t = c ;
         t++ ;
         break ;

      // von neumann
      case 'v':
         if (neighbormask != MOORE || wolfram != -1) {
            // multiple neighborhoods specified
            return "Only one neighborhood allowed." ;
         }
         neighbormask = VON_NEUMANN ;
         neighbors = 4 ;
         *t = c ;
         t++ ;
         break ;

      // wolfram
      case 'w':
         // check if at beginning of string
         if (t == tidystring) {
            if (neighbormask != MOORE || wolfram != -1) {
               // multiple neighborhoods specified
               return "Only one neighborhood allowed." ;
            }
            wolfram = 0 ;
         }
         else {
            // copy character
            *t = c ;
            t++ ;
            totalistic = false ;
         }
         break ;

      // other characters
      default:
         // ignore space
         if (c != ' ') {
            // check character is valid
            charpos = strchr(valid_rule_letters, c) ;
            if (charpos) {
               // copy character
               *t = c ; 
               t++ ;

               // check if totalistic (i.e. found a valid non-digit)
               digit = charpos - valid_rule_letters ;
               if (digit > 8) {
                  totalistic = false ;
               }
               else {
                  // update maximum digit found
                  if (digit > maxdigit) {
                     maxdigit = digit ;
                  }
               }
            }
            else {
               return "Bad character found." ;
            }
         }
         break ;
      }

      // next character
      r++ ;
   }

   // ensure null terminated
   *t = 0 ;

   // don't allow empty rule string
   t = tidystring ;
   if (*t == 0) {
      return "Rule cannot be empty string." ;
   }

   // can't have slash and underscore
   if (underscorepos && slashpos) {
      return "Can't have slash and underscore." ;
   }

   // underscore only valid for non-totalistic rules
   if (underscorepos && totalistic) {
      return "Underscore not valid for totalistic rules, use slash." ;
   }

   // if underscore defined then set the slash position
   if (underscorepos) {
      slashpos = underscorepos ;
   }

   // check for Wolfram
   if (wolfram == 0) {
      // parse Wolfram 1D rule
      while (*t >= '0' && *t <= '9') {
         wolfram = 10 * wolfram + *t - '0' ;
         t++ ;
      }
      if (wolfram < 0 || wolfram > 254 || wolfram & 1) {
         return "Wolfram rule must be an even number from 0 to 254." ;
      }
      if (*t) {
         return "Bad character in Wolfram rule." ;
      }
   }
   else {
      // if neighborhood specified then must be last character
      if (neighbormask != MOORE) {
         len = strlen(t) ;
         if (len) {
            c = t[len - 1] ;
            if (!((c == 'h') || (c == 'v'))) {
               return "Neighborhood must be at end of rule." ;
            }
            // remove character
            t[len - 1] = 0 ;
         }
      }

      // at least one of slash, b or s must be present
      if (!(slashpos || bpos || spos)) {
         return "Rule must contain a slash or B or S." ;
      }
   
      // digits can not be greater than the number of neighbors for the defined neighborhood
      if (maxdigit > neighbors) {
         return "Digit greater than neighborhood allows." ;
      }

      // if slash present and both b and s then one must be each side of the slash
      if (slashpos && bpos && spos) {
         if ((bpos < slashpos && spos < slashpos) || (bpos > slashpos && spos > slashpos)) {
            return "B and S must be either side of slash." ;
         }
      }

      // check if there was a slash to divide birth from survival
      if (!slashpos) {
         // check if both b and s exist
         if (bpos && spos) {
            // determine whether b or s is first
            if (bpos < spos) {
               // skip b and cut the string using s
               bpos++ ;
               *spos = 0 ;
               spos++ ;
            }
            else {
               // skip s and cut the string using b
               spos++ ;
               *bpos = 0 ;
               bpos++ ;
            }
         }
         else {
            // just bpos
            if (bpos) {
               bpos = t ;
               removeChar(bpos, 'b') ;
               spos = bpos + strlen(bpos) ;
            }
            else {
               // just spos
               spos = t;
               removeChar(spos, 's') ;
               bpos = spos + strlen(spos ) ;
            }
         }
      }
      else {
         // slash exists so set determine which part is b and which is s
         *slashpos = 0 ;

         // check if b or s are defined
         if (bpos || spos) {
            // check for birth first
            if ((bpos && bpos < slashpos) || (spos && spos > slashpos)) {
               // birth then survival
               bpos = t ;
               spos = slashpos + 1 ;
            }
            else {
               // survival then birth
               bpos = slashpos + 1 ;
               spos = t ;
            }

            // remove b or s from rule parts
            removeChar(bpos, 'b') ;
            removeChar(spos, 's') ;
         }
         else {
            // no b or s so survival first
            spos = t ;
            bpos = slashpos + 1 ;
         }
      }

      // if not totalistic and a part exists it must start with a digit
      if (!totalistic) {
         // check birth
         c = *bpos ;
         if (c && (c < '0' || c > '8')) {
            return "Non-totalistic birth must start with a digit." ;
         }
         // check survival
         c = *spos ;
         if (c && (c < '0' || c > '8')) {
            return "Non-totalistic survival must start with a digit." ;
         }
      }
   
      // if not totalistic then neighborhood must be Moore
      if (!totalistic && neighbormask != MOORE) {
         return "Non-totalistic only supported with Moore neighborhood." ;
      }

      // validate letters used against each specified neighbor count
      if (!lettersValid(bpos)) {
         return "Letter not valid for birth neighbor count." ;
      }
      if (!lettersValid(spos)) {
         return "Letter not valid for survival neighbor count." ;
      }
   }

   // AKT: check for rule suffix like ":T200,100" to specify a bounded universe
   if (colonpos) {
      const char* err = algo->setgridsize(colonpos) ;
      if (err) return err ;
   } else {
      // universe is unbounded
      algo->gridwd = 0 ;
      algo->gridht = 0 ;
   }

   // create the rule map
   if (wolfram >= 0) {
      // create the 3x3 map
      createWolframMap() ;

      // save the canonical rule name
      createCanonicalName(algo) ;

      // convert to the 4x4 map
      convertTo4x4Map(rule0) ;
   }
   else {
      // generate the 3x3 map (which also creates the data for the canonical format)
      createRuleMap(bpos, spos) ;

      // save the canonical rule name
      createCanonicalName(algo) ;

      // check for B0 rules
      if (totalistic && strchr(bpos, '0')) {
          // check for Smax
          if (strchr(spos, '0' + neighbors)) {
             // B0 Smax
             createB0SmaxRuleMap(bpos, spos) ;

             // convert to the even 4x4 map
             convertTo4x4Map(rule0) ;
          }
          else {
             // set alternate rules needed
             alternate_rules = true ;

             // B0 without Smax even generation
             createB0EvenRuleMap(bpos, spos) ;

             // convert to the even 4x4 map
             convertTo4x4Map(rule0) ;

             // B0 without Smax odd generation
             createB0OddRuleMap(bpos, spos) ;

             // convert to the odd 4x4 map
             convertTo4x4Map(rule1) ;
          }
      }
      else {
         // non-B0 rule so convert 3x3 to 4x4 map
         convertTo4x4Map(rule0) ;
      }
   }

   // exit with success
   return 0 ;
}

const char* liferules::getrule() {
   return canonrule ;
}

// B3/S23 -> (1 << 3) + (1 << (9 + 2)) + (1 << (9 + 3)) = 0x1808
bool liferules::isRegularLife() {
   return (neighbormask == MOORE && totalistic && rulebits == 0x1808 && wolfram < 0) ;
}
gollybase/liferules.h

Code: Select all

                        /*** /

This file is part of Golly, a Game of Life Simulator.
Copyright (C) 2013 Andrew Trevorrow and Tomas Rokicki.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

 Web site:  http://sourceforge.net/projects/golly
 Authors:   rokicki@gmail.com  andrew@trevorrow.com

                        / ***/
/**
 *   This class implements the rules supported by QuickLife and HashLife.
 *   A rule lookup table is used for computing a new 2x2 grid from
 *   a provided 4x4 grid (two tables are used to emulate B0-not-Smax rules).
 *   The input is a 16-bit integer, with the most significant bit the
 *   upper left corner, bits going horizontally and then down.  The output
 *   is a 6-bit integer, with the top two bits indicating the top row of
 *   the 2x2 output grid and the least significant two bits indicating the
 *   bottom row.  The middle two bits are always zero.
 */
#ifndef LIFERULES_H
#define LIFERULES_H
#include "lifealgo.h"
const int MAXRULESIZE = 200 ;
const int ALL3X3 = 512 ;
class liferules {
public:
   liferules() ;
   ~liferules() ;
   // string returned by setrule is any error
   const char *setrule(const char *s, lifealgo *algo) ;
   const char *getrule() ;
   
   // AKT: we need 2 tables to support B0-not-Smax rule emulation
   // where max is 8, 6 or 4 depending on the neighborhood
   char rule0[65536];        // rule table for even gens if rule has B0 but not Smax,
                             // or for all gens if rule has no B0, or it has B0 *and* Smax
   char rule1[65536];        // rule table for odd gens if rule has B0 but not Smax
   bool alternate_rules;     // set by setrule; true if rule has B0 but not Smax
   
   // AKT: support for various neighborhoods
   // rowett: support for hensel2
   enum neighborhood_masks {
      MOORE = 0x1ff,         // all 8 neighbors
      HEXAGONAL = 0x0fe,     // ignore NE and SW neighbors
      VON_NEUMANN = 0x0ba    // 4 orthogonal neighbors
   } ;

   bool isRegularLife() ;    // is this B3/S23?
   bool isHexagonal() const { return neighbormask == HEXAGONAL ; }
   bool isVonNeumann() const { return neighbormask == VON_NEUMANN ; }
   bool isWolfram() const { return wolfram >= 0 ; }

private:
   // canonical version of valid rule passed into setrule
   char canonrule[MAXRULESIZE] ;
   neighborhood_masks neighbormask ;
   bool totalistic ;         // is rule totalistic?
   int neighbors ;           // number  of neighbors
   int rulebits ;            // bitmask of neighbor counts used (9 birth, 9 survival)
   int letter_bits[18] ;     // bitmask for non-totalistic letters used
   int neg_letter_bits[18] ; // bitmask for non-totalistic negative letters used
   int wolfram ;             // >= 0 if Wn rule (n is even and <= 254)
   int survival_offset ;     // bit offset in rulebits for survival
   void initRule() ;
   void setTotalistic(int value, bool survival) ;
   int flipBits(int x) ;
   int rotateBits90Clockwise(int x) ;
   void setSymmetrical512(int x, int b) ;
   void setSymmetrical(int value, bool survival, int lindex, int normal) ;
   void setTotalisticRuleFromString(const char *rule, bool survival) ;
   void setRuleFromString(const char *rule, bool survival) ;
   void createWolframMap() ;
   void createRuleMap(const char *birth, const char *survival) ;
   void createB0SmaxRuleMap(const char *birth, const char *survival) ;
   void createB0EvenRuleMap(const char *birth, const char *survival) ;
   void createB0OddRuleMap(const char *birth, const char *survival) ;
   void convertTo4x4Map(char *which) ;
   void createCanonicalName(lifealgo *algo) ;
   void removeChar(char *string, char skip) ;
   bool lettersValid(const char *part) ;
   const char *valid_rule_letters ;
   const char *rule_letters[4] ;
   const int *rule_neighborhoods[4] ;
   char rule3x3[ALL3X3] ;  // all 3x3 cell mappings 012345678->4'
} ;
#endif
EDIT: updated with latest fixes
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:

Code: Select all

#C [[ TRACKBOX 0 -1/12 -1/12 0 ]]
#C [[ THEME 4 STARS STOP 3430 AUTOSTART ]]
x = 51, y = 34, rule = B3/S23
32bo$30b2ob2o$32bo$47b2o$46bo2bo$33b2o11b2ob2o$32bo2bo11bo$31b2o3bo10b
3o$31bo4bo$31bo3bo$32bo2bo$33bo3$21bo7bo14b2o$20b3o5bobo12bob2o$19b2ob
4o2bo2bo10b2o$20b3o2bo18bo$21bo2b2o2bo$25b2obobo$29bo10b2o$40b2o2$3o$
4bo28bo$4b2o13bo12bobo$4bo2bo11bo12bobo$5bobo11bo13bo$5bo$4b2o15bo$3bo
bo14bobo$5b2o14bo$2b2o$4bo!
[/list]
Okay, I'm still confused about N and S boundaries -- or else the Help for LifeViewer is. Seems like the "0 -1/12 -1/12 0" above should move the S and W boundaries, but it's the N and W boundaries that are moving.

When I first posted the TRACKBOX example on the Test thread, I wrote it as "TRACKBOX 0 1/2 0 1/6", because the Help says it's TRACKBOX E S W N... and I wanted the S boundary to move at c/2, and the N boundary to move at c/6.

But to get it to work I have to use TRACKBOX 0 1/6 0 1/2, even though that seems backwards. How am I thinking about this wrong -- or should the Help just say TRACKBOX E N W S ?

That wouldn't be the only change, because the error message that came up is "S is great..." ("...greater than N", I assume, but the text goes outside of the viewer and doesn't wrap.) As far as I can see, S should be greater than or equal to N.

The E and W settings seem to make sense, both in the quoted pattern at the top of this message and in my attempt to try the same thing at 90 degrees -- the E boundary is in fact moving at -c/6, and the W boundary is moving at -c/2:

Code: Select all

x = 21, y = 140, rule = B3-ekrj4-j5-enqy68/S2eki3-ek4cnqjz
6b2o$3bob3o3bo$2b3ob2o2bobo$2b3ob2o2bobobo$3bob3o3b2o$6b2o2b2o3bo$12bo
$12bo$6b2o2b2o3bo$3bob3o3b2o$2b3ob2o2bobobo$2b3ob2o2bobo$3bob3o3bo$6b
2o25$5b2o$4b2o$3b2o$4b2o9$5bobo$4bo3bo$4bo$4b4o8$5bo$4bo2bo$4bo$4b3o
20$12b2o$11b2o2bo$10bobo2bo$9bob2obo$8b2o2bo4bo$9bob2o4bo$10b7obo$13b
4o$14b4o11$9bobo$8b6o$10bo2bo$10bo2bo$8b6o$9bobo17$18bo$9b2o7b3o$bob3o
3b4o2b4o$3obo3b3o3b5o$7o3b4ob2o$bobob2obo$4b3obobo$bo3bo2bo$4b3obobo$b
obob2obo$7o3b4ob2o$3obo3b3o3b5o$bob3o3b4o2b4o$9b2o7b3o$18bo!
#C [[ AUTOSTART TRACKBOX -1/6 0 -1/2 0 LOOP 1000 ]]
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:Okay, I'm still confused about N and S boundaries -- or else the Help for LifeViewer is. Seems like the "0 -1/12 -1/12 0" above should move the S and W boundaries, but it's the N and W boundaries that are moving.
Counter-intuitively for LifeViewer N is down the screen (because Y coordinates increase as you go down).
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:
dvgrn wrote:Okay, I'm still confused about N and S boundaries -- or else the Help for LifeViewer is. Seems like the "0 -1/12 -1/12 0" above should move the S and W boundaries, but it's the N and W boundaries that are moving.
Counter-intuitively for LifeViewer N is down the screen (because Y coordinates increase as you go down).
Ouch! No, I must respectfully disagree. If North is downward, West would have to be toward the right side of the screen (i.e., 90 degrees leftward from North). A "North" that has West 90 degrees rightward/clockwise of it isn't really north at all... or else you're looking at the map from underneath, and LifeViewer never warned me I was doing that.

You might say Y coordinates also increase as you go downward on a map in the Southern Hemisphere, but they don't change their compasses so that they're the mirror image of Northern Hemisphere compasses. Even the most contrarian independent-minded Australians, when they print maps with the South Pole at the top, keep the basic chirality of West being 90 degrees clockwise from South.

However, at least that explains why your N/S coordinates are so puzzling but your E/W coordinates make sense. Perhaps some future build might replace "E S W N" with right, up, left, and down? Doesn't seem like even I could get confused by that.
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:Ouch! No, I must respectfully disagree.
I didn't say it was right... If you can't make up arbitrary nonsensical coordinate systems then what can you do? ;)
I really like the idea of viewing the LifeViewer map from behind. I must have an unassigned hotkey I can use for it :)

I'm happy to correct the N and S, which I think I prefer to up, down, east and west. There will be a new build shortly because I need to port some of the work I did on canonical non-totalistic rules back to LifeViewer so it's consistent with the upcoming Golly 2.8 release.
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 210 is now available on the lazyslug server

Enhancements in this build:
  • added a new variable to [[ TITLE ]] string
    • #A - the pattern rule alias name
  • number of times [[ FILL ]] is used is displayed in help information
Fixes in this build:
  • switched N and S for [[ TRACKBOX ]] so N is up and S is down - test
  • fixed [[ TRACKBOX ]] error so it doesn't need to wrap
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 211 is now available on the lazyslug server

Enhancements in this build:
  • non-totalistic rules now use correct canonical form
  • tooltips are now in boxes with shadows
Fixes in this build:
  • at some zooms the right hand grid line was not being drawn
Dave this build should probably be a release candidate.


The backlog is now:
  • make Generations patterns die properly when they hit the boundary
  • accurate "Life ended at" generation when STEP > 1
  • investigate whether bounded grids have the correct population count
  • make Step Forward/Step Back work a generation at a time with Waypoints and add Time Forward/Time Back controls
  • LifeViewerGL (the WebGL version)
  • pattern editor
  • VIC20 mode - it's happening
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 212 is now available on the lazyslug server

Enhancements in this build:
  • CXRLE Pos now works with [R]History rules
  • hotkey "Tab" and shift "Tab" now step forward and back STEP generations
  • UI controls now step back and forward 1 generation
  • hotkey "Space" and hotkey "b" now step back and forward 1 generation
The updated hotkey map is here
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Sometimes we do things because we should or we must.
Sometimes we do things simply because we can.

BBC Model B
C64
VIC 20
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Now investigating asm.js for further performance improvements. Looks like a pig to hand write but my simple test gave a 20% speed improvement.

Would probably become irrelevant with WebGL if I decide to do all of the generation and rendering on the GPU.
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

Build 213 is now available on the lazyslug server

Enhancements in this build:
  • numeric keypad now works as hotkeys
  • changed the behaviour of speed up (hotkey "=" or num "+") and speed down (hotkey "-" or num "-") so they work across GPS and STEP
  • hotkey "0" and num "0" now reset speed and gps
  • hotkey "5" and num "5" now resets angle (used to be hotkey "0")
  • all [[ COLOR ]] recipients can now use named colours
  • hex patterns (except Generations) now support bounded grids - test
Fixes in this build:
  • resizing snapshots could leave unused buffers not resized
The updated hotkey map is here
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:Build 213 is now available on the lazyslug server...
Just ran into some odd behavior with HISTORYFIT --

Code: Select all

#C p96 8-engine Corderman rake
#C Dean Hickerson, 7/3/1998
x = 73, y = 73, rule = B3/S23
54bo$37b3o8bo5bo$36bo3bo6bo7bo$37b2o9b4o2b2o$39b2ob2o9b3o$41b2o11bobo$
55bo7b2o$55bo7b2o4$30b3o$29bo3bo$28bo3b2o17bo$29bo20bobo18b2o$30b4o16b
o2bo17b2o$32bob2o15b2o$32bo2bo$33b2o12b2o$47b2o3$40bo21bobo$39bobo19bo
$39bobo20bo2bo$40bo23bobo$67bo$66b3o$13bo52bo$12bobo49b2o$11bo3bo50bo$
11bo3bo48bo2bo$11bobob3o46bobo$12b2obo2bo45bo$16bobo31bo$16b2o31bobo$
2bo$bobo45bo2bo3bob3o$bobo47bo4bo$bo2bo18b2o27bob2obobo$2bobo17bo2bo
27b2o3bo$5bo17b2o29bo$4b2o$4bo4$2bo15b2o$bobo14b2o$3bo29b3o$3bo10b2o
21bo12b2o$3bo9bo2bo20b2o10bo$14bobo20bo2bo8b2o$4bo10bo22bobo$2ob3o32bo
$2b3ob2o29b2o12b2o$5bo30bobo12b2o$38b2o$35b2o$37bo2$24b3o$24b3o$6b2o
17bo$6b2o18b2o$27bo$26bo4$24b2o$14b2o8b2o$14b2o9bo!
[[ HISTORYFIT ]]
The Info window says that the mode is Autofit History, but either the pattern shows up very small, or it shows up at regular size but the zoom level doesn't change as the pattern grows. Try closing and re-opening the viewer -- the last zoom state is remembered when it probably shouldn't be. Seems to be still a problem with Build 213.

... Yes, I should use TRACKBOX anyway and not be so lazy.
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:Just ran into some odd behavior with HISTORYFIT
There were two issues here:
  1. [[ HISTORYFIT ]] just sets the AutoFit mode. It does not switch AutoFit on. You need [[ AUTOFIT ]] also.
  2. There was a bug in LifeViewer which I've just fixed - test
I've also created a new page so you can quickly cut and paste RLE patterns to test in LifeViewer.
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:
dvgrn wrote:Just ran into some odd behavior with HISTORYFIT
There were two issues here:
  1. [[ HISTORYFIT ]] just sets the AutoFit mode. It does not switch AutoFit on. You need [[ AUTOFIT ]] also.
  2. There was a bug in LifeViewer which I've just fixed - test
Looks like Build 214 fixes the case I ran into most recently:

Code: Select all

x = 3, y = 3, rule = B3cekainyqjr/S2cekain3cekainyqjr
obo$obo$3o!
#C [[ AUTOFIT HISTORYFIT ]]
I don't have time for a thorough review of Build 214 until later this week, but my random spot-checks haven't turned up any problems so far.

As long as Nathaniel doesn't mind rolling back to an earlier build if something really unexpected shows up, 214 seems like a good release candidate to me!
User avatar
Nathaniel
Site Admin
Posts: 907
Joined: December 10th, 2008, 3:48 pm
Location: New Brunswick, Canada
Contact:

Re: Life tag testing

Post by Nathaniel »

Build 214 is now up on the forums and the wiki.
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

Looks like there's either a problem with waypoint labels, or I have to update old waypoint scripts to match new parsing rules. This animation used to say "Construction complete" only after T=3535, but now that label is popping up almost instantly. What am I forgetting?
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:Looks like there's either a problem with waypoint labels, or I have to update old waypoint scripts to match new parsing rules. This animation used to say "Construction complete" only after T=3535, but now that label is popping up almost instantly. What am I forgetting?
See here.
User avatar
dvgrn
Moderator
Posts: 12049
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Life tag testing

Post by dvgrn »

rowett wrote:
dvgrn wrote:What am I forgetting?
See here.
Dang. And it was even the same exact case, and I said "What am I forgetting?" last time, too. Sorry about that.

Usually these cases of apparent absent-mindedness are due to, well, absent-mindedness -- but also due to being in the middle of something else when I run into something strange, that I figure I'd better document before I forget about it. I've patched up the troublesome link properly now. Thanks again for the walkthrough on waypoint-wrestling... it will probably sink in eventually.

EDIT: How about this case? It looks fine the way I've left it, but if I remove AUTOSTART as shown below, LifeViewer only sets the zoom level for the initial view -- it ignores the X and Y settings until you hit the Play button.

Code: Select all

x = 897, y = 903, rule = B3/S23
2o$2o4$3o$o$bo8$21b3o$21bo$22bo28$17b3o$17bo$18bo8$38b3o$38bo$39bo28$
34b3o$34bo$35bo8$55b3o$55bo$56bo28$72b3o$72bo$73bo8$91b3o$91bo$92bo8$
104b3o$104bo$105bo28$119b2o$118b2o$120bo8$149b2o$148b2o$150bo18$179b2o
$178b2o$180bo33$212b3o$212bo$213bo28$250b3o$250bo$251bo28$281b3o$281bo
$282bo28$285b3o$285bo$286bo28$315b3o$315bo$316bo28$360b3o$360bo$361bo
28$375b3o$375bo$376bo28$412b2o$411b2o$413bo28$435b2o$434b2o$436bo8$
493b3o$493bo$494bo28$519b3o$519bo$520bo28$551b3o$551bo$552bo28$574b3o$
574bo$575bo28$615b2o$614b2o$616bo48$654b3o$654bo$655bo28$687b3o$687bo$
688bo28$724b3o$724bo$725bo28$753b2o$752b2o$754bo28$787b3o$787bo$788bo
28$803b2o$802b2o$804bo28$836b2o$835b2o$837bo28$865b2o$864b2o$866bo28$
894b3o$894bo$895bo!
#C [[ STEP 8 ZOOM 4 X -445 Y -415 ]]
#C [[ T 0 PAUSE 2 "loaf2GtoG slow salvo\n35 gliders total" LOOP 4000 ]]
User avatar
rowett
Moderator
Posts: 4596
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: Life tag testing

Post by rowett »

dvgrn wrote:How about this case? It looks fine the way I've left it, but if I remove AUTOSTART as shown below, LifeViewer only sets the zoom level for the initial view -- it ignores the X and Y settings until you hit the Play button.
OK so that one is my fault. Fixed in the next build. That's 1 each.

The release will be a while since:
  1. I've been distracted by implementing B0 non-totalistic rules in Golly and I've now got to port that to LifeViewer.
  2. I'm currently improving Golly's rendering performance for zoomed in patterns (zoom x2 was particularly bad for patterns with lots of cells).
  3. I've switched to a heap based allocation model in LifeViewer which will dramatically reduce GC and enable me to recode some of the key routines in asm.js, plus I'm also keeping an eye on WebAssembly.
Locked