ConwayLife.com - A community for Conway's Game of Life and related cellular automata
Home  •  LifeWiki  •  Forums  •  Download Golly

Nutshell

For general discussion about Conway's Game of Life.

Nutshell

Postby M. I. Wright » April 18th, 2018, 12:47 am

Hi! For the past month or so I've been working on my own derivative ruletable spec+language. I've written a Python 3.6+ utility to compile it down to a normal Golly ruletable.

https://github.com/eltrhn/nutshell

Its main draw, for anyone curious, is in not binding variables by name: this means that the same variable name can be used multiple times in a transition without needing to define uniquely-named variants as in a Golly table. There are many other features too, however!

The README serves as documentation and is as comprehensive as I could make it. Please be aware that a large amount of the info in this thread below is outdated, because I started posting very early on (when the project's name was still "rueltabel", among other larger differences) -- so don't rely on anything I say in my posts below up through May 19th, 2018, lol.

If you want to get started writing your own Nutshell rule, just follow the "setup" instructions in the README to get the transpiler script downloaded, then continue referring to it for info on syntax/semantics/etc. Please also feel free to give me feedback or ask questions on this post :D
Last edited by M. I. Wright on October 23rd, 2018, 4:47 pm, edited 20 times in total.
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: rueltabel

Postby 77topaz » April 18th, 2018, 5:44 am

After witnessing its development over the past few weeks on the Discord, it's nice to finally see this in its finished state on the forums. :D
User avatar
77topaz
 
Posts: 1345
Joined: January 12th, 2018, 9:19 pm

Re: rueltable

Postby M. I. Wright » April 22nd, 2018, 2:57 am

Guess I'll log updates here too. Biggest change now is that I've renamed the thing from "rueltabel" to "rueltable" (for which a certain "Is it a typo?" may have been a motivating factor) -- but other than that, I've also added two new things!

  • The README used to make mention of a "tentative" syntax for the output specifiers -- this is tentative no more. You can now write an output map along the lines of "CD[CD: var]", CD being any compass direction. Take, for instance, the following transition:
    1, any, any, 0, SE..SW (3, 4), 0, any, 0 -> E[SE: (1, 2)]
    The "-> E[SE: (1, 2)]" says "Assuming I'm state 1: take the variable southeast of me, map it to the variable (1, 2), and spawn a cell with that value to my east." Or, in graphical terms:
    # T = 0 #
    0 0 0
    0 1 0
    3 3 3

    # T = 1 #
    0 0 0
    0 0 1
    3 3 3
    # T = 0 #
    0 0 0
    0 1 0
    3 3 4

    # T = 1 #
    0 0 0
    0 0 2
    3 3 4
    ...where "1" is the center cell at T=0. This feature, finally, allows me to actually make the rule I created rueltable for! (so expect that... at some point)
    ---
  • Variables now are able to be (a) subtracted from, and (b) negated, using the - (dash) and -- (double dash) operators. I'll copy/paste this one from the README:
    0, foo-bar, bar-2, bar-(2, 3), -1, --1, -bar, --(3, 4), (foo, bar), baz

    # foo-bar says "All states in foo that are not in bar"
    # foo-2 says "All states in foo that are not 2"
    # bar-(2, 3) says "All states in bar that are not in (2, 3)"
    # -1 says "All *live* states that are not 1" (expands to {2, 3, 4} assuming n_states==5)
    # --1 says "*All* states (including 0) that are not 1" (expands to {0, 2, 3, 4} assuming the same)
    # -bar and --(3, 4) say the same but with multiple states enclosed in a variable
    "Addition" of variables can be accomplished by writing them together in a variable literal, as with the last state above.
    Negation means that rules like DeficientSeeds can be written like so:
    @RUEL DeficientSeeds
    @TABLE
    states: 7
    neighborhood: Moore
    symmetries: rotate4 reflect

    0, N 0, NE -1, E..W 0, NW -1, 1
    0, N -2, NE..SW 0, W -2, NW 0, 2
    0, 0, 0, -3, SE..W 0, -3, 3
    0, -4, NE..W 0, -4, 4
    0, -5, NE..SE 0, -5, SW..NW 0, 5
    0, N..E 0, -6, S..W 0, -6, 6

    any, N..NW any, 0
    and this compiles to 30-40+ lines, depending on how you count. (Rueltabel is obsoleted for this purpose by my other script, but take this as a demo)
    It also allows "NewTons", Blinkerspawn's (basic) alpha-particles-y test rule, to be written like so:
    @RUEL NewTons
    @TABLE
    states: 7
    neighborhood: Moore
    symmetries: none

    # photon splitting
    0, (1, 5), NE..SE any, (1, 3), SW..NW any, 0 -> E[(4, _, ...)]  W[(6, _, ...)]
    0, any, any, (1, 6), SE..SW any, (1, 4), any, 0 -> N[(3, _, ...)]  S[(5, _, ...)]

    # photon moving
    0, --3, any, --4, any, 3, any, --6, any, 3
    0, --3, any, --4, any, --5, any, 4, any, 4
    0, 5, any, --4, any, --5, any, --6, any, 5
    0, --3, any, 6, any, --5, any, --6, any, 6

    # photon dying
    (3..6), N..NW any, 0
    (where it previously required a variable to be explicitly defined for each negation)

A future (non-bugfix) release will have support for two other things: a shorthand permute-symmetry syntax that looks something like this,
0, 1:3, 0, 1  # gets filled in automatically
0, 1:3, 0:5, 1

# Both of which expand to:
0, 1, 1, 1, 0, 0, 0, 0, 0, 1

and, off a suggestion from SuperSuperMario24, the ability to change symmetries midway through a table. This would for example allow DeficientLife to be written like so:
@RUEL DeficientLife
@TABLE
states: 12
neighborhood: Moore

# Birth
symmetries: rotate4 reflect
0, 0, -2, 0, 0, 0, -2, 0, -2, 2
0, -3, 0, -3, 0, 0, 0, -3, 0, 3
0, 0, 0, -4, 0, -4, 0, 0, -4, 4
0, N -5, NE..SW 0, W..NW -5, 5
0, N..NE -6, E..W 0, NW -6, 6
0, 0, -7, -7, SE..W 0, NW -7, 7
0, 0, -8, 0, 0, -8, 0, 0, -8, 8
0, -9, 0, 0, -9, 0, 0, 0, -9, 9
0, -10, 0, -10, 0, 0, 0, 0, -10, 10
0, -11, 0, 0, 0, -11, 0, 0, -11, 11

# Survival
symmetries: permute
live, live:2, 0, 1 # s2
live, live:3, 0, 1 # s3

any, N..NW any, 0

...but, being honest, I'm not quite sure how I should go about this. I know I'll have to go through the completed transitions and expand them all to the "least-abstracted" symmetry (e.g. "symmetries:permute" followed by "symmetries:none" would mean expanding the "symmetries:permute" segment), but I'll need to figure out how to make the compiler know to expand the different symmetry levels to each other. If anyone's reading this and has ideas, shoot :!:
Last edited by M. I. Wright on April 22nd, 2018, 12:00 pm, edited 1 time in total.
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: rueltable

Postby 77topaz » April 22nd, 2018, 5:56 am

But isn't changing from "rueltabel" (two differences from "ruletable") to "rueltable" (one difference from "ruletable") going to make people more likely to think the word is simply a typo? :?
User avatar
77topaz
 
Posts: 1345
Joined: January 12th, 2018, 9:19 pm

Re: rueltable

Postby calcyman » April 22nd, 2018, 6:33 am

77topaz wrote:But isn't changing from "rueltabel" (two differences from "ruletable") to "rueltable" (one difference from "ruletable") going to make people more likely to think the word is simply a typo? :?


Indeed. I also preferred the original name of 'rueltabel' (and am totally in favour of this new high-level language!).
What do you do with ill crystallographers? Take them to the mono-clinic!
User avatar
calcyman
 
Posts: 2072
Joined: June 1st, 2009, 4:32 pm

Re: rueltable

Postby rowett » April 22nd, 2018, 9:20 am

Perhaps better to call it something completely different that isn't a typo.

How about either of the following:
  • "Dictator" - someone who makes rules.
  • "Scobberlotcher" - someone who avoids hard work.
User avatar
rowett
Moderator
 
Posts: 1518
Joined: January 31st, 2013, 2:34 am
Location: UK

Re: rueltable

Postby M. I. Wright » April 22nd, 2018, 11:52 am

All good points, thank you!
I'm going to change it back to "rueltabel" for now, then -- may as well -- but I do see the value in giving it a more-unique name too. Don't know about those two specifically (humorous as they are) but I'd be 100% open to further suggestions :)
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: rueltable

Postby dvgrn » April 22nd, 2018, 12:24 pm

M. I. Wright wrote:All good points, thank you!
I'm going to change it back to "rueltabel" for now, then -- may as well -- but I do see the value in giving it a more-unique name too. Don't know about those two specifically (humorous as they are) but I'd be 100% open to further suggestions :)

How about just "ruletemplate"? Boring, I know, but maybe more likely to make sense to a casual reader than RULEZtable or SovereignTable or what have you.
User avatar
dvgrn
Moderator
 
Posts: 5739
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI

Re: rueltabel

Postby M. I. Wright » April 29th, 2018, 5:31 pm

That's absolutely reasonable, but I have to admit I've grown somewhat attached to the idea of a weird name... eh :roll:

Anyway, here's another update. I finally implemented the symmetry-normalization thing as well as the permute shorthand, the former meaning that it's now acceptable to switch symmetries midway through a tabel. For instance, that DeficientLife.ruel file from above --
@RUEL DeficientLife
@TABEL
states: 12
neighborhood: Moore

# Birth
symmetries: rotate4 reflect
0, 0, -2, 0, 0, 0, -2, 0, -2, 2
0, -3, 0, -3, 0, 0, 0, -3, 0, 3
0, 0, 0, -4, 0, -4, 0, 0, -4, 4
0, N -5, NE..SW 0, W..NW -5, 5
0, N..NE -6, E..W 0, NW -6, 6
0, 0, -7, -7, SE..W 0, NW -7, 7
0, 0, -8, 0, 0, -8, 0, 0, -8, 8
0, -9, 0, 0, -9, 0, 0, 0, -9, 9
0, -10, 0, -10, 0, 0, 0, 0, -10, 10
0, -11, 0, 0, 0, -11, 0, 0, -11, 11

# Survival
symmetries: permute
live, live:2, 0, 1 # s2
live, live:3, 0, 1 # s3

# Death otherwise
any, any:8, 0
now produces a working B3/S23-deficient rulefile.

EDIT: It now produces 'optimal' results (an update to the shrunken pre-edit text below)! The issue was that I was doing the normalizing after resolving variable name-bindings, so a transition napkin like "live_1, live_2, live_3, 0, 0, 0, 0, 0" where all the "live_N"s have the same value was producing multiple "distinct" outputs like:
live_1, 0, 0, 0, 0, live_2, 0, live_3
live_3, 0, 0, 0, 0, live_2, 0, live_1
This was fixed by removing the _N suffixes of all variables that only appear once before normalizing and then restoring them afterward. You can see this in examples/compiled_ruletables/DeficientLife.rule.

However, it is suboptimal. Check the comparison here: rueltabel is producing 42 rotate4reflect transitions from permute-symmetric "live, live live, live, 0, 0, 0, 0, 0, 1" (which is the survival-on-3 line above) -- but, as shown by deficient_gen.py, it apparently only needs to be 10. I'm stumped here.
My approach to normalizing symmetries was to implement a class for each symmetry type with a hash function that produces identical hashes for each identical transformation of itself -- for instance, a "Rotate4((0, 1, 2, 3, 4, 5, 6, 7))" object has the same hash as "Rotate4((2, 3, 4, 5, 6, 7, 0, 1))". Each class also has an "expand()" method that returns a multiple-transition representation of itself under "symmetries:none".
So when expanding from, say, "symmetries:permute" to "symmetries:rotate4reflect", I create a Permute() object from the napkin* of every transition under "symmetries:permute" and expand each, then create a Rotate4Reflect() object from each item of this expansion, and finally toss these rotate4reflect objects into a set. (This is where the hash function comes in -- if an object has the same hash as another it won't create a duplicate set entry)
So I figure there's some symmetry stuff I'm not accounting for that would slim the rueltabel transition list from 42 to 10...? Not sure. Again, if anyone has ideas...
(...and note that my actual code is still rather dirty, so I don't know if looking through it will help much. Apologies)



But enough of that :P I've also added a "find this transition" flag:
rueltabel_find.png
rueltabel_find.png (72.45 KiB) Viewed 12006 times
which makes it so that, if a cell isn't behaving the way it's supposed to, you can "-f" the transition it's doing and it'll find the offending line for you (instead of you having to guess at what you typo'd). Has been quite handy.

*napkin: The neighborhood segment of a transition. (i.e. transition excluding its first and last items -- coined by 83bismuth38 a while ago? in reference to a triangular-neighborhood 'bowling napkin' specifically)

---

So I think this is pretty much a 'feature-complete' alpha now? Everything from my original spec's been implemented! -- no guarantees of being bug-free, though.
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: rueltabel

Postby M. I. Wright » May 11th, 2018, 2:42 am

Alright, last update for now.

EDIT: The @COLORS syntax has changed, as have of course the @RUEL/@TABEL segment headers. Please view the repo's README (@COLORS described near the bottom) for the currently-correct syntax.

  • Changed permute-symmetry shorthand to use "*" instead of ":" to avoid confusion with the other uses of colons. Breaking change, but fortunately I don't have anything besides my own examples to break :P
  • Made all segments optional. The @TABEL and @RUEL segments, if omitted, will no longer throw a fit and can be made 'normal' by spelling the @header names conventionally, and segments whose headers aren't misspelt (@ICONS, @COLORS) can be ignored during parsing by writing "#golly" as the first word after. (That is: "@COLORS #golly" or "@COLORS<newline>#golly".)
  • Implemented a new @ICONS syntax that uses normal XRLE instead of the XPM format. An example follows:
    @NUTSHELL IconTest

    @TABLE
    n_states:14
    neighborhood:Moore
    symmetries:none

    @COLORS
    10: FFF

    @ICONS
    0: 303030
    1: D0D0D0
    2: 007FFF
    3: 535353
    4: AD331D
    5: 1A1A1A
    6: C0C0C0
    7: 6077A0
    8: 405780
    9: 0000FF

    #C 1
    x = 13, y = 13, rule = //10
    5.3A$5.3A$2.9A$2.3A3.3A$2.3A3.3A$3A7.3A$3A7.3A$3A7.3A$2.3A3.3A$2.3A3.
    3A$2.9A$5.3A$5.3A!

    #C 2
    x = 5, y = 11, rule = //10
    5B$5B$2B$2B$2B$2B$2B$2B$2B$5B$5B!

    #C 3
    x = 5, y = 11, rule = //10
    5B$5B$3.2B$3.2B$3.2B$3.2B$3.2B$3.2B$3.2B$5B$5B!

    #C 4
    x = 11, y = 11, rule = //10
    4.3C$4.3C$4.3C$4.3C$11C$11C$11C$4.3C$4.3C$4.3C$4.3C!

    #C 5
    x = 11, y = 3, rule = //10
    11C$11C$11C!

    #C 6
    x = 11, y = 11, rule = //10
    9.2D$7.4D$5.4D$3.4D$.4D$3D$.4D$3.4D$5.4D$7.4D$9.2D!

    #C 7
    x = 11, y = 11, rule = //10
    2D$4D$2.4D$4.4D$6.4D$8.3D$6.4D$4.4D$2.4D$4D$2D!

    #C 8
    x = 9, y = 11, rule = //10
    .7E$9E$9E$9E$9E$9E$.8E$5.4E$5.4E$4.4E$.6E!

    #C 9
    x = 9, y = 7, rule = //10
    .7E$9E$9E$9E$9E$9E$.7E!

    #C 11
    x = 9, y = 9, rule = //10
    2G5.2G$2G5.2G$.2G3.2G$.2G3.2G$2.2G.2G$2.2G.2G$3.3G$3.3G$4.G!

    #C 12
    x = 9, y = 9, rule = //10
    2H5.2H$2H5.2H$.2H3.2H$.2H3.2H$2.2H.2H$2.2H.2H$3.3H$3.3H$4.H!

    #C 13
    x = 9, y = 9, rule = //10
    4.I$3.3I$3.3I$2.2I.2I$2.2I.2I$.2I3.2I$.2I3.2I$2I5.2I$2I5.2I!
    The idea is that you're going to be in Golly anyway when you're fiddling with a rule, so it'll be easier to quickly copy/paste that RLE in and out of a blank Golly tab than to edit XPM images in your text editor. Icons are automatically centered & resized to the nearest Golly icon size (7, 15, 31).

    The first number on each line (in the bit before the RLEs) corresponds to a cell state used below. "0 303030", for instance, assigns the symbol "." (state 0) the color #303030.
    The parser also accepts the cellstate symbol to be written in place of the numbers, as in ". 303030 / A D0D0D0 / B 007FFF / ..." if it's easier to read. (The colon should not be used in this form.)

    The rule of each RLE is ignored -- I just chose a generic multistate //10 to get all desired colors in -- and the comment above each rule is required to start with '#' and contain numbers somewhere in it; more than one number means more than one cell state gets this icon. Multiple numbers here can be separated either by commas, whitespace, or both.
    Note that the icons don't have to be in sequence or even present (the pre-icon comment facilitates ordering). If an icon is skipped (like state 10 is above!) it will be made as a solid square with a color taken from the @COLORS declaration for that state. (An omitted icon with no fill color defined in @COLORS will raise an error, however.)
  • EDIT 5/14/18: You can now also declare a *gradient* to fill in missing states, even when they aren't addressed in @COLORS. The syntax is "? <hex color> <hex color>" or "? <hex color> <ignored separator> <hex color>", defined along with the other colors in @ICONS (before the RLEs).

    The gradient relies on the "states:" directive in @TABEL to compute its start and end -- but if you don't have any @TABEL section then this won't be available, so in this case you may append to this line a bracketed number indicating your desired "n_states" value e.g. "? <hex color> <hex color> [<n_states>]".

    A full example might look like any of "? 000 FFF", "? 000 - FFF", "? 000 FFF [13]", and "? 000 - FFF [13]" -- indicating a gradient from #000 (black) to #FFF (white), with the latter two implying the absence of the @TABEL section and explicitly defining n_states in its lieu.
    --
    (If you don't want a certain @COLORS color to take precedence over the gradient, put an *asterisk before its cellstate in the part before the colon; for instance, "*10: FFF" above. This is a touch particular/specific, though, so I only implemented it as a "just in case" and I fully expect it to go unused.)

Here's the output of that ruel file:
@RULE IconTest
*********************************
**** COMPILED FROM RUELTABEL ****
*********************************


@TABLE
n_states:15
neighborhood:Moore
symmetries:none



@COLORS
10 255 15 240


@ICONS
XPM
"15 195 11 2"
".. c #303030"
"AA c #D0D0D0"
"BB c #007FFE"
"CC c #535353"
"DD c #AD331D"
"EE c #1A1A1A"
"FF c #C0C0C0"
"GG c #6077A0"
"HH c #405780"
"II c #0000FF"
"}b c #FFFFFF"
".............................."
"............AAAAAA............"
"............AAAAAA............"
"......AAAAAAAAAAAAAAAAAA......"
"......AAAAAA......AAAAAA......"
"......AAAAAA......AAAAAA......"
"..AAAAAA..............AAAAAA.."
"..AAAAAA..............AAAAAA.."
"..AAAAAA..............AAAAAA.."
"......AAAAAA......AAAAAA......"
"......AAAAAA......AAAAAA......"
"......AAAAAAAAAAAAAAAAAA......"
"............AAAAAA............"
"............AAAAAA............"
".............................."
".............................."
".............................."
"..........BBBBBBBBBB.........."
"..........BBBBBBBBBB.........."
"..........BBBB................"
"..........BBBB................"
"..........BBBB................"
"..........BBBB................"
"..........BBBB................"
"..........BBBB................"
"..........BBBB................"
"..........BBBBBBBBBB.........."
"..........BBBBBBBBBB.........."
".............................."
".............................."
".............................."
".............................."
"..........BBBBBBBBBB.........."
"..........BBBBBBBBBB.........."
"................BBBB.........."
"................BBBB.........."
"................BBBB.........."
"................BBBB.........."
"................BBBB.........."
"................BBBB.........."
"................BBBB.........."
"..........BBBBBBBBBB.........."
"..........BBBBBBBBBB.........."
".............................."
".............................."
".............................."
".............................."
"............CCCCCC............"
"............CCCCCC............"
"............CCCCCC............"
"............CCCCCC............"
"....CCCCCCCCCCCCCCCCCCCCCC...."
"....CCCCCCCCCCCCCCCCCCCCCC...."
"....CCCCCCCCCCCCCCCCCCCCCC...."
"............CCCCCC............"
"............CCCCCC............"
"............CCCCCC............"
"............CCCCCC............"
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
"....CCCCCCCCCCCCCCCCCCCCCC...."
"....CCCCCCCCCCCCCCCCCCCCCC...."
"....CCCCCCCCCCCCCCCCCCCCCC...."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
"......................DDDD...."
"..................DDDDDDDD...."
"..............DDDDDDDD........"
"..........DDDDDDDD............"
"......DDDDDDDD................"
"....DDDDDD...................."
"......DDDDDDDD................"
"..........DDDDDDDD............"
"..............DDDDDDDD........"
"..................DDDDDDDD...."
"......................DDDD...."
".............................."
".............................."
".............................."
".............................."
"....DDDD......................"
"....DDDDDDDD.................."
"........DDDDDDDD.............."
"............DDDDDDDD.........."
"................DDDDDDDD......"
"....................DDDDDD...."
"................DDDDDDDD......"
"............DDDDDDDD.........."
"........DDDDDDDD.............."
"....DDDDDDDD.................."
"....DDDD......................"
".............................."
".............................."
".............................."
".............................."
"........EEEEEEEEEEEEEE........"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"........EEEEEEEEEEEEEEEE......"
"................EEEEEEEE......"
"................EEEEEEEE......"
"..............EEEEEEEE........"
"........EEEEEEEEEEEE.........."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
"........EEEEEEEEEEEEEE........"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"......EEEEEEEEEEEEEEEEEE......"
"........EEEEEEEEEEEEEE........"
".............................."
".............................."
".............................."
".............................."
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
"}b}b}b}b}b}b}b}b}b}b}b}b}b}b}b"
".............................."
".............................."
".............................."
"......GGGG..........GGGG......"
"......GGGG..........GGGG......"
"........GGGG......GGGG........"
"........GGGG......GGGG........"
"..........GGGG..GGGG.........."
"..........GGGG..GGGG.........."
"............GGGGGG............"
"............GGGGGG............"
"..............GG.............."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
"......HHHH..........HHHH......"
"......HHHH..........HHHH......"
"........HHHH......HHHH........"
"........HHHH......HHHH........"
"..........HHHH..HHHH.........."
"..........HHHH..HHHH.........."
"............HHHHHH............"
"............HHHHHH............"
"..............HH.............."
".............................."
".............................."
".............................."
".............................."
".............................."
".............................."
"..............II.............."
"............IIIIII............"
"............IIIIII............"
"..........IIII..IIII.........."
"..........IIII..IIII.........."
"........IIII......IIII........"
"........IIII......IIII........"
"......IIII..........IIII......"
"......IIII..........IIII......"
".............................."
".............................."
".............................."
("}b" is a randomly-chosen name for the auto-filled-in color)
Last edited by M. I. Wright on July 24th, 2018, 8:07 pm, edited 2 times in total.
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: rueltabel

Postby Redstoneboi » May 19th, 2018, 11:09 pm

does it have some way to "add" 1 to the state number of cells?
if yes, does it roll over when it reaches the state count?
it would be really nice to have an easy way to make a rock paper scissors rule, or emulate a generations rule.
c(>^w^<c)~*
This is 「Fluffy」
「Fluffy」is my sutando.
「Fluffy」has the ability to engineer r e p l i c a t o r s.
「Fluffy」likes to watch spaceship guns in Golly.
「Fluffy」knows Natsuki best girl.
User avatar
Redstoneboi
 
Posts: 340
Joined: May 14th, 2018, 3:57 am

Re: rueltabel

Postby M. I. Wright » May 19th, 2018, 11:41 pm

Sure! If I understand correctly, a mapping can handle this:
@TABLE
states: 256
neighborhood: Moore
symmetries: none

(1..255), N..NW any, [0: (2..255, 1)]

That line expands to 255 Golly transitions: state 1 to state 2, 2 to 3, 3 to 4, ..., 254 to 255, and 255 to 1 (there's the roll-over!). The anonymous variables on either end make use of the double-dot 'range' syntax; 1..255 expands to every state from 1 to 255.

If it's easier to visualize, here's an example with fewer states:
@TABLE
states: 5
neighborhood: von Neumann
symmetries: none

(1, 2, 3, 4), N..W any, [0: (2, 3, 4, 1)]
and this expands to
@TABLE
n_states: 5
neighborhood: vonNeumann
symmetries: none

### variable declarations omitted ###

1, any_1, any_2, any_3, any_4, 2
2, any_1, any_2, any_3, any_4, 3
3, any_1, any_2, any_3, any_4, 4
4, any_1, any_2, any_3, any_4, 1


By the way, there's an example of "Rock-Paper-Scissors Life" in the examples/ directory of the repo, although it's not quite as elegant as I'd have hoped thanks to some annoying stuff with variables.

EDIT: Cellstate ranges now also accept a step; you can still write "first..last" to represent all states from first to last (inclusive), but "first-step..last" now indicates that only every "step-th" state should be included. For example, "(1, 3..9)" expands to (1, 3, 4, 5, 6, 7, 8, 9), but "(1, 3-3..9)" expands to (1, 3, 6, 9).

Cellstate ranges don't currently accept a step, though -- not sure how a not-ugly syntax for this might look? -- so if you wanted to do something fancier like only affect every other state you'd sadly have to type the variable out in full:
@TABLE
states: 30
neighborhood: Moore
symmetries: none

from = (1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27)

# minor abuse of subtraction follows
# it represents all even states (i.e. all live states *except* those included in 'from')
to = live-from

from, N..NW any, [0: to]
...still fits the actual transition on one line, though!
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: Nutshell

Postby M. I. Wright » October 23rd, 2018, 9:51 pm

A small, belated update: Nutshell’s mostly stable + usable now. Some features worth highlighting:

  • Constants: The @NUTSHELL segment, Nutshell’s analogue to @RULE, provides a syntax for declaring “constants” (names that refer to a single cellstate) alongside a description of some particular state. These names can then be used in the rest of the rule (a) without clogging up @TABLE with single-cellstate variable declarations, and (b) without, in some cases, ever needing to refer to cellstates by number.

  • Optional directives: All of “neighborhood: Moore”, “symmetries: none”, and “states: ?” (where states is n_states) are preinitialized and need not be written outright (as long as those values are what’s desired). In particular, states: ? says to infer the states value from the maximum cellstate referred to in the table or declared in @NUTSHELL.

  • Custom range-1-Moore-subset neighborhoods: Passing a comma-delimited list of compass directions to the “neighborhood” directive, such as “neighborhood: NW, N, NE”, makes the rest of the table use that neighborhood (in that order) for its transitions. Nutshell then expands each transition into the smallest encompassing Golly neighborhood.

  • Inline-rulestring transitions: If a certain cellstate or group thereof needs to behave according to a Golly-compatible 2-state rulestring, Nutshell lets you declare so by replacing the middle states (the neighborhood) of a transition with a special syntax that indicates the rulestring, foreground state/s, and background state/s. Among other uses, this allows rules which extend some totalistic or isotropic-nontot rule to not waste precious space manually defining its transitions.
    For example, a History ruletable for any Hensel-expressible rule can be created by replacing the birth and survival conditions in the following template:
    @NUTSHELL XHistory
    Template for History rules.

    1: ON cell
    : {History} Indicates whether cell was ever ON
    : {MarkedON} Marked ON cell; may change to OFF, but remains marked
    : {MarkedOFF} Marked OFF cell; may change to ON, but remains marked
    : {InitialON} Becomes a normal marked OFF cell on dying, but is initially marked differently from a normal marked ON cell
    : {Boundary} Grey boundary cell

    @COLORS
    0F0: 1
    000080: History
    D8FFD8: MarkedON
    F00: MarkedOFF
    FF0: InitialON
    606060: Boundary

    @TABLE
    symmetries: permute

    off = (MarkedOFF, History, 0)
    on = (1, MarkedON, InitialON)

    # Birth; REPLACE THE 3 BELOW
    off, <3 / on / (off, Boundary)>; [0: (MarkedON, 1, ...)]

    # Death on touching a boundary cell
    on, Boundary ~ 1, (0, Boundary); [0: (History, MarkedOFF, ...)]

    # Survival; REPLACE THE 23 BELOW
    on, <23 / on / off>; [0]

    # Death
    on, any; [0: (History, MarkedOFF, ...)]

The README's also gotten a gigantic makeover since I last posted. As the edited first post says, I tried my best to make it comprehensive, and also to structure it as something of a quick-start guide for people familiar with Golly's ruletable format. Consider giving it a read!
gamer54657 wrote:God save us all.
God save humanity.

hgkhjfgh

nutshelltlifeDiscord 'Conwaylife Lounge'
M. I. Wright
 
Posts: 371
Joined: June 13th, 2015, 12:04 pm

Re: Nutshell

Postby simsim314 » March 7th, 2019, 4:44 pm

I've discovered nutshell and used it in my script to generate the QCA rule.

In order to do that I've implemented a lua script. It allows to use golly to create the rule. But instead of stating the "states" your states in golly as input represent variables and functions to unroll some variable. For example I had majority gate - so it's a function that takes three arguments, they came at place 3, 5, 7. The script unrolls all the possibilities and generates a rule based on the options, while keeping other variables just as strings. You write the function in lua, which is relatively simple job usually. The maintenance and bug fixing of rule generated by the script reduced dramatically - I fix only the functions, or the input.

The compilation is done into Nutshell. I also hope to join forces and make a universal script for universal rule table generator, which is defined in golly visually only.

--A script by michael simkin 2019 - auto generates rules using states in golly which represent variables.
--Insert your code only between user definition start and end definitions.
--Register your variables using the line: def:append("any", any)
--f1, f2, ..., fn - represent state 1,2,3...n in golly.
--index_function is the part which selects what part of the rule you want to unroll in order to use the functions.
--index_function[] = {2, 3} will unroll variables which placed in golly in the second and third place.
--In golly you fill 8 states around some x = 0 y = 10*N. You also place the function index in x = 10 y = 10*N next to the rule.
--Forum post here: http://conwaylife.com/forums/viewtopic.php?f=7&t=3361&p=59227&hilit=nutshell#p59227

------------------------------------Helper functions----------------------------------

-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base, init)
   local c = {}    -- a new class instance
   if not init and type(base) == 'function' then
      init = base
      base = nil
   elseif type(base) == 'table' then
    -- our new class is a shallow copy of the base class!
      for i,v in pairs(base) do
         c[i] = v
      end
      c._base = base
   end
   -- the class will be the metatable for all its objects,
   -- and they will look up their methods in it.
   c.__index = c

   -- expose a constructor which can be called by <classname>(<args>)
   local mt = {}
   mt.__call = function(class_tbl, ...)
   local obj = {}
   setmetatable(obj,c)
   if init then
      init(obj,...)
   else
      -- make sure that any stuff from the base class is initialized!
      if base and base.init then
      base.init(obj, ...)
      end
   end
   return obj
   end
   c.init = init
   c.is_a = function(self, klass)
      local m = getmetatable(self)
      while m do
         if m == klass then return true end
         m = m._base
      end
      return false
   end
   setmetatable(c, mt)
   return c
end

Definitions = class(function(df)
  df.table_of_everything = {}
  df.table_of_keys = {}
  df.table_of_idx = {}
end)

function Definitions:append(name, value)
   
   self.table_of_everything[name] = value
   self.table_of_keys[#self.table_of_keys + 1] = name
   self.table_of_idx[name] = #self.table_of_keys
end

function interpret(f, params)
   if #params == 0 then
      return f()
   elseif #params == 1 then
      return f(params[1])
   elseif #params == 2 then
      return f(params[1], params[2])
   elseif #params == 3 then
      return f(params[1], params[2], params[3])
   end
end

function index_to_function(idx)
   if idx == 1 then
      return f1
   elseif idx == 2 then
      return f2
   elseif idx == 3 then
      return f3
   elseif idx == 4 then
      return f4
   elseif idx == 5 then
      return f5
   elseif idx == 6 then
      return f6
   elseif idx == 7 then
      return f7
   elseif idx == 8 then
      return f8
   elseif idx == 9 then
      return f9
   elseif idx == 10 then
      return f10
   elseif idx == 11 then
      return f11
   elseif idx == 12 then
      return f12
   elseif idx == 13 then
      return f13
   elseif idx == 14 then
      return f14
   elseif idx == 15 then
      return f15
   end
end

NdIterator = class(function(it, sizes)
  it.box = sizes
  it.cur = {}
  it.flags = {}
  for i = 1, #sizes do
   it.cur[#it.cur + 1] = 1
   it.flags[#it.flags + 1] = false
  end
end)

function NdIterator:next()
   for i = 1, #self.box do
      self.cur[i] = self.cur[i] + 1
      
      if self.cur[i] > self.box[i] then
         self.cur[i] = 1
      else
         break
      end
   end
end

function NdIterator:total()
   total = 1
   for i = 1, #self.box do
      total = total * self.box[i]
   end
   
   return total
end

local def = Definitions()

--------------------------------------User definitions Start--------------------------------------

--visual compiler
local g =  golly()

--0 - empty space
--switch - 0,1,unk,A0,A1 : 1, 2, 3, 4, 5
--hold - 0,1,unk,A0,A1 : 6, 7, 8, 9, 10
--relese  A0, A1, r,  : 11, 12, 13
--relax - A0, A1, r,  : 14, 15, 16

function from_idx(idx)
   
   if idx <= 10 then
      idx = (idx - 1) % 5 + 1
      
      if idx == 1 then
         return 0
      elseif idx == 2 then
         return 1
      elseif idx == 3 then
         return 2
      elseif idx == 4 then
         return 0
      else
         return 1
      end
      
   else
      idx = idx - 10
      idx = idx % 3
      return idx
   end
end

--state 1
any  = {"any"}

--state 2
live = {"live"}

--state 3
switch = {1, 2, 3, 4, 5}

--state 4
hold = {6, 7, 8, 9, 10}

--state 5
relese = {11, 12, 13}

--state 6
relax = {14, 15, 16}

--state 7
switch_hold = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

--state 8
not_switch_hold = {0, 11, 12, 13, 14, 15, 16}

--state 9
undefined_relax = {16}

--state 10
defined_relax = {14, 15}

--state 11
not_switch = {0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}

def:append("any", any)
def:append("live", live)
def:append("switch", switch)
def:append("hold", hold)
def:append("relese", relese)
def:append("relax", relax)
def:append("switch_hold", switch_hold)
def:append("not_switch_hold", not_switch_hold)
def:append("undefined_relax", undefined_relax)
def:append("defined_relax", defined_relax)
def:append("not_switch", not_switch)


--switch->hold
function f1(sw)
   return sw + 5
end

--hold->relese
function f2(hld)
   if hld < 9 then
      return 13
   else
      return hld + 2
   end
end

--release->relax
function f3(ease)
   return ease + 3
end

--(defined_relax)->(defined_switch)
function f4(defax)
   return defax - 10
end

--switch_hold->switch
function f5(sw)
   return (from_idx(sw) + 1)
end

--(swr1, swr2)->switch (known or unknown depending)
function f6(sw1, sw2)
   if from_idx(sw1) == from_idx(sw2) then
      return from_idx(sw1) + 1
   else
      return 3
   end
end

function f7(sw1, sw2)
   if from_idx(sw1) == from_idx(sw2)then
      if  from_idx(sw2) ~= 2 then
         return 2 - from_idx(sw1)
      else
         return 3
      end
   else
      return 3
   end
end

function f8(sw1, sw2, sw3)
   
   arr = {sw1, sw2, sw3}
   counter = {0,0,0}
   
   for i = 1, 3 do
      idx = from_idx(arr[i]) + 1
      counter[idx] =  counter[idx] + 1
      
      if counter[idx] == 2 then
         return idx
      end
   end
   
   return 3
end

function f9(sw)
   return 3
end

index_function = {{0}, {0}, {0}, {0}, {7}, {3, 7}, {4, 6}, {3, 5, 7}, {0}}
colors = {
"0 255 0: 1, 4, 6, 9, 11, 14",
"0 150 0: 6",
"255 255 0: 2, 5, 10, 12, 15",
"150 150 0: 7",
"192 192 192: 3, 8",
"64 64 64: 13, 16"
}
--------------------------------------User definitions End--------------------------------------
---------------------Compilation part don't touch--------------------------------

for i = 1, #index_function do
   indeces = index_function[i]

   for j = 1, #indeces do
      index_function[i][j] = index_function[i][j] + 1
   end
end

function toskip(x, y)
   
   if #(g.getcells({x - 10, y, 20, 1})) >= 60 or #(g.getcells({x - 10, y, 20, 1})) == 0 then
      return true
   else
      return false
   end
end

golly_order = {{0, 0},{0, -1},{1, -1},{1, 0},{1, 1},{0, 1},{-1, 1},{-1, 0},{-1, -1}}
rules = {}

rect = g.getrect()
y0 = math.floor(rect[2] / 10) * 10 - 20
y1 = math.floor((rect[2] + rect[4]) / 10) * 10 + 20

y = y0
total = 0
while y < y1 do
   
   rule = {{}, 0}
   --Fill the variables
   if toskip(0, y) == false then
      for i = 1, 9 do
         rule[1][#rule[1] + 1] = g.getcell(0 + golly_order[i][1], y + golly_order[i][2])
      end
   
      --This is the function index
      rule[2] = g.getcell(10, y)       
      rules[#rules + 1] = rule
   end
   
   y = y + 10
end

name = g.getstring("Enter full path with file name")

local file = io.open(name, "w")

file:write("@NUTSHELL QdCA\n\n@TABLE\nstates: 17\nsymmetries: rotate4 reflect\nneighborhood: Moore\n")

for i = 1, #def.table_of_keys do
   value = def.table_of_everything[def.table_of_keys[i]]
   
   if (value[1] == "any" or value[1] == "live") == false then
      file:write(def.table_of_keys[i].." = (")
      for idx = 1, #value do
         if idx == #value then
            file:write(tostring(value[idx])..")\n")
         else
            file:write(tostring(value[idx])..", ")
         end
         
      end
   end
end

for i = 1, #rules do
   rule = rules[i]
   vars = rule[1] --9 variables around me
   func_idx = rule[2] -- function index

   input_vars = index_function[func_idx]
   local state_iterator = NdIterator({1, 1, 1, 1, 1, 1, 1, 1, 1})
      
   for j = 1, #input_vars do
      values = def.table_of_everything[def.table_of_keys[vars[input_vars[j]]]]
      state_iterator.box[input_vars[j]] = #values
      state_iterator.flags[input_vars[j]] = true
   end
   
   max_idx = state_iterator:total()

   for state_iterator_idx = 1, max_idx do
      input_func = {}
      
      for j = 1, #state_iterator.box do
         if state_iterator.flags[j] == false then
            
            var_name = "0"
            
            if vars[j] ~= 0 then
               var_name = def.table_of_keys[vars[j]]
            end
            
            if j == #state_iterator.box then
               file:write(var_name.."; ")
            else
               file:write(var_name..", ")
            end
                     
         else
            values = def.table_of_everything[def.table_of_keys[vars[j]]]
            value = values[state_iterator.cur[j]]
            input_func[#input_func + 1] = value
            
            if j == #state_iterator.box then
               file:write(tostring(value).."; ")
            else
               file:write(tostring(value)..", ")
            end
         end
      end
      
      value = interpret(index_to_function(func_idx), input_func)
      file:write(tostring(value).."\n")
      state_iterator:next()
   end
end

file:write("@COLORS\n")

for i = 1,#colors do
   file:write(colors[i].."\n")
end

file.close()


Here is an example of the input:
x = 144, y = 246, rule = QdCA
63.80F7$60.F$60.F$60.F31.3A$60.F31.ACA8.A$60.F31.3A$61.F$4.5F52.F$3.F
2.F.2F51.F$2.F59.F$2.F59.F$.F35.4F21.F$.F23.F10.2F2.F3.F.6F.2F7.F$.F
5.4F5.4F5.3F7.F3.2F3.F.F5.3F6.F30.3A$.2F7.2F3.2F2.F5.2F.F5.F2.3F4.2F
5.2F.F6.F30.ADA8.B$2.F6.2F.F2.5F4.3F2.F3.2F2.2F5.2F5.F2.2F5.F30.3A$3.
2F4.F2.F2.F6.4F3.2F2.F2.F7.2F5.F.F.2F.F3.F$4.3F2.F2.F3.2F3.2F.2F5.F.
3F8.F7.2F2.F.F3.2F$7.2F3.F5.4F9.3F5.3F2.F11.3F4.F$31.9F23.F4$92.3A$
92.AEA8.C$92.3A8$92.3A$92.AJA8.D$92.3A9$64.80F9$92.3H$21.F70.GIH8.E$
20.F3.F67.A2H$7.F11.2F19.3F$7.F11.F7.F4.F5.3F.F$7.F11.F7.F3.3F4.F2.2F
$8.F5.F3.F4.2F2.F2.F6.F2.2F$8.F4.3F2.F5.F2.F.2F6.3F$9.F3.F.F.2F5.F2.F
.F5.4F$10.3F2.3F6.F2.2F8.F8.F$10.2F12.F2.2F8.F7.2F45.3H$38.F4.3F46.GI
G8.F$38.5F49.3H8$92.3H$92.HIH8.I$92.3H8$92.3K$92.CIK8.J$92.3K89$10.F
52.80F$9.2F23.F$.F7.F24.F$3F6.F24.F$4F4.F25.F2.F11.4F$2F.F4.F6.2F3.2F
4.2F3.7F8.4F3.3F$2F.F4.F4.2F.F2.F5.3F5.F8.2F5.4F$2F.2F3.F3.2F.2F2.F4.
2F2.F4.2F.F2.3F.F$2F2.F2.F3.4F4.F.F2.F3.F4.2F.F.F3.F$F3.F2.F3.2F5.F2.
F2.2F.2F4.F2.3F2.2F50.H$F3.F2.F2.F3.2F2.F.3F2.5F2.2F.6F52.I9.G$F4.2F
3.4F4.2F2.F6.F2.4F2.F3.2F48.G.G$5.2F3.2F10.F6.F.2F6.4F$5.2F15.F6.2F$
19.3F.F$18.F4.F$18.F4.F$18.F4.F$18.F4.F$18.F4.F$18.2F3.F39.80F$20.4F
8$93.H$92.GIG8.H$93.G$3.2F8.2F16.F21.2F$4.2F7.2F15.2F22.2F$4.3F5.2F.F
39.F$4.F.F5.F2.F39.F$4.F2.2F2.F3.F9.2F5.2F22.F$4.F4.3F3.F7.2F.F3.2F
17.F6.F$5.F3.2F4.F6.2F2.F3.2F3.3F11.F6.F$5.F10.F4.2F2.2F2.3F.6F17.F4.
F$5.F10.F3.2F3.2F2.2F2.2F2.F.F4.3F2.F6.F3.2F$5.2F9.F3.F3.F.F.3F2.F3.F
.F3.2F.F3.F6.F.2F.F8.F$5.2F9.F3.F2.F3.2F.F2.F2.2F.F3.F2.F3.F6.2F4.F7.
F$6.F10.F3.3F5.F3.F.2F2.F2.F7.F5.2F6.F5.2F$17.F11.F3.3F3.F2.F7.F7.F6.
2F3.F$17.F11.F3.2F5.2F8.2F6.F7.F3.F$18.F10.F10.2F9.F6.F4.F3.4F$18.F
10.F10.2F9.F7.F3.F6.F$18.2F8.2F29.F3.F5.F$19.F8.F30.F2.2F5.F$19.F8.F
31.2F7.F$28.F31.F8.F$27.F41.F$25.3F40.F$25.2F41.F$68.F$68.F$68.F$68.F
$67.F$66.2F$65.F$64.F$64.F$63.F$62.2F!


Input example (make sure all cells centers are at X = 0, Y = 10*N and the functions codes are at same Y, and X = 10) - better use the attached file.
Attachments
Golly_input_simkin_script.mc
(5.84 KiB) Downloaded 144 times
User avatar
simsim314
 
Posts: 1689
Joined: February 10th, 2014, 1:27 pm


Return to General Discussion

Who is online

Users browsing this forum: No registered users and 0 guests