Brilliant, thanks!Nathaniel wrote:Done!rowett wrote:Nathaniel - sorry I found a bug with LifeViewer on the Wiki! Please will you upload build 207.
Life tag testing
Re: Life tag testing
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 208 is now available on the lazyslug server
Enhancements in this build:
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
- 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
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 209 is now available on the lazyslug server
Enhancements in this build:
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
- Track mode details were sometimes incorrect in help information
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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.
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.
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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.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.
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.
Re: Life tag testing
Just for fun...
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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!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.
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) ;
}
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
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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.rowett wrote:[/list]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!
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 ]]Re: Life tag testing
Counter-intuitively for LifeViewer N is down the screen (because Y coordinates increase as you go down).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.
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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.rowett wrote:Counter-intuitively for LifeViewer N is down the screen (because Y coordinates increase as you go down).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.
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.
Re: Life tag testing
I didn't say it was right... If you can't make up arbitrary nonsensical coordinate systems then what can you do?dvgrn wrote:Ouch! No, I must respectfully disagree.
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.
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 210 is now available on the lazyslug server
Enhancements in this build:
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
- 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
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 211 is now available on the lazyslug server
Enhancements in this build:
The backlog is now:
Enhancements in this build:
- non-totalistic rules now use correct canonical form
- tooltips are now in boxes with shadows
- at some zooms the right hand grid line was not being drawn
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
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 212 is now available on the lazyslug server
Enhancements in this build:
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
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Sometimes we do things because we should or we must.
Sometimes we do things simply because we can.
BBC Model B
C64
VIC 20
Sometimes we do things simply because we can.
BBC Model B
C64
VIC 20
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
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.
Would probably become irrelevant with WebGL if I decide to do all of the generation and rendering on the GPU.
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Build 213 is now available on the lazyslug server
Enhancements in this build:
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
- resizing snapshots could leave unused buffers not resized
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Just ran into some odd behavior with HISTORYFIT --rowett wrote:Build 213 is now available on the lazyslug server...
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 ]]... Yes, I should use TRACKBOX anyway and not be so lazy.
Re: Life tag testing
There were two issues here:dvgrn wrote:Just ran into some odd behavior with HISTORYFIT
- [[ HISTORYFIT ]] just sets the AutoFit mode. It does not switch AutoFit on. You need [[ AUTOFIT ]] also.
- There was a bug in LifeViewer which I've just fixed - test
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Looks like Build 214 fixes the case I ran into most recently:rowett wrote:There were two issues here:dvgrn wrote:Just ran into some odd behavior with HISTORYFIT
- [[ HISTORYFIT ]] just sets the AutoFit mode. It does not switch AutoFit on. You need [[ AUTOFIT ]] also.
- There was a bug in LifeViewer which I've just fixed - test
Code: Select all
x = 3, y = 3, rule = B3cekainyqjr/S2cekain3cekainyqjr
obo$obo$3o!
#C [[ AUTOFIT HISTORYFIT ]]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!
- Nathaniel
- Site Admin
- Posts: 907
- Joined: December 10th, 2008, 3:48 pm
- Location: New Brunswick, Canada
- Contact:
Re: Life tag testing
Build 214 is now up on the forums and the wiki.
Re: Life tag testing
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?
Re: Life tag testing
See here.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?
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife
Re: Life tag testing
Dang. And it was even the same exact case, and I said "What am I forgetting?" last time, too. Sorry about that.rowett wrote:See here.dvgrn wrote:What am I forgetting?
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 ]]Re: Life tag testing
OK so that one is my fault. Fixed in the next build. That's 1 each.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.
The release will be a while since:
- I've been distracted by implementing B0 non-totalistic rules in Golly and I've now got to port that to LifeViewer.
- I'm currently improving Golly's rendering performance for zoomed in patterns (zoom x2 was particularly bad for patterns with lots of cells).
- 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.
LifeViewer https://lazyslug.com/lifeviewer LifeViewer Pro https://lazyslug.com/lifeviewer/pro
BeebLife https://lazyslug.com/beeblife
BeebLife https://lazyslug.com/beeblife