Golly scripts

For scripts to aid with computation or simulation in cellular automata.
Naszvadi
Posts: 817
Joined: May 7th, 2016, 8:53 am
Contact:

Re: Golly scripts

Post by Naszvadi » June 27th, 2020, 6:31 am

Please test this script!

@dvgrn should give this pattern converter script a name due to the lack of ideas - and also further improvements to be planned like generalized hexasymm 4-state rulespace/generator for corresponding OT life-like rules etc:

Code: Select all

#!/usr/bin/python

# Pattern converter script for use with Golly.
# Author: Peter, NASZVADI (), June 2020.
# BUGGY! Do NOT use this script!

# Converts B3/S23 patterns to equivalent hexasymmcgol ones.

import golly as g

currentcellsunsorted = g.getcells(g.getrect())
currentcells = zip(currentcellsunsorted[::2], currentcellsunsorted[1::2])
newcells = {}

if not currentcells:
    g.show('No alive cells. Aborting script.')
elif g.getrule().lower() not in ['b3/s23', 'life', 'b3_s23']:
    g.show('Aborting script - not a CGoL rule: ' + g.getrule())
else:
    for i in currentcells:
        g.setcell(i[0], i[1], 0)
    g.setrule('hexasymmcgol')
    firstrow = min(currentcellsunsorted[::2])
    for i in currentcells:
        xnew = i[0] + i[1] - firstrow
        inew = (xnew // 2, i[1])
        if inew in newcells:
            newcells[inew] = 3
        else:
            newcells[inew] = 2 - xnew % 2
    for i in newcells:
        g.setcell(i[0], i[1], newcells[i])
Update #1:
The above script probably converts badly part of patterns belong to negative quarter-space, an RC2 version is here:

Code: Select all

#!/usr/bin/python

# Pattern converter script for use with Golly.
# Author: Peter, NASZVADI (), June 2020.

# Converts B3/S23 patterns to equivalent hexasymmcgol ones.

import golly as g

currentcellsunsorted = g.getcells(g.getrect())
currentcells = zip(currentcellsunsorted[::2], currentcellsunsorted[1::2])
newcells = {}

if not currentcells:
    g.show('No alive cells. Aborting script.')
elif g.getrule().lower() not in ['b3/s23', 'life', 'b3_s23']:
    g.show('Aborting script - not a CGoL rule: ' + g.getrule())
else:
    for i in currentcells:
        g.setcell(i[0], i[1], 0)
    g.setrule('hexasymmcgol')
    firstrow = min(currentcellsunsorted[::2])
    for i in currentcells:
        xnew = i[0] + i[1] - firstrow
        inew = ((xnew - (xnew % 2)) // 2, i[1])
        if inew in newcells:
            newcells[inew] = 3
        else:
            newcells[inew] = 2 - xnew % 2
    for i in newcells:
        g.setcell(i[0], i[1], newcells[i])
Attachments
hexasymmcgol.rule.gz
UNPACK it before use!
(17.86 KiB) Downloaded 81 times

User avatar
Ian07
Posts: 691
Joined: September 22nd, 2018, 8:48 am

Re: Golly scripts

Post by Ian07 » August 1st, 2020, 3:17 pm

A relatively simple Life-to-SkewLife converter:

Code: Select all

"""
skew.py, by Ian07

Returns the current pattern as a pattern in SkewLife (i.e. the LifeViewer rule R2,C2,S2-3,B3,N@22a544, specifically rotated 45 degrees clockwise from the original) in the clipboard.

Probably won't work with showinviewer.lua for the moment, since Golly itself currently doesn't support custom neighborhoods.

Bugs:
-Copying doesn't appear to work consistently, and has been temporarily disabled
"""

import golly as g

cells = g.getcells(g.getrect())
g.new("output")
# Affine transformation: each orthogonal movement is mapped to a diagonal movement
g.putcells(cells, 0, 0, 1, -1, 1, 1)

# Change the rule
"""
g.select(g.getrect())
g.copy()
clip = g.getclipstr().replace("B3/S23", "R2,C2,S2-3,B3,N@22a544")
g.setclipstr(clip)
"""

User avatar
LaundryPizza03
Posts: 1303
Joined: December 15th, 2017, 12:05 am
Location: Unidentified location "https://en.wikipedia.org/wiki/Texas"

Re: Golly scripts

Post by LaundryPizza03 » August 9th, 2020, 7:09 am

Dictionary for converting MAP transitions to Hensel notation. The symbols 0c and 8c are used by LLS.

Code: Select all

{0: 'B0c', 1: 'B1c', 2: 'B1e', 3: 'B2a', 4: 'B1c', 5: 'B2c', 6: 'B2a', 7: 'B3i', 8: 'B1e', 9: 'B2a', 10: 'B2e', 11: 'B3a', 12: 'B2k', 13: 'B3n', 14: 'B3j', 15: 'B4a', 16: 'S0c', 17: 'S1c', 18: 'S1e', 19: 'S2a', 20: 'S1c', 21: 'S2c', 22: 'S2a', 23: 'S3i', 24: 'S1e', 25: 'S2a', 26: 'S2e', 27: 'S3a', 28: 'S2k', 29: 'S3n', 30: 'S3j', 31: 'S4a', 32: 'B1e', 33: 'B2k', 34: 'B2e', 35: 'B3j', 36: 'B2a', 37: 'B3n', 38: 'B3a', 39: 'B4a', 40: 'B2i', 41: 'B3r', 42: 'B3e', 43: 'B4r', 44: 'B3r', 45: 'B4i', 46: 'B4r', 47: 'B5i', 48: 'S1e', 49: 'S2k', 50: 'S2e', 51: 'S3j', 52: 'S2a', 53: 'S3n', 54: 'S3a', 55: 'S4a', 56: 'S2i', 57: 'S3r', 58: 'S3e', 59: 'S4r', 60: 'S3r', 61: 'S4i', 62: 'S4r', 63: 'S5i', 64: 'B1c', 65: 'B2c', 66: 'B2k', 67: 'B3n', 68: 'B2n', 69: 'B3c', 70: 'B3q', 71: 'B4n', 72: 'B2a', 73: 'B3i', 74: 'B3j', 75: 'B4a', 76: 'B3q', 77: 'B4n', 78: 'B4w', 79: 'B5a', 80: 'S1c', 81: 'S2c', 82: 'S2k', 83: 'S3n', 84: 'S2n', 85: 'S3c', 86: 'S3q', 87: 'S4n', 88: 'S2a', 89: 'S3i', 90: 'S3j', 91: 'S4a', 92: 'S3q', 93: 'S4n', 94: 'S4w', 95: 'S5a', 96: 'B2k', 97: 'B3y', 98: 'B3k', 99: 'B4k', 100: 'B3q', 101: 'B4y', 102: 'B4q', 103: 'B5j', 104: 'B3r', 105: 'B4t', 106: 'B4j', 107: 'B5n', 108: 'B4z', 109: 'B5r', 110: 'B5q', 111: 'B6a', 112: 'S2k', 113: 'S3y', 114: 'S3k', 115: 'S4k', 116: 'S3q', 117: 'S4y', 118: 'S4q', 119: 'S5j', 120: 'S3r', 121: 'S4t', 122: 'S4j', 123: 'S5n', 124: 'S4z', 125: 'S5r', 126: 'S5q', 127: 'S6a', 128: 'B1e', 129: 'B2k', 130: 'B2i', 131: 'B3r', 132: 'B2k', 133: 'B3y', 134: 'B3r', 135: 'B4t', 136: 'B2e', 137: 'B3j', 138: 'B3e', 139: 'B4r', 140: 'B3k', 141: 'B4k', 142: 'B4j', 143: 'B5n', 144: 'S1e', 145: 'S2k', 146: 'S2i', 147: 'S3r', 148: 'S2k', 149: 'S3y', 150: 'S3r', 151: 'S4t', 152: 'S2e', 153: 'S3j', 154: 'S3e', 155: 'S4r', 156: 'S3k', 157: 'S4k', 158: 'S4j', 159: 'S5n', 160: 'B2e', 161: 'B3k', 162: 'B3e', 163: 'B4j', 164: 'B3j', 165: 'B4k', 166: 'B4r', 167: 'B5n', 168: 'B3e', 169: 'B4j', 170: 'B4e', 171: 'B5c', 172: 'B4j', 173: 'B5y', 174: 'B5c', 175: 'B6c', 176: 'S2e', 177: 'S3k', 178: 'S3e', 179: 'S4j', 180: 'S3j', 181: 'S4k', 182: 'S4r', 183: 'S5n', 184: 'S3e', 185: 'S4j', 186: 'S4e', 187: 'S5c', 188: 'S4j', 189: 'S5y', 190: 'S5c', 191: 'S6c', 192: 'B2a', 193: 'B3n', 194: 'B3r', 195: 'B4i', 196: 'B3q', 197: 'B4y', 198: 'B4z', 199: 'B5r', 200: 'B3a', 201: 'B4a', 202: 'B4r', 203: 'B5i', 204: 'B4q', 205: 'B5j', 206: 'B5q', 207: 'B6a', 208: 'S2a', 209: 'S3n', 210: 'S3r', 211: 'S4i', 212: 'S3q', 213: 'S4y', 214: 'S4z', 215: 'S5r', 216: 'S3a', 217: 'S4a', 218: 'S4r', 219: 'S5i', 220: 'S4q', 221: 'S5j', 222: 'S5q', 223: 'S6a', 224: 'B3j', 225: 'B4k', 226: 'B4j', 227: 'B5y', 228: 'B4w', 229: 'B5k', 230: 'B5q', 231: 'B6k', 232: 'B4r', 233: 'B5n', 234: 'B5c', 235: 'B6c', 236: 'B5q', 237: 'B6k', 238: 'B6n', 239: 'B7c', 240: 'S3j', 241: 'S4k', 242: 'S4j', 243: 'S5y', 244: 'S4w', 245: 'S5k', 246: 'S5q', 247: 'S6k', 248: 'S4r', 249: 'S5n', 250: 'S5c', 251: 'S6c', 252: 'S5q', 253: 'S6k', 254: 'S6n', 255: 'S7c', 256: 'B1c', 257: 'B2n', 258: 'B2k', 259: 'B3q', 260: 'B2c', 261: 'B3c', 262: 'B3n', 263: 'B4n', 264: 'B2k', 265: 'B3q', 266: 'B3k', 267: 'B4q', 268: 'B3y', 269: 'B4y', 270: 'B4k', 271: 'B5j', 272: 'S1c', 273: 'S2n', 274: 'S2k', 275: 'S3q', 276: 'S2c', 277: 'S3c', 278: 'S3n', 279: 'S4n', 280: 'S2k', 281: 'S3q', 282: 'S3k', 283: 'S4q', 284: 'S3y', 285: 'S4y', 286: 'S4k', 287: 'S5j', 288: 'B2a', 289: 'B3q', 290: 'B3j', 291: 'B4w', 292: 'B3i', 293: 'B4n', 294: 'B4a', 295: 'B5a', 296: 'B3r', 297: 'B4z', 298: 'B4j', 299: 'B5q', 300: 'B4t', 301: 'B5r', 302: 'B5n', 303: 'B6a', 304: 'S2a', 305: 'S3q', 306: 'S3j', 307: 'S4w', 308: 'S3i', 309: 'S4n', 310: 'S4a', 311: 'S5a', 312: 'S3r', 313: 'S4z', 314: 'S4j', 315: 'S5q', 316: 'S4t', 317: 'S5r', 318: 'S5n', 319: 'S6a', 320: 'B2c', 321: 'B3c', 322: 'B3y', 323: 'B4y', 324: 'B3c', 325: 'B4c', 326: 'B4y', 327: 'B5e', 328: 'B3n', 329: 'B4n', 330: 'B4k', 331: 'B5j', 332: 'B4y', 333: 'B5e', 334: 'B5k', 335: 'B6e', 336: 'S2c', 337: 'S3c', 338: 'S3y', 339: 'S4y', 340: 'S3c', 341: 'S4c', 342: 'S4y', 343: 'S5e', 344: 'S3n', 345: 'S4n', 346: 'S4k', 347: 'S5j', 348: 'S4y', 349: 'S5e', 350: 'S5k', 351: 'S6e', 352: 'B3n', 353: 'B4y', 354: 'B4k', 355: 'B5k', 356: 'B4n', 357: 'B5e', 358: 'B5j', 359: 'B6e', 360: 'B4i', 361: 'B5r', 362: 'B5y', 363: 'B6k', 364: 'B5r', 365: 'B6i', 366: 'B6k', 367: 'B7e', 368: 'S3n', 369: 'S4y', 370: 'S4k', 371: 'S5k', 372: 'S4n', 373: 'S5e', 374: 'S5j', 375: 'S6e', 376: 'S4i', 377: 'S5r', 378: 'S5y', 379: 'S6k', 380: 'S5r', 381: 'S6i', 382: 'S6k', 383: 'S7e', 384: 'B2a', 385: 'B3q', 386: 'B3r', 387: 'B4z', 388: 'B3n', 389: 'B4y', 390: 'B4i', 391: 'B5r', 392: 'B3j', 393: 'B4w', 394: 'B4j', 395: 'B5q', 396: 'B4k', 397: 'B5k', 398: 'B5y', 399: 'B6k', 400: 'S2a', 401: 'S3q', 402: 'S3r', 403: 'S4z', 404: 'S3n', 405: 'S4y', 406: 'S4i', 407: 'S5r', 408: 'S3j', 409: 'S4w', 410: 'S4j', 411: 'S5q', 412: 'S4k', 413: 'S5k', 414: 'S5y', 415: 'S6k', 416: 'B3a', 417: 'B4q', 418: 'B4r', 419: 'B5q', 420: 'B4a', 421: 'B5j', 422: 'B5i', 423: 'B6a', 424: 'B4r', 425: 'B5q', 426: 'B5c', 427: 'B6n', 428: 'B5n', 429: 'B6k', 430: 'B6c', 431: 'B7c', 432: 'S3a', 433: 'S4q', 434: 'S4r', 435: 'S5q', 436: 'S4a', 437: 'S5j', 438: 'S5i', 439: 'S6a', 440: 'S4r', 441: 'S5q', 442: 'S5c', 443: 'S6n', 444: 'S5n', 445: 'S6k', 446: 'S6c', 447: 'S7c', 448: 'B3i', 449: 'B4n', 450: 'B4t', 451: 'B5r', 452: 'B4n', 453: 'B5e', 454: 'B5r', 455: 'B6i', 456: 'B4a', 457: 'B5a', 458: 'B5n', 459: 'B6a', 460: 'B5j', 461: 'B6e', 462: 'B6k', 463: 'B7e', 464: 'S3i', 465: 'S4n', 466: 'S4t', 467: 'S5r', 468: 'S4n', 469: 'S5e', 470: 'S5r', 471: 'S6i', 472: 'S4a', 473: 'S5a', 474: 'S5n', 475: 'S6a', 476: 'S5j', 477: 'S6e', 478: 'S6k', 479: 'S7e', 480: 'B4a', 481: 'B5j', 482: 'B5n', 483: 'B6k', 484: 'B5a', 485: 'B6e', 486: 'B6a', 487: 'B7e', 488: 'B5i', 489: 'B6a', 490: 'B6c', 491: 'B7c', 492: 'B6a', 493: 'B7e', 494: 'B7c', 495: 'B8c', 496: 'S4a', 497: 'S5j', 498: 'S5n', 499: 'S6k', 500: 'S5a', 501: 'S6e', 502: 'S6a', 503: 'S7e', 504: 'S5i', 505: 'S6a', 506: 'S6c', 507: 'S7c', 508: 'S6a', 509: 'S7e', 510: 'S7c', 511: 'S8c'}
Or, in list format:

Code: Select all

['B0c', 'B1c', 'B1e', 'B2a', 'B1c', 'B2c', 'B2a', 'B3i', 'B1e', 'B2a', 'B2e', 'B3a', 'B2k', 'B3n', 'B3j', 'B4a', 'S0c', 'S1c', 'S1e', 'S2a', 'S1c', 'S2c', 'S2a', 'S3i', 'S1e', 'S2a', 'S2e', 'S3a', 'S2k', 'S3n', 'S3j', 'S4a', 'B1e', 'B2k', 'B2e', 'B3j', 'B2a', 'B3n', 'B3a', 'B4a', 'B2i', 'B3r', 'B3e', 'B4r', 'B3r', 'B4i', 'B4r', 'B5i', 'S1e', 'S2k', 'S2e', 'S3j', 'S2a', 'S3n', 'S3a', 'S4a', 'S2i', 'S3r', 'S3e', 'S4r', 'S3r', 'S4i', 'S4r', 'S5i', 'B1c', 'B2c', 'B2k', 'B3n', 'B2n', 'B3c', 'B3q', 'B4n', 'B2a', 'B3i', 'B3j', 'B4a', 'B3q', 'B4n', 'B4w', 'B5a', 'S1c', 'S2c', 'S2k', 'S3n', 'S2n', 'S3c', 'S3q', 'S4n', 'S2a', 'S3i', 'S3j', 'S4a', 'S3q', 'S4n', 'S4w', 'S5a', 'B2k', 'B3y', 'B3k', 'B4k', 'B3q', 'B4y', 'B4q', 'B5j', 'B3r', 'B4t', 'B4j', 'B5n', 'B4z', 'B5r', 'B5q', 'B6a', 'S2k', 'S3y', 'S3k', 'S4k', 'S3q', 'S4y', 'S4q', 'S5j', 'S3r', 'S4t', 'S4j', 'S5n', 'S4z', 'S5r', 'S5q', 'S6a', 'B1e', 'B2k', 'B2i', 'B3r', 'B2k', 'B3y', 'B3r', 'B4t', 'B2e', 'B3j', 'B3e', 'B4r', 'B3k', 'B4k', 'B4j', 'B5n', 'S1e', 'S2k', 'S2i', 'S3r', 'S2k', 'S3y', 'S3r', 'S4t', 'S2e', 'S3j', 'S3e', 'S4r', 'S3k', 'S4k', 'S4j', 'S5n', 'B2e', 'B3k', 'B3e', 'B4j', 'B3j', 'B4k', 'B4r', 'B5n', 'B3e', 'B4j', 'B4e', 'B5c', 'B4j', 'B5y', 'B5c', 'B6c', 'S2e', 'S3k', 'S3e', 'S4j', 'S3j', 'S4k', 'S4r', 'S5n', 'S3e', 'S4j', 'S4e', 'S5c', 'S4j', 'S5y', 'S5c', 'S6c', 'B2a', 'B3n', 'B3r', 'B4i', 'B3q', 'B4y', 'B4z', 'B5r', 'B3a', 'B4a', 'B4r', 'B5i', 'B4q', 'B5j', 'B5q', 'B6a', 'S2a', 'S3n', 'S3r', 'S4i', 'S3q', 'S4y', 'S4z', 'S5r', 'S3a', 'S4a', 'S4r', 'S5i', 'S4q', 'S5j', 'S5q', 'S6a', 'B3j', 'B4k', 'B4j', 'B5y', 'B4w', 'B5k', 'B5q', 'B6k', 'B4r', 'B5n', 'B5c', 'B6c', 'B5q', 'B6k', 'B6n', 'B7c', 'S3j', 'S4k', 'S4j', 'S5y', 'S4w', 'S5k', 'S5q', 'S6k', 'S4r', 'S5n', 'S5c', 'S6c', 'S5q', 'S6k', 'S6n', 'S7c', 'B1c', 'B2n', 'B2k', 'B3q', 'B2c', 'B3c', 'B3n', 'B4n', 'B2k', 'B3q', 'B3k', 'B4q', 'B3y', 'B4y', 'B4k', 'B5j', 'S1c', 'S2n', 'S2k', 'S3q', 'S2c', 'S3c', 'S3n', 'S4n', 'S2k', 'S3q', 'S3k', 'S4q', 'S3y', 'S4y', 'S4k', 'S5j', 'B2a', 'B3q', 'B3j', 'B4w', 'B3i', 'B4n', 'B4a', 'B5a', 'B3r', 'B4z', 'B4j', 'B5q', 'B4t', 'B5r', 'B5n', 'B6a', 'S2a', 'S3q', 'S3j', 'S4w', 'S3i', 'S4n', 'S4a', 'S5a', 'S3r', 'S4z', 'S4j', 'S5q', 'S4t', 'S5r', 'S5n', 'S6a', 'B2c', 'B3c', 'B3y', 'B4y', 'B3c', 'B4c', 'B4y', 'B5e', 'B3n', 'B4n', 'B4k', 'B5j', 'B4y', 'B5e', 'B5k', 'B6e', 'S2c', 'S3c', 'S3y', 'S4y', 'S3c', 'S4c', 'S4y', 'S5e', 'S3n', 'S4n', 'S4k', 'S5j', 'S4y', 'S5e', 'S5k', 'S6e', 'B3n', 'B4y', 'B4k', 'B5k', 'B4n', 'B5e', 'B5j', 'B6e', 'B4i', 'B5r', 'B5y', 'B6k', 'B5r', 'B6i', 'B6k', 'B7e', 'S3n', 'S4y', 'S4k', 'S5k', 'S4n', 'S5e', 'S5j', 'S6e', 'S4i', 'S5r', 'S5y', 'S6k', 'S5r', 'S6i', 'S6k', 'S7e', 'B2a', 'B3q', 'B3r', 'B4z', 'B3n', 'B4y', 'B4i', 'B5r', 'B3j', 'B4w', 'B4j', 'B5q', 'B4k', 'B5k', 'B5y', 'B6k', 'S2a', 'S3q', 'S3r', 'S4z', 'S3n', 'S4y', 'S4i', 'S5r', 'S3j', 'S4w', 'S4j', 'S5q', 'S4k', 'S5k', 'S5y', 'S6k', 'B3a', 'B4q', 'B4r', 'B5q', 'B4a', 'B5j', 'B5i', 'B6a', 'B4r', 'B5q', 'B5c', 'B6n', 'B5n', 'B6k', 'B6c', 'B7c', 'S3a', 'S4q', 'S4r', 'S5q', 'S4a', 'S5j', 'S5i', 'S6a', 'S4r', 'S5q', 'S5c', 'S6n', 'S5n', 'S6k', 'S6c', 'S7c', 'B3i', 'B4n', 'B4t', 'B5r', 'B4n', 'B5e', 'B5r', 'B6i', 'B4a', 'B5a', 'B5n', 'B6a', 'B5j', 'B6e', 'B6k', 'B7e', 'S3i', 'S4n', 'S4t', 'S5r', 'S4n', 'S5e', 'S5r', 'S6i', 'S4a', 'S5a', 'S5n', 'S6a', 'S5j', 'S6e', 'S6k', 'S7e', 'B4a', 'B5j', 'B5n', 'B6k', 'B5a', 'B6e', 'B6a', 'B7e', 'B5i', 'B6a', 'B6c', 'B7c', 'B6a', 'B7e', 'B7c', 'B8c', 'S4a', 'S5j', 'S5n', 'S6k', 'S5a', 'S6e', 'S6a', 'S7e', 'S5i', 'S6a', 'S6c', 'S7c', 'S6a', 'S7e', 'S7c', 'S8c']

Code: Select all

x = 4, y = 3, rule = B3-q4z5y/S234k5j
2b2o$b2o$2o!
LaundryPizza03 at Wikipedia

The latest edition of new-gliders.db.txt and oscillators.db.txt have 31531 spaceships and 1293 oscillators from outer-totalistic rules. You are invited to help!

User avatar
EvinZL
Posts: 480
Joined: November 8th, 2018, 4:15 pm
Location: A tungsten pool travelling towards the sun
Contact:

Re: Golly scripts

Post by EvinZL » November 6th, 2020, 11:25 am

Now that Golly 4 supports Python 3, here's a nutshell loader.

First, you need nutshell/nutshell put in the same folder as your script.

Then save this script as a Python file:

Code: Select all

import golly as g

import os
from nutshell import segmentor, compiler

ruel = g.getclipstr()
lines = ruel.splitlines(True)
finished = compiler.compile(segmentor.parse(lines))
rule = finished.split("\n")[0][6:]
path = g.getdir("rules") + rule + ".rule"
if os.path.exists(path):
    g.warn(f"{rule}.rule already exists. Do you want to override it?")
with open(path, "w") as f:
    f.write(finished)
g.setrule(rule)
g.setclipstr(finished)
So my directory structure is like
nutshell1.jpg
nutshell1.jpg (6.02 KiB) Viewed 2321 times
Where the nutshell folder is the actual nutshell library.

And then to load a nutshell file, just copy the file to clipboard and then run the nutshell loader. It should change the rule and copy the compiled ruletable to your clipboard.

In practice, I just have Ctrl+Shift+L keybound to that.

Code: Select all

x = 59, y = 12, rule = B2i3-kq4j/S2-i3
.2A7.2A35.2A7.2A$3A7.3A15.3A15.3A7.3A$.2A7.2A15.A3.A15.2A7.2A$26.A5.A
$26.A5.A$26.3A.3A2$26.3A.3A$26.A5.A$26.A5.A$27.A3.A$28.3A!

User avatar
dvgrn
Moderator
Posts: 7987
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » November 6th, 2020, 11:32 am

EvinZL wrote:
November 6th, 2020, 11:25 am
Now that Golly 4 supports Python 3, here's a nutshell loader.
Wonderful, thanks! When nutshell first came out I was thinking about how it could be supported within Golly, but the Python 2/Python 3 problem just seemed too big to deal with. It's nice how problems sometimes solve themselves if you just wait long enough.

User avatar
Ian07
Posts: 691
Joined: September 22nd, 2018, 8:48 am

Re: Golly scripts

Post by Ian07 » November 6th, 2020, 1:44 pm

EvinZL wrote:
November 6th, 2020, 11:25 am
And then to load a nutshell file, just copy the file to clipboard and then run the nutshell loader. It should change the rule and copy the compiled ruletable to your clipboard.
I ran the Python script and got this error:
Attachments
ff332285d8057ecc54918f15fc429939.png
ff332285d8057ecc54918f15fc429939.png (16.5 KiB) Viewed 2301 times

wildmyron
Posts: 1519
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » November 6th, 2020, 7:52 pm

Ian07 wrote:
November 6th, 2020, 1:44 pm
EvinZL wrote:
November 6th, 2020, 11:25 am
And then to load a nutshell file, just copy the file to clipboard and then run the nutshell loader. It should change the rule and copy the compiled ruletable to your clipboard.
I ran the Python script and got this error:
The problem is that you've got a file named nutshell.py in the location where you should have a folder named nutshell. Have you named EvinZLs script nutshell.py? If so, rename it to load_nutshell.py. Also, you just need the nutshell folder from the repo, not the whole repo put into a folder named nutshell.
The latest version of the 5S Project contains over 226,000 spaceships. There is also a GitHub mirror of the collection. Tabulated pages up to period 160 (out of date) are available on the LifeWiki.

Currently nactive here due to a severe case of LWTDS.

User avatar
Ian07
Posts: 691
Joined: September 22nd, 2018, 8:48 am

Re: Golly scripts

Post by Ian07 » November 6th, 2020, 11:55 pm

wildmyron wrote:
November 6th, 2020, 7:52 pm
The problem is that you've got a file named nutshell.py in the location where you should have a folder named nutshell. Have you named EvinZLs script nutshell.py? If so, rename it to load_nutshell.py. Also, you just need the nutshell folder from the repo, not the whole repo put into a folder named nutshell.
Both done, but the error now looks like this:
Attachments
52381dc74c0d21217d819fdfef8cfe77.png
52381dc74c0d21217d819fdfef8cfe77.png (29.27 KiB) Viewed 2284 times

wildmyron
Posts: 1519
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Golly scripts

Post by wildmyron » November 26th, 2020, 12:09 pm

Here is an updated version of my SOF canonisation script for Golly Python. It can now canonise all periodic patterns according to the requirements on http://pentadecathlon.com/objects/defin ... itions.php and is Py2/Py3 compatible. This script has had a bit more testing than the previous version, but there may still be patterns which are not correctly encoded into standard form. Thanks to Ian07 for some additional testing.

Code: Select all

#sof-canonise.py v1.0
# Canonical small object format representation
# Representation for CGoL objects devised by Heinrich Koenig
# Format description at http://conwaylife.com/wiki/SOF
# Further information at http://www.pentadecathlon.com/objects/definitions/definitions.php
# Determine the canonical small object format representation of the current
# pattern using the comparison rules.
#
# By Arie Paap
# Oct. 2014
#
# Changelog
# Nov. 2020: v1.0 - Added canonisation of spaceships
# Nov. 2020: Fixed another bug in sof_compare(). Might actually work for oscillators now.
#            Added periodicity detection with bijoscar()
# Nov. 2020: Py2/Py3 compatibility. Fixed major bug in sof_compare().

import math
import golly as g

# Determine period and displacement of current pattern
# Part of apgsearch v1.0 by Adam P. Goucher
def bijoscar(maxsteps):
    initpop = int(g.getpop())
    initrect = g.getrect()
    if (len(initrect) == 0):
        return 0, None
    inithash = g.hash(initrect)
    
    for i in range(maxsteps):
        g.run(1)
        
        if (int(g.getpop()) == initpop):
            prect = g.getrect()
            phash = g.hash(prect)
            
            if (phash == inithash):
                period = i + 1
                
                if (prect == initrect):
                    return period, (0, 0)
                else:
                    dx = prect[0] - initrect[0]
                    dy = prect[1] - initrect[1]
                    return period, (dx, dy)

    return -1, None

# Define a sign function
sign = lambda x: int(math.copysign(1, x))

# Obtains a canonical representation of the current pattern
# Uses algorithm by Adam P. Goucher
def sof_canonise(duration, dxdy):
    sof = ''
    moving = 's' # Stationary

    # For spaceships the pattern needs to be oriented in the standard direction
    # N, NW, or in between.
    if not dxdy == (0, 0):
        dx, dy = dxdy
        if abs(dx) > abs(dy):
            a, b, c, d = 0, -sign(dy), -sign(dx), 0
        else:
            a, b, c, d = -sign(dx), 0, 0, -sign(dy)
        rect = g.getrect()
        ship = g.getcells(rect)
        g.select(rect)
        g.clear(0)
        g.putcells(ship, 0, 0, a, b, c, d)
        
        dx, dy = (dx * a + dy * b, dx * c + dy * d)
        if dx == 0:
            moving = 'o' # Orthogonal
        elif dx == -dy:
            moving = 'd' # Diagonal
        else:
            moving = 'k' # Oblique

    # We need to compare each phase to find the one used to determine the
    # pattern's standard form. The standard form is selected from a phase
    # with the minimum population
    minpop = int(g.getpop())
    if (duration > 1):
        for t in range(duration):
            minpop = min(minpop, int(g.getpop()))
            g.run(1)
    
    if minpop == 0:
        return '0'

    for t in range(duration):
        if int(g.getpop()) == minpop:
            rect = g.getrect()
            ox = rect[0]
            oy = rect[1]
            x_length = rect[2]
            y_breadth = rect[3]
                
            # Choose the orientation which comes first according to sof comparison rules:
            sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox, oy, 1, 0, 0, 1)) # Identity
            if moving in ('s', 'o'):
                sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox+x_length-1, oy, -1, 0, 0, 1)) # flip_x
            if moving in ('s', 'd'):
                sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox+x_length-1, oy+y_breadth-1, 0, -1, -1, 0)) # swap_xy_flip
            if moving == 's':
                sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox, oy+y_breadth-1, 1, 0, 0, -1)) # flip_y
                sof = sof_compare(sof, sof_representation(x_length, y_breadth, ox+x_length-1, oy+y_breadth-1, -1, 0, 0, -1)) # 180
                sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox, oy, 0, 1, 1, 0)) # swap_xy
                sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox+x_length-1, oy, 0, -1, 1, 0)) # cw 90
                sof = sof_compare(sof, sof_representation(y_breadth, x_length, ox, oy+y_breadth-1, 0, 1, -1, 0)) # ccw 90
        
        if (duration > 1):
            g.run(1)
    
    return sof

# Determine sof for current pattern in specified orientation:
def sof_representation(length, breadth, ox, oy, a, b, c, d):
    sof = ''
    
    for v in range(breadth):
        value = 1
        run = 0
        blank = True
        if (v != 0):
            sof += '-'
        for u in range(length+1):
            x = ox + a*u + b*v
            y = oy + c*u + d*v
            
            if (g.getcell(x,y) == value):
                # Continue the run
                run += 1
            else:
                # Encode the run, unless a run of zeros reaches the boundary
                if (u < length or value == 1):
                    while (run > 78):
                        run -= 78
                        sof += '~0'
                    sof += chr(run + 48) # ord('0') = 48
                run = 1
                value = 1 - value # Toggle value
            if (value==1):
                blank = False
        if (blank == True):
            # Remove unnecessary '0'
            sof = sof[:-2] + '-'
    sof += '.'
    return sof

# Compares two sof patterns according to sof comparison rules.
def sof_compare(a, b):
    if a == b: return a
    if not a: return b
    if not b: return a
    
    alines = str.split(a, '-')
    blines = str.split(b, '-')
    aline = ''
    bline = ''
    
    # Find first line with different cell
    for line in range(0, min(len(alines), len(blines))):
        aline = alines[line]
        bline = blines[line]
        if not (aline == bline):
            break
    
    # Find first difference
    value = 1
    ii = 0
    for ii in range(0, min(len(aline), len(bline))):
        if not (aline[ii] == bline[ii]):
            break
        value = 1 - value
    
    if not aline:
        return b
    if not bline:
        return a
        
    if (value == 1):
        if aline[ii] > bline[ii]:
            return a
        else:
            return b
    else:
        if aline[ii] > bline[ii]:
            return b
        else:
            return a

# p = int(g.getstring('Enter period of pattern', '1'))
p, dxdy = bijoscar(1000)
if dxdy is None:
    if p == 0:
        g.exit('Pattern has died out.')
    else:
        g.exit('Pattern periodicity not detected within 1000 generations.')

canonPatt = sof_canonise(p, dxdy)
g.setclipstr(canonPatt)
g.show('SOF representation of pattern is: ' + canonPatt + ' (copied to clipboard).')
The latest version of the 5S Project contains over 226,000 spaceships. There is also a GitHub mirror of the collection. Tabulated pages up to period 160 (out of date) are available on the LifeWiki.

Currently nactive here due to a severe case of LWTDS.

User avatar
otismo
Posts: 658
Joined: August 18th, 2010, 1:41 pm
Location: Florida
Contact:

Re: Golly scripts

Post by otismo » January 10th, 2021, 7:13 am

Eylrid wrote:
October 12th, 2009, 5:16 am
Andrew wrote:
Eylrid wrote:I can't seem to get it to work for some reason.
PM 2Ring is using a very old version of Golly so some of his scripts don't work with newer versions (2.0 or later). Here is a fixed version:

Code: Select all

# Golly Python script. Written by PM 2Ring, March 2009. Updated August 2009.
# Modified for Golly 2.0+ by Andrew Trevorrow.

''' Create a 'bitmap printer'.

    Print a bitmap in LWSSs, using the current selection as the bitmap source.
'''

import golly
from glife import *

def get_bitmap():
    ''' Get current selection & turn it into a bitmap. '''
    selrect = golly.getselrect()
    if len(selrect) == 0: golly.exit("There is no selection, aborting.")

    #Get bitmap size
    w, h = selrect[2:]
    #Adjust width, so it's in the form of 4m, m>1
    w = (w + 3) // 4 * 4
    w = max(8, w)

    #Initialize empty bitmap
    row = w * [0]
    bm = [row[:] for i in xrange(h)]

    #Populate bitmap with cell data
    u, v = selrect[:2]
    cells = golly.getcells(selrect)
    cellsxy = [(cells[i] - u, cells[i+1] - v) for i in xrange(0, len(cells), 2)]
    for x, y in cellsxy:
        bm[y][x] = 1
    return bm

def linemaker(loopm):
    ''' Create an empty LineMaker '''
    LM = pattern('''
34b2o$34bo$25b2o5bobo$24b3o5b2o$12bo8bob2o$12bobo6bo2bo$2o11bobo5bob2o
$2o11bo2bo7b3o21b2o$13bobo9b2o21b2o$12bobo$12bo9bo$23b2o$22b2o6$47b3o$
24b2o20bo3bo$24b2o19bo5bo$46bo3bo$47b3o$47b3o5$49b3o$44bo3b2ob2o$42b2o
4b2ob2o$35b2o6b2o3b5o$35b2o10b2o3b2o7$45b2o5b2o$45bo6bo$46b3o4b3o$48bo
6bo!''')
    LMTop = LM + pattern('2b2o$2bo$obo$2o!', 32, 7)
    LMBase = pattern('''
11bo$10bo$10b3o12$23b2o$23bobo$10bobo13bo$9bo2bo2b2o6bo2bo12b2o$8b2o5b
obo8bo11b3o$2o4b2o3bo3bo3bo3bobo9bob2o15bo$2o6b2o5b3ob2o2b2o10bo2bo8b
3o4bobo$9bo2bo3b2o17bob2o16bobo$10bobo25b3o2b2o2bo7bo2bo3b2o$39b2o2bo
3bo7bobo4b2o$44bo2bo6bobo$26bo17b2o8bo$24bobo$25b2o2$36bo$36bobo$36b2o
7$33bo7bo$32b4o5bobo$27b2o2bo2b2o8b2o4b2o$27b2o2b2o11b2o4b2o$16b2o6b2o
10bo7b2o$16b2o5b3o10bo4bobo$24b2o10bo4bo$27b2o$27b2o!''', 14, 29)
    LMBase += LM(55, 42, flip) 
    return LMTop(8 + loopm, -21 - loopm) + LMBase

def dmprinter(pdy, copies=1):
    ''' Generate & display a dot matrix printer for named bitmap pattern '''
    #Horizontal pixel separation between LineMakers. minimum = 5
    LMsx = 5

    #Horizontal distance between bitmap pixels. Constant, due to LineMaker period
    pdx = 15

    #Distance between LineMakers
    LMdx, LMdy = -LMsx * pdx, pdy

    #Get bitmap pattern from current selection.
    bm = get_bitmap()

    #Make printer in a new layer
    golly.duplicate()
    golly.setoption("syncviews", False)

    #Build new window title from old
    title = golly.getname().split('.')[0]
    title = '%s_Printer [%d] v0.10' % (title, pdy)
    golly.new(title)

    #Make sure we're using the standard Life generation rule
    golly.setrule("B3/S23")

    #Bitmap dimensions. Width MUST be of form 4m, for m >=1
    bmheight = len(bm)
    bmwidth = len(bm[0])
    mid = (bmheight + 1) // 2

    loopw = (bmwidth - 8) // 4
    loopm = pdx * loopw

    #Gliders, heading north-east
    g0 = pattern('2bo$2o$b2o!')
    g1 = pattern('obo$2o$bo!')
    gliders = [g0, g1, g1(0, 2, rccw), g0(0, 2, rccw),
        g0(2, 2, flip), g1(2, 2, flip), g1(2, 0, rcw), g0(2, 0, rcw)]
        
    #Create full Glider loop.
    gg = []
    ox, oy = 35, 23
    for j in xrange(loopw + 1):
        dd = pdx * j
        gg += [gliders[0](ox + dd, oy - dd), gliders[1](ox + 8 + dd, oy - 7 - dd)]
    dd = loopm
    gg += [gliders[2](45 + dd, 4 - dd), gliders[3](37 + dd, -3 - dd)]

    ox, oy = 26 + loopm, -4 - loopm
    for j in xrange(loopw + 1):
        dd = pdx * j
        gg += [gliders[4](ox - dd, oy + dd), gliders[5](ox - 8 - dd, oy + 7 + dd)]
    dd = loopm
    gg += [gliders[6](16, 15), gliders[7](24, 22)]
    parity = 2*((loopw + 1)*(0, 0) + (1, 1))

    def buildLM():
        ''' Build a complete LineMaker '''
        #Populate glider loop. (jj, ii) are bitmap coords
        gloop = pattern()
        for j in xrange(bmwidth):
            jj = (j - delta + bmwidth - 1) % bmwidth

            #Is there a pixel at this location?
            if bm[ii][jj] == parity[j]:                
                gloop += gg[j]  #Add glider to the loop

        #Only put LineMaker if glider loop isn't empty
        if len(gloop) > 0:
            (LMBlank + gloop).put(Lx, Ly, trans)

    #Assemble blank LineMaker
    LMBlank = linemaker(loopm)

    #Do upper LineMakers
    trans = identity
    for i in xrange(mid):
        ii = mid - (i + 1)
        io = mid + (i + 1)
        Lx, Ly = io * LMdx, ii * LMdy
        delta = LMsx * io
        buildLM()

    #Do lower LineMakers
    trans = flip_y
    for i in xrange(mid, bmheight):
        ii = i
        io = i + 2
        Lx, Ly = io * LMdx + pdx, ii * LMdy + 128
        delta = LMsx * io - 1
        buildLM()

    #Eaters facing south-east & north-east
    eaterSE = pattern('2o$bo$bobo$2b2o!')
    eaterNE = eaterSE(0, 10, flip_y)
    eY = 59
    eaters = (eaterNE(0, eY), eaterSE(0, eY))
    
    #Eater horizontal displacement. Must be odd
    #bmwidth muliplier determines number of copies visible 'outside' printer.
    eX = (bmheight + 1) * LMdx - (copies * bmwidth - 1) * pdx   
    eX = 1 + eX // 2 * 2    #Adjust parity
    all = pattern()
    for i in xrange(bmheight):
        all += eaters[i % (1 + LMsx % 2)](eX, i * LMdy)
    all.put()   

    #Centre view over bitmap output area
    golly.setpos(str(-pdx * bmwidth // 2 + (bmheight - 1) * LMdx), str(Ly//2))
    #golly.setmag(-2)    #Aliasing effects happen at smaller scales than -2 :(
    golly.fit()

def main():
    #Vertical distance between pixels. Maybe get this from user. minimum = 16
    pdy = 16

    #Generate & display a dot matrix printer from current selection
    dmprinter(pdy, copies=1)

    golly.setcursor("Zoom In")
    golly.setalgo("HashLife")
    golly.setbase(2)
    golly.setstep(6)

main()
Thankyou! That is really cool!
Nathaniel wrote:
Eylrid wrote:I can't seem to get it to work for some reason.
When reporting difficulty with scripts, please let us know *what* doesn't work. For example: do you get an error message? If so, what's the message? If not, what happens? Does nothing at all happen? Etc.
Will do. (That one wasn't doing anything at all.)
now getting these error messages :
brand new installation
brand new installation
SSerrorPY.png (18.05 KiB) Viewed 1971 times
"C'est la vie, c'est la guerre." Life is War
.....**
...****
.******
*( ͡° ͜ʖ ͡°)*

Code: Select all

#CXRLE Pos=0,0
x = 26, y = 34, rule = B3/S23
23bo$23bobo$23b2o9$2b3o2$o5bo$o5bo$o5bo2$2b3o5b2o$10b2o3$2b2o$2b2o10$
15b2o$15b2o!

User avatar
yujh
Posts: 2228
Joined: February 27th, 2020, 11:23 pm
Location: 我不觉得我迷路了,我可能在K2-146 b上 (@bibunsekibun)
Contact:

Re: Golly scripts

Post by yujh » January 10th, 2021, 8:14 am

Just provide the current script u r using.
???
Just delete that.

User avatar
otismo
Posts: 658
Joined: August 18th, 2010, 1:41 pm
Location: Florida
Contact:

Re: Golly scripts

Post by otismo » January 10th, 2021, 10:42 am

evidently I have gremlin corrupting my files -

a fresh clean copy works perfectly

please delete - Thank You !
"C'est la vie, c'est la guerre." Life is War
.....**
...****
.******
*( ͡° ͜ʖ ͡°)*

Code: Select all

#CXRLE Pos=0,0
x = 26, y = 34, rule = B3/S23
23bo$23bobo$23b2o9$2b3o2$o5bo$o5bo$o5bo2$2b3o5b2o$10b2o3$2b2o$2b2o10$
15b2o$15b2o!

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » February 12th, 2021, 10:24 pm

Here's a script for generating random rules for 3-State Outer-Totalistic CAs and gathering basic information about them:

Code: Select all

import random
import string
import os
import golly
from distutils import util


#functions
def generate_config():
    configstring = "#Execution Options (options for while Golly is running a rule)\n\t#Search Options\n\t\t#the number of searches to run for a rule\n"
    configstring = configstring + "\t\tsearch_number = " + golly.getstring("Number of soups per rule searched:", "10")
    configstring = configstring + "\n\n\t\t#the number of generations to run a search\n\t\tsearch_length = " + golly.getstring("Number of generations to run each soup:", "100")
    configstring = configstring + "\n\n\t\t#stops searching a rule if its bounding box is expanding at c\n\t\t#searches take much less time if this is enabled\n\t\t#rules for which this activates won't have data beyond their name and the fact they tested positive for this check written in the log file\n\t\t"
    cCheck = bool(int(golly.getstring("Do you want to skip the majority of calculations for rules that expand at c? (1=Y/0=N):", "1")))
    configstring = configstring + "\n\t\tstop_run_at_c = " + str(cCheck)
    if cCheck:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives"
        configstring = configstring + "\n\t\t\tc_check_when = " + golly.getstring("After how many generations should expansion rate be checked?","50")
        configstring = configstring + "\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive"
        configstring = configstring + "\n\t\t\tc_density_check = " + golly.getstring("Above which density do you want c-expanding rules to be filtered out? (input a value between 0 and 1):", "0.1")
    else:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives\n\t\t\tc_check_when = 50\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive\n\t\t\tc_density_check = 0.1"
    configstring = configstring + "\n\n\t\t#otherwise, there is an option to stop running after exceeding a certain population\n\t\t#check is only executed if stop_run_at_c is false\n\t\t#only use this if you don't want to calculate data for exploding rules in general"
    configstring = configstring + "\n\t\tpopulation_stop_check = false"
    configstring = configstring + "\n\n\t\t\tstop_above_population = 15000\n\n#Classification Options\n\t#only starts checking for stability after a specific generation"
    configstring = configstring + "\n\tstart_stability_check_after_gen = 50\n\n\t#classifies a rule as stable if its population has stopped changing by a specific generation\n\t#not affected by start_stability_check_after_gen"
    configstring = configstring + "\n\tstability_change_gen = 50\n\n#Log Options (options for what should be printed to the log)\n\t#writes data collected for stable rules if true"
    configstring = configstring + "\n\twrite_stable_data = true\n\n\t#writes data collected for exploding rules if true\n\twrite_exploding_data = true\n\n\t#writes data for (seemingly) chaotic rules if true\n\twrite_chaotic_data = true\n\n\t#writes the median population for every generation if true\n\twrite_median_population = true\n\n\t#writes the median population change (growth) for every generation if true\n\twrite_median_growth = true\n\n\t#writes the median growth change (acceleration) for every generation if true\n\twrite_median_acceleration = true\n\n\t#writes the median density for every generation if true\n\twrite_median_density = true\n\n\t#writes the summary of the data collected if true\n\twrite_data_summary = true"
    configstring = configstring + "\n\n#visualize rules searched in golly\n#this is much slower than a normal search, so low settings are recommended\nvisualize = false\n\n-\n"

    f = open("rulegenconfig.txt","w")
    f.write(configstring)
    f.close

def generate_rulestring():
    #variables
    rulestring = ""
    possibleValues = ["00","01","02","03","04","05","06","07","08","10","11","12","13","14","15","16","17","20","21","22","23","24","25","26","30","31","32","33","34","35","40","41","42","43","44","50","51","52","53","60","61","62","70","71","80"]
    possibleValuesBirth = ["01","02","03","04","05","06","07","08","10","11","12","13","14","15","16","17","20","21","22","23","24","25","26","30","31","32","33","34","35","40","41","42","43","44","50","51","52","53","60","61","62","70","71","80"]
    oneOrTwo = []   #aB or bB, aTb or aS, bTa or bS
    aB = "BA"
    bB = "/BB"
    aTb = "/TA"
    bTa = "/TB"
    aS = "/SA"
    bS = "/SB"
    isFixedRandChanging = random.randint(0,1)
    fixedRand = random.random()
    loopIndex = 0

    #execution
    if fixedRand < 0.1:
        fixedRand = fixedRand + 0.1
    elif fixedRand > 0.9:
        fixedRand = fixedRand - 0.1
    for x in possibleValuesBirth:
        oneOrTwo.append(random.randint(1,2))
        if random.random() < fixedRand:
            if oneOrTwo[loopIndex] == 1:
                aB = aB + x
            else:
                bB = bB + x
        loopIndex = loopIndex + 1

    if isFixedRandChanging == 1:
        fixedrand = random.random()
        if fixedRand < 0.1:
            fixedRand = fixedRand + 0.1
        elif fixedRand > 0.9:
            fixedRand = fixedRand - 0.1
    oneOrTwo = []
    loopIndex = 0
    for y in possibleValues:
        oneOrTwo.append(random.randint(1,2))
        if random.random() < fixedRand:
            if oneOrTwo[loopIndex] == 1:
                aTb = aTb + y
            else:
                aS = aS + y
        loopIndex = loopIndex + 1

    if isFixedRandChanging == 1:
        fixedrand = random.random()
        if fixedRand < 0.1:
            fixedRand = fixedRand + 0.1
        elif fixedRand > 0.9:
            fixedRand = fixedRand - 0.1
    oneOrTwo = []
    loopIndex = 0
    for z in possibleValues:
        oneOrTwo.append(random.randint(1,2))
        if random.random() < fixedRand:
            if oneOrTwo[loopIndex] == 1:
                bTa = bTa + z
            else:
                bS = bS + z
        loopIndex = loopIndex + 1
        
    rulestring = aB + bB + aTb + bTa + aS + bS
    return rulestring

def generate_rulefile(rulestring,rulefileName):
    #variables
    top = "@RULE "
    header = "\n\n@TABLE\nn_states:3\nneighborhood:Moore\nsymmetries:permute\n\nvar a={0,1,2}\nvar b=a\nvar c=a\nvar d=a\nvar e=a\nvar f=a\nvar g=a\nvar h=a\nvar i=a\n\n"
    body = ""
    aB = rulestring[2:rulestring.find("/BB")]
    bB = rulestring[rulestring.find("/BB")+3:rulestring.find("/TA")]
    aTb = rulestring[rulestring.find("/TA")+3:rulestring.find("/TB")]
    bTa = rulestring[rulestring.find("/TB")+3:rulestring.find("/SA")]
    aS = rulestring[rulestring.find("/SA")+3:rulestring.find("/SB")]
    bS = rulestring[rulestring.find("/SB")+3:]
    rulefile = ""
    sectionList = [aB,bB,aTb,bTa,aS,bS]
    sectionFirstNums = ["0,","0,","1,","2,","1,","2,"]
    sectionLastNums = ["1,","2,","2,","1,","1,","2,"]
    outerIndex = 0
    isOne = True

    for a in sectionList:
        for b in a:
            if isOne:
                body = body + sectionFirstNums[outerIndex] + "1,"*int(b)
                temp = int(b)
                isOne = False
            else:
                body = body + "2,"*int(b) + "0,"*(8 - int(b) - temp) + sectionLastNums[outerIndex] + "\n"
                isOne = True
        outerIndex = outerIndex + 1
    body = body + "a,b,c,d,e,f,g,h,i,0\n"
    
    rulefile = top + rulefileName + header + body
    f = open(golly.getdir("rules") + rulefileName + ".rule","w")
    f.write(rulefile)
    f.close()


def get_rate_of_change_list(initList):
    finalList = []
    i = 0

    while i < len(initList):
        if i == 0:
            finalList.append(0)
            i = i + 1
            continue
        finalList.append(initList[i] - initList[i-1])
        i = i + 1

    return finalList

def get_median_list(initList):
    finalList = []
    y = []

    for x in initList:
        y = x
        y.sort()
        finalList.append(y[int(len(y)/2)])
    return finalList

def c_check(halfBox,presentBox,presentDensity):
    if halfBox[0] + checkWhen <= presentBox[0] or halfBox[1] + checkWhen <= presentBox[1]:
        if presentDensity > checkDensity:
            return True
    return False
#returns a counterclockwise-rotated list; this is so that values in a sublist
#will be from the same generation, and that subsequent sublists will be from
#subsequent generations
def get_rotated_list(alist):
    a = []
    for x in zip(*alist[::1]):
        a.append(list(x))
    return a

#execution

#getting inputs from user
rulesToSearch = int(golly.getstring("How many rules to test:","100"))

if not os.path.isfile("rulegenconfig.txt"):
    generate_config()

cfile = open("rulegenconfig.txt","r")
config = cfile.read()
cfile.close()
tempstring = ""

tempstring = config[config.find("search_number = ")+16:]
searchesPerRule = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("search_length = ")+16:]
gensPerSearch = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stop_run_at_c = ")+16:]
breakAtC = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
if breakAtC:
    breakAtC = 1
else:
    breakAtC = 0

if breakAtC == 1:
    tempstring = config[config.find("c_check_when = ")+15:]
    checkWhen = int(tempstring[:tempstring.find("\n")])
    if checkWhen % 2 != 0:
        checkWhen = checkWhen - 1

    tempstring = config[config.find("c_density_check = ")+18:]
    checkDensity = float(tempstring[:tempstring.find("\n")])

    popLimitCheck = False
    popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch
else:
    checkWhen = gensPerSearch + 2
    checkDensity = 1

    tempstring = config[config.find("population_stop_check = ")+24:]
    popLimitCheck = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
    if popLimitCheck:
        tempstring = config[config.find("stop_above_population = ")+24:]
        popLimit = int(tempstring[:tempstring.find("\n")])
    else:
        popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch

tempstring = config[config.find("start_stability_check_after_gen = ")+34:]
startStabilityCheck = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stability_change_gen = ")+23:]
stabilityChangeGen = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("write_stable_data = ")+20:]
showStableData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_exploding_data = ")+23:]
showExplodingData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_chaotic_data = ")+21:]
showChaoticData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_population = ")+26:]
showPopList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_growth = ")+22:]
showGrowthList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_acceleration = ")+28:]
showAccelerationList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_density = ")+23:]
showDensityList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_data_summary = ")+21:]
showSummary = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("visualize = ")+12:]
visualize = bool(util.strtobool(tempstring[:tempstring.find("\n")]))


for r in range(rulesToSearch):
    #variables - reset for each rule
    rule = generate_rulestring()
    currentSearch = 0

    isStable = True
    isExploding = False
    maybeExploding = False
    isChaotic = False
    explodesAtC = False
    exceedsPopulationLimit = False

    populationList = []
    populationChangeList = []
    growthAccelerationList = []
    densityList = []
    finalBoxList = []
    
    medianPopulationList = []
    medianPopulationChangeList = []
    medianGrowthAccelerationList = []
    medianDensityList = []
    averageFinalBox = []    #[[x,y]]
    
    averageGrowthAcceleration = 0
    medianFinalPopulation = 0
    averageFinalDensity = 0


    #execution
    generate_rulefile(rule,"temp")

    golly.new("h")
    golly.setrule("temp")
    golly.select([20,20,20,20])

    #start of search loop
    while currentSearch < searchesPerRule:
        #variables - reset for each search
        populationListTemp = []
        densityListTemp = []
        finalBox = []
        populationChangeListTemp = []
        growthAccelerationListTemp = []

        currentGeneration = 0

        #execution
        golly.clear(1)
        golly.randfill(50)
        golly.show("Rule: " + str(r + 1) + " " * (30 - len(str(r + 1))) + "Search: " + str(currentSearch + 1))
        if visualize:
            golly.setpos("30","30")
            golly.setmag(1)
            golly.update()

        #start of sim loop
        while currentGeneration < gensPerSearch:
            #special handling for gen 0
            if currentGeneration == 0:
                populationListTemp.append(int(golly.getpop()))
            currentGeneration = currentGeneration + 1
            golly.run(1)
            if visualize:
                golly.update()

            currentPop = int(golly.getpop())
            if len(golly.getrect()) != 0:
                currentBox = [golly.getrect()[2],golly.getrect()[3]]
                currentDensity = currentPop / (currentBox[0] * currentBox[1])

                if currentBox[0] >= 22 or currentBox[1] >= 22:
                    if currentGeneration >= startStabilityCheck:
                        isStable = False
                #<cCheck>
                if currentGeneration == checkWhen / 2:
                    halBox = currentBox
                if currentGeneration == checkWhen:
                    explodesAtC = c_check(halBox,currentBox,currentDensity)
                if explodesAtC:
                    break
                #</cCheck>
            else:
                currentBox = [0,0]
                currentDensity = 0
            if popLimitCheck and currentPop > popLimit:
                exceedsPopulationLimit = True
                break
            
            populationListTemp.append(currentPop)
            densityListTemp.append(currentDensity)
            finalBox = currentBox
        #end of sim loop

        #<cCheck>
        if explodesAtC or exceedsPopulationLimit:
            break
        #</cCheck>

        populationChangeListTemp = get_rate_of_change_list(populationListTemp)
        growthAccelerationListTemp = get_rate_of_change_list(populationChangeListTemp)

        populationList.append(populationListTemp)
        populationChangeList.append(populationChangeListTemp)
        growthAccelerationList.append(growthAccelerationListTemp)
        densityList.append(densityListTemp)
        finalBoxList.append(finalBox)

        currentSearch = currentSearch + 1
    #end of search loop

    if explodesAtC == False and exceedsPopulationLimit == False:
        populationList = get_rotated_list(populationList)
        populationChangeList = get_rotated_list(populationChangeList)
        growthAccelerationList = get_rotated_list(growthAccelerationList)
        densityList = get_rotated_list(densityList)
        finalBoxList = get_rotated_list(finalBoxList)

        medianPopulationList = get_median_list(populationList)
        medianPopulationChangeList = get_median_list(populationChangeList)
        medianGrowthAccelerationList = get_median_list(growthAccelerationList)
        medianDensityList = get_median_list(densityList)
        averageFinalBox.append(sum(finalBoxList[0])/len(finalBoxList[0]))
        averageFinalBox.append(sum(finalBoxList[1])/len(finalBoxList[1]))

        averageGrowthAcceleration = sum(medianGrowthAccelerationList)/len(medianGrowthAccelerationList)
        medianFinalPopulation = medianPopulationList[len(medianPopulationList)-1]
        averageFinalPopulation = sum(populationList[len(populationList)-1])/len(populationList[len(populationList)-1])
        averageFinalDensity = sum(densityList[len(densityList)-1])/len(densityList[len(densityList)-1])

        positive = 0
        negative = 0
        for x in medianPopulationChangeList:
            if x > 0:
                positive = positive + 1
            else:
                negative = negative + 1
        if positive > negative * 1.1:
            isExploding = True
        elif positive > negative:
            maybeExploding = True
        if medianPopulationChangeList[stabilityChangeGen] == 0 and medianPopulationChangeList[stabilityChangeGen+1] == 0 and medianPopulationChangeList[stabilityChangeGen+2] == 0 and medianPopulationChangeList[stabilityChangeGen+3] == 0 and medianPopulationChangeList[stabilityChangeGen+4] == 0:
            isStable = True
        if isExploding == False and isStable == False and maybeExploding == False:
            isChaotic = True

    logstring = rule + "\n"

    if explodesAtC == False and exceedsPopulationLimit == False:
        if showPopList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "Population:   "
            dex = 0
            for x in medianPopulationList:
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showGrowthList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nGrowth:       "
            dex = 0
            for x in medianPopulationChangeList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showAccelerationList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nAcceleration: "
            dex = 0
            for x in medianGrowthAccelerationList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showDensityList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nDensity:      "
            dex = 0
            for x in medianDensityList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %.3f]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1

        if showSummary and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\n[Average Acceleration: %f]" % (averageGrowthAcceleration)
            logstring = logstring + "[Median/Average Final Population: %d/%.2f]\n" % (medianFinalPopulation,averageFinalPopulation)
            logstring = logstring + "[Average Final Bounding Box Size: %.2fx%.2f]" % (averageFinalBox[0],averageFinalBox[1])
            logstring = logstring + "[Average Final Density: %f]\n" % (averageFinalDensity)

        if isExploding:
            logstring = logstring + "[Exploding]"
        elif maybeExploding:
            logstring = logstring + "[Possibly Exploding]"
        elif isStable:
            logstring = logstring + "[Stable]"
        else:
            logstring = logstring + "[Possibly Chaotic]"

    else:
        if explodesAtC:
            logstring = logstring + "[Explodes at c]"
        else:
            logstring = logstring + "[Population Limit Exceeded]"
    
    logstring = logstring + "\n\n"
    if not os.path.isfile("rulelog_detailed.txt"):
        g = open("rulelog_detailed.txt","w")
        g.write("\n")
        g.close()
    g = open("rulelog_detailed.txt","a+")
    g.write(logstring)
    g.close()
    os.remove(golly.getdir("rules") + "temp.rule")

golly.setrule("b3s23")
golly.new("h")
        
Upon running the script for the first time, it will create a config file and a log file. Within the config file will be options for how the script should be run. Rules that are generated will be saved to the log file along with any data collected about them.

I also wrote a rulestring to rulefile converter for 3-State Outer-Totalistic rules that will work for the rules generated by the above script as well as any rule using the notation described here:

Code: Select all

import random
import string
import os
import golly

#variables
rulestring = golly.getstring("Input rulestring:")


top = "@RULE "
header = "\n\n@TABLE\nn_states:3\nneighborhood:Moore\nsymmetries:permute\n\nvar a={0,1,2}\nvar b=a\nvar c=a\nvar d=a\nvar e=a\nvar f=a\nvar g=a\nvar h=a\nvar i=a\n\n"
body = ""
aB = rulestring[2:rulestring.find("/BB")]
bB = rulestring[rulestring.find("/BB")+3:rulestring.find("/TA")]
aTb = rulestring[rulestring.find("/TA")+3:rulestring.find("/TB")]
bTa = rulestring[rulestring.find("/TB")+3:rulestring.find("/SA")]
aS = rulestring[rulestring.find("/SA")+3:rulestring.find("/SB")]
bS = rulestring[rulestring.find("/SB")+3:]

rulefileName = "BA" + aB + "_BB" + bB + "_TA" + aTb + "_TB" + bTa + "_SA" + aS + "_SB" + bS
rulefileName = rulefileName[:150]
rulefile = ""
sectionList = [aB,bB,aTb,bTa,aS,bS]
sectionFirstNums = ["0,","0,","1,","2,","1,","2,"]
sectionLastNums = ["1,","2,","2,","1,","1,","2,"]
outerIndex = 0
isOne = True

for a in sectionList:
    for b in a:
        if isOne:
            body = body + sectionFirstNums[outerIndex] + "1,"*int(b)
            temp = int(b)
            isOne = False
        else:
            body = body + "2,"*int(b) + "0,"*(8 - int(b) - temp) + sectionLastNums[outerIndex] + "\n"
            isOne = True
    outerIndex = outerIndex + 1
body = body + "a,b,c,d,e,f,g,h,i,0\n"
tail = "#" + rulestring

rulefile = top + rulefileName + header + body + tail
f = open(golly.getdir("rules") + rulefileName + ".rule","w")
f.write(rulefile)
f.close()

golly.setrule(rulefileName)
Last edited by Cyclotrons on March 2nd, 2021, 9:53 am, edited 2 times in total.
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

User avatar
yujh
Posts: 2228
Joined: February 27th, 2020, 11:23 pm
Location: 我不觉得我迷路了,我可能在K2-146 b上 (@bibunsekibun)
Contact:

Re: Golly scripts

Post by yujh » February 13th, 2021, 1:06 am

Great! What about int rules?

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » February 13th, 2021, 3:48 pm

yujh wrote:
February 13th, 2021, 1:06 am
Great! What about int rules?
Just modified my script to generate and test INT rules! It will create a separate log file and config file from the 3-State OT rule generator.

Code: Select all

import random
import string
import os
import golly
from distutils import util


#functions
def generate_config():
    configstring = "#Execution Options (options for while Golly is running a rule)\n\t#Search Options\n\t\t#the number of searches to run for a rule\n"
    configstring = configstring + "\t\tsearch_number = " + golly.getstring("Number of soups per rule searched:", "10")
    configstring = configstring + "\n\n\t\t#the number of generations to run a search\n\t\tsearch_length = " + golly.getstring("Number of generations to run each soup:", "100")
    configstring = configstring + "\n\n\t\t#stops searching a rule if its bounding box is expanding at c\n\t\t#searches take much less time if this is enabled\n\t\t#rules for which this activates won't have data beyond their name and the fact they tested positive for this check written in the log file\n\t\t"
    cCheck = bool(int(golly.getstring("Do you want to skip the majority of calculations for rules that expand at c? (1=Y/0=N):", "1")))
    configstring = configstring + "\n\t\tstop_run_at_c = " + str(cCheck)
    if cCheck:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives"
        configstring = configstring + "\n\t\t\tc_check_when = " + golly.getstring("After how many generations should expansion rate be checked?","50")
        configstring = configstring + "\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive"
        configstring = configstring + "\n\t\t\tc_density_check = " + golly.getstring("Above which density do you want c-expanding rules to be filtered out? (input a value between 0 and 1):", "0.1")
    else:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives\n\t\t\tc_check_when = 50\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive\n\t\t\tc_density_check = 0.1"
    configstring = configstring + "\n\n\t\t#otherwise, there is an option to stop running after exceeding a certain population\n\t\t#check is only executed if stop_run_at_c is false\n\t\t#only use this if you don't want to calculate data for exploding rules in general"
    configstring = configstring + "\n\t\tpopulation_stop_check = false"
    configstring = configstring + "\n\n\t\t\tstop_above_population = 15000\n\n#Classification Options\n\t#only starts checking for stability after a specific generation"
    configstring = configstring + "\n\tstart_stability_check_after_gen = 50\n\n\t#classifies a rule as stable if its population has stopped changing by a specific generation\n\t#not affected by start_stability_check_after_gen"
    configstring = configstring + "\n\tstability_change_gen = 50\n\n#Log Options (options for what should be printed to the log)\n\t#writes data collected for stable rules if true"
    configstring = configstring + "\n\twrite_stable_data = true\n\n\t#writes data collected for exploding rules if true\n\twrite_exploding_data = true\n\n\t#writes data for (seemingly) chaotic rules if true\n\twrite_chaotic_data = true\n\n\t#writes the median population for every generation if true\n\twrite_median_population = true\n\n\t#writes the median population change (growth) for every generation if true\n\twrite_median_growth = true\n\n\t#writes the median growth change (acceleration) for every generation if true\n\twrite_median_acceleration = true\n\n\t#writes the median density for every generation if true\n\twrite_median_density = true\n\n\t#writes the summary of the data collected if true\n\twrite_data_summary = true"
    configstring = configstring + "\n\n#visualize rules searched in golly\n#this is much slower than a normal search, so low settings are recommended\nvisualize = false"
    configstring = configstring + "\n\n#does not search B0 rules if true\nexclude_B0 = " + str(bool(int(golly.getstring("Do you want to exclude B0 rules from your search? (1=Y/0=N)","0")))) + "\n\n-|n"

    f = open("rulegenconfig_INT.txt","w")
    f.write(configstring)
    f.close

def generate_rulestring(B0):
    fixedRandBS = random.uniform(0.1,0.9)
    fixedRandNums = random.uniform(0.1,0.9)
    changingFixedRandBS = random.randint(0,1)
    changingFixedRandNums = random.randint(0,1)

    rulestr = ""
    B = "B"
    S = "/S"
    nums = [0,1,2,3,4,5,6,7,8]
    chars = ["c","e","k","a","i","n","y","q","j","r","t","w","z"]
    tempstr = ""
    if B0:
        nums = [1,2,3,4,5,6,7,8]

    for n in nums:
        for c in chars:
            if n == 0 or n == 8:
                break
            if changingFixedRandNums == 1:
                fixedRandNums = random.uniform(0.1,0.9)
            if random.random() > fixedRandNums:
                tempstr = tempstr + c
            if (n == 1 and c == "e") or (n == 2 and c == "n") or (n == 3 and c == "r") or (n == 5 and c == "r") or (n == 6 and c == "n") or (n == 7 and c == "e"):
                break
        if random.random() > fixedRandBS and (len(tempstr) > 0 or n == 0 or n == 8):
            B = B + str(n) + tempstr
        tempstr = ""

    nums = [0,1,2,3,4,5,6,7,8]
    if changingFixedRandBS == 1:
        fixedRandBS = random.uniform(0.1,0.9)
    for n in nums:
        for c in chars:
            if n == 0 or n == 8:
                break
            if changingFixedRandNums == 1:
                fixedRandNums = random.uniform(0.1,0.9)
            if random.random() > fixedRandNums:
                tempstr = tempstr + c
            if (n == 1 and c == "e") or (n == 2 and c == "n") or (n == 3 and c == "r") or (n == 5 and c == "r") or (n == 6 and c == "n") or (n == 7 and c == "e"):
                break
        if random.random() > fixedRandBS and (len(tempstr) > 0 or n == 0 or n == 8):
            S = S + str(n) + tempstr
        tempstr = ""
    rulestr = B + S
    return rulestr
            
def get_rate_of_change_list(initList):
    finalList = []
    i = 0

    while i < len(initList):
        if i == 0:
            finalList.append(0)
            i = i + 1
            continue
        finalList.append(initList[i] - initList[i-1])
        i = i + 1

    return finalList

def get_median_list(initList):
    finalList = []
    y = []

    for x in initList:
        y = x
        y.sort()
        finalList.append(y[int(len(y)/2)])
    return finalList

def c_check(halfBox,presentBox,presentDensity):
    if halfBox[0] + checkWhen <= presentBox[0] or halfBox[1] + checkWhen <= presentBox[1]:
        if presentDensity > checkDensity:
            return True
    return False
#returns a counterclockwise-rotated list; this is so that values in a sublist
#will be from the same generation, and that subsequent sublists will be from
#subsequent generations
def get_rotated_list(alist):
    a = []
    for x in zip(*alist[::1]):
        a.append(list(x))
    return a

#execution

#getting inputs from user
rulesToSearch = int(golly.getstring("How many rules to test:","100"))

if not os.path.isfile("rulegenconfig_INT.txt"):
    generate_config()

cfile = open("rulegenconfig_INT.txt","r")
config = cfile.read()
cfile.close()
tempstring = ""

tempstring = config[config.find("search_number = ")+16:]
searchesPerRule = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("search_length = ")+16:]
gensPerSearch = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stop_run_at_c = ")+16:]
breakAtC = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
if breakAtC:
    breakAtC = 1
else:
    breakAtC = 0

if breakAtC == 1:
    tempstring = config[config.find("c_check_when = ")+15:]
    checkWhen = int(tempstring[:tempstring.find("\n")])
    if checkWhen % 2 != 0:
        checkWhen = checkWhen - 1

    tempstring = config[config.find("c_density_check = ")+18:]
    checkDensity = float(tempstring[:tempstring.find("\n")])

    popLimitCheck = False
    popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch
else:
    checkWhen = gensPerSearch + 2
    checkDensity = 1

    tempstring = config[config.find("population_stop_check = ")+24:]
    popLimitCheck = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
    if popLimitCheck:
        tempstring = config[config.find("stop_above_population = ")+24:]
        popLimit = int(tempstring[:tempstring.find("\n")])
    else:
        popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch

tempstring = config[config.find("start_stability_check_after_gen = ")+34:]
startStabilityCheck = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stability_change_gen = ")+23:]
stabilityChangeGen = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("write_stable_data = ")+20:]
showStableData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_exploding_data = ")+23:]
showExplodingData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_chaotic_data = ")+21:]
showChaoticData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_population = ")+26:]
showPopList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_growth = ")+22:]
showGrowthList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_acceleration = ")+28:]
showAccelerationList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_density = ")+23:]
showDensityList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_data_summary = ")+21:]
showSummary = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("visualize = ")+12:]
visualize = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("exclude_B0 = ")+13:]
excludeB0 = bool(util.strtobool(tempstring[:tempstring.find("\n")]))


for r in range(rulesToSearch):
    #variables - reset for each rule
    rule = generate_rulestring(excludeB0)
    if len(rule) == 3:
        rule = generate_rulestring(excludeB0)
    currentSearch = 0

    isStable = True
    isExploding = False
    maybeExploding = False
    isChaotic = False
    explodesAtC = False
    exceedsPopulationLimit = False

    populationList = []
    populationChangeList = []
    growthAccelerationList = []
    densityList = []
    finalBoxList = []
    
    medianPopulationList = []
    medianPopulationChangeList = []
    medianGrowthAccelerationList = []
    medianDensityList = []
    averageFinalBox = []    #[[x,y]]
    
    averageGrowthAcceleration = 0
    medianFinalPopulation = 0
    averageFinalDensity = 0


    #execution

    golly.new("h")
    golly.setrule(rule)
    golly.select([20,20,20,20])

    #start of search loop
    while currentSearch < searchesPerRule:
        #variables - reset for each search
        populationListTemp = []
        densityListTemp = []
        finalBox = []
        populationChangeListTemp = []
        growthAccelerationListTemp = []

        currentGeneration = 0

        #execution
        golly.clear(1)
        golly.randfill(50)
        golly.show("Rule: " + str(r + 1) + " " * (30 - len(str(r + 1))) + "Search: " + str(currentSearch + 1))
        if visualize:
            golly.setpos("30","30")
            golly.setmag(1)
            golly.update()

        #start of sim loop
        while currentGeneration < gensPerSearch:
            #special handling for gen 0
            if currentGeneration == 0:
                populationListTemp.append(int(golly.getpop()))
            currentGeneration = currentGeneration + 1
            golly.run(1)
            if visualize:
                golly.update()

            currentPop = int(golly.getpop())
            if len(golly.getrect()) != 0:
                currentBox = [golly.getrect()[2],golly.getrect()[3]]
                currentDensity = currentPop / (currentBox[0] * currentBox[1])

                if currentBox[0] >= 22 or currentBox[1] >= 22:
                    if currentGeneration >= startStabilityCheck:
                        isStable = False
                #<cCheck>
                if currentGeneration == checkWhen / 2:
                    halBox = currentBox
                if currentGeneration == checkWhen:
                    explodesAtC = c_check(halBox,currentBox,currentDensity)
                if explodesAtC:
                    break
                #</cCheck>
            else:
                currentBox = [0,0]
                currentDensity = 0
            if popLimitCheck and currentPop > popLimit:
                exceedsPopulationLimit = True
                break
            
            populationListTemp.append(currentPop)
            densityListTemp.append(currentDensity)
            finalBox = currentBox
        #end of sim loop

        #<cCheck>
        if explodesAtC or exceedsPopulationLimit:
            break
        #</cCheck>

        populationChangeListTemp = get_rate_of_change_list(populationListTemp)
        growthAccelerationListTemp = get_rate_of_change_list(populationChangeListTemp)

        populationList.append(populationListTemp)
        populationChangeList.append(populationChangeListTemp)
        growthAccelerationList.append(growthAccelerationListTemp)
        densityList.append(densityListTemp)
        finalBoxList.append(finalBox)

        currentSearch = currentSearch + 1
    #end of search loop

    if explodesAtC == False and exceedsPopulationLimit == False:
        populationList = get_rotated_list(populationList)
        populationChangeList = get_rotated_list(populationChangeList)
        growthAccelerationList = get_rotated_list(growthAccelerationList)
        densityList = get_rotated_list(densityList)
        finalBoxList = get_rotated_list(finalBoxList)

        medianPopulationList = get_median_list(populationList)
        medianPopulationChangeList = get_median_list(populationChangeList)
        medianGrowthAccelerationList = get_median_list(growthAccelerationList)
        medianDensityList = get_median_list(densityList)
        averageFinalBox.append(sum(finalBoxList[0])/len(finalBoxList[0]))
        averageFinalBox.append(sum(finalBoxList[1])/len(finalBoxList[1]))

        averageGrowthAcceleration = sum(medianGrowthAccelerationList)/len(medianGrowthAccelerationList)
        medianFinalPopulation = medianPopulationList[len(medianPopulationList)-1]
        averageFinalPopulation = sum(populationList[len(populationList)-1])/len(populationList[len(populationList)-1])
        averageFinalDensity = sum(densityList[len(densityList)-1])/len(densityList[len(densityList)-1])

        positive = 0
        negative = 0
        for x in medianPopulationChangeList:
            if x > 0:
                positive = positive + 1
            else:
                negative = negative + 1
        if positive > negative * 1.1:
            isExploding = True
        elif positive > negative:
            maybeExploding = True
        if (medianFinalPopulation > gensPerSearch**1.6 or averageFinalPopulation > gensPerSearch**1.6) and golly.getrule()[:2] == "B0":
            isExploding = True
        if medianPopulationChangeList[stabilityChangeGen] == 0 and medianPopulationChangeList[stabilityChangeGen+1] == 0 and medianPopulationChangeList[stabilityChangeGen+2] == 0 and medianPopulationChangeList[stabilityChangeGen+3] == 0 and medianPopulationChangeList[stabilityChangeGen+4] == 0:
            isStable = True
        if isExploding == False and isStable == False and maybeExploding == False:
            isChaotic = True

    logstring = golly.getrule() + "\n"

    if explodesAtC == False and exceedsPopulationLimit == False:
        if showPopList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "Population:   "
            dex = 0
            for x in medianPopulationList:
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showGrowthList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nGrowth:       "
            dex = 0
            for x in medianPopulationChangeList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showAccelerationList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nAcceleration: "
            dex = 0
            for x in medianGrowthAccelerationList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showDensityList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nDensity:      "
            dex = 0
            for x in medianDensityList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %.3f]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1

        if showSummary and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\n[Average Acceleration: %f]" % (averageGrowthAcceleration)
            logstring = logstring + "[Median/Average Final Population: %d/%.2f]\n" % (medianFinalPopulation,averageFinalPopulation)
            logstring = logstring + "[Average Final Bounding Box Size: %.2fx%.2f]" % (averageFinalBox[0],averageFinalBox[1])
            logstring = logstring + "[Average Final Density: %f]\n" % (averageFinalDensity)

        if isExploding:
            logstring = logstring + "[Exploding]"
        elif maybeExploding:
            logstring = logstring + "[Possibly Exploding]"
        elif isStable:
            logstring = logstring + "[Stable]"
        else:
            logstring = logstring + "[Possibly Chaotic]"

    else:
        if explodesAtC:
            logstring = logstring + "[Explodes at c]"
        else:
            logstring = logstring + "[Population Limit Exceeded]"
    
    logstring = logstring + "\n\n"
    if not os.path.isfile("rulelog_detailed_INT.txt"):
        g = open("rulelog_detailed_INT.txt","w")
        g.write("\n")
        g.close()
    g = open("rulelog_detailed_INT.txt","a+")
    g.write(logstring)
    g.close()

golly.setrule("b3s23")
golly.new("h")
        
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » February 25th, 2021, 12:55 pm

Just created a 3-State INT rule generator! The rulestrings in the output file use the notation described here.

Note that these rulestrings can get long. A quick calculation I did indicated that the maximum possible length for a rulestring is over 10,000 characters! Since rulestrings over 1,000 characters in length are common, the log file this creates can get pretty big.

Like with my other rule generators, this also creates a log file and a config file.

Code: Select all

import random
import string
import os
import golly
from distutils import util


ruleStructure = [[0,["",["","",None,"00000000","00000000"]]],[1,["c",["1c","0",None,"00000001","00000002"]],["e",["1e","0",None,"00000010","00000020"]]],[2,["c",["2c","0",None,"00000101","00000202"],["1c","1c",None,"00000102","00000201"]],["e",["2e","0",None,"00001010","00002020"],["1e","1e",None,"00001020","00002010"]],["k",["2k","0",None,"00001001","00002002"],["1e","1c",None,"00001002","00002001"]],["a",["2a","0",None,"00000011","00000022"],["1e","1c",None,"00000012","00000021"]],["i",["2i","0",None,"00100010","00200020"],["1e","1e",None,"00100020","00200010"]],["n",["2n","0",None,"00010001","00020002"],["1c","1c",None,"00010002","00020001"]]],[3,["c",["3c","0",None,"00010101","00020202"],["2n","1c",None,"00010201","00020102"],["2c","1c",None,"00010102","00020201"]],["e",["3e","0",None,"00101010","00202020"],["2i","1c",None,"00102010","00201020"],["2e","1e",None,"00101020","00202010"]],["k",["3k","0",None,"00101001","00202002"],["2e","1c",None,"00101002","00202001"],["2k","1e",None,"00102001","00201002"]],["a",["3a","0",None,"00001110","00002220"],["2e","1c",None,"00001210","00002120"],["2a","1e",None,"00001120","00002210"]],["i",["3i","0",None,"00000111","00000222"],["2a","1c",None,"00000112","00000221"],["2c","1e",None,"00000121","00000212"]],["n",["3n","0",None,"00001101","00002202"],["2a","1c",None,"00001102","00002201"],["2k","1c",None,"00001201","00002102"],["2c","1e",None,"00002101","00001202"]],["y",["3y","0",None,"00100101","00200202"],["2k","1c",None,"00100102","00200201"],["2c","1e",None,"00200101","00100202"]],["q",["3q","0",None,"00010011","00020022"],["2a","1c",None,"00020011","00010022"],["2k","1c",None,"00010012","00020021"],["2n","1e",None,"00010021","00020012"]],["j",["3j","0",None,"00001011","00002022"],["2e","1c",None,"00001012","00002021"],["2a","1e",None,"00002011","00001022"],["2k","1e",None,"00001021","00002012"]],["r",["3r","0",None,"00100011","00200022"],["2i","1c",None,"00100012","00200021"],["2a","1e",None,"00200011","00100022"],["2k","1e",None,"00100021","00200012"]]],[4,["c",["4c","0",None,"01010101","02020202"],["3c","1c",None,"01010102","02020201"],["2c","2c",None,"01010202","02020101"],["2n","2n",None,"01020102","02010201"]],["e",["4e","0",None,"10101010","20202020"],["3e","1e",None,"10101020","20202010"],["2e","2e",None,"10102020","20201010"],["2i","2i",None,"10201020","20102010"]],["k",["4k","0",None,"00101101","00202202"],["3j","1c",None,"00101102","00202201"],["3k","1c",None,"00101201","00202102"],["3y","1e",None,"00102101","00201202"],["3n","1e",None,"00201101","00102202"],["2e","2c",None,"00101202","00202101"],["2k","2k$",None,"00102102","00201201"],["2k","2a",None,"00102201","00201102"]],["a",["4a","0",None,"00001111","00002222"],["3j","1c",None,"00001211","00002122"],["3a","1c",None,"00001112","00002221"],["3n","1e",None,"00001121","00002212"],["3i","1e",None,"00002111","00001222"],["2e","2c",None,"00001212","00002121"],["2a","2a$",None,"00001122","00002211"],["2k","2a",None,"00001221","00002112"]],["i",["4i","0",None,"00110110","00220220"],["3r","1c",None,"00110210","00220120"],["3n","1e",None,"00110120","00220210"],["2i","2c",None,"00120210","00210120"],["2k","2k",None,"00120120","00210210"],["2a","2a",None,"00110220","00220110"]],["n",["4n","0",None,"00010111","00020222"],["3q","1c",None,"00010211","00020122"],["3n","1c",None,"00010112","00020221"],["3i","1c",None,"00020111","00010222"],["3c","1e",None,"00010121","00020212"],["2k","2c",None,"00010212","00020121"],["2n","2a",None,"00010221","00020112"],["2c","2a",None,"00010122","00020211"]],["y",["4y","0",None,"00110101","00220202"],["3q","1c",None,"00110201","00220102"],["3y","1c",None,"00120101","00210202"],["3n","1c",None,"00110102","00220201"],["3c","1e",None,"00210101","00120202"],["2a","2c",None,"00110202","00220101"],["2k","2c",None,"00120201","00210102"],["2k","2n",None,"00120102","00210201"]],["q",["4q","0",None,"00111001","00222002"],["3a","1c",None,"00111002","00222001"],["3k","1c",None,"00121001","00212002"],["3q","1e",None,"00112001","00221002"],["2a","2k",None,"00112002","00221001"],["2e","2n",None,"00121002","00212001"]],["j",["4j","0",None,"00101011","00202022"],["3e","1c",None,"00101012","00202021"],["3r","1e",None,"00102011","00201022"],["3j","1e",None,"00201011","00102022"],["3k","1e",None,"00101021","00202012"],["2k","2e",None,"00102021","00201012"],["2i","2k",None,"00102012","00201021"],["2e","2a",None,"00101022","00202011"]],["r",["4r","0",None,"00101110","00202220"],["3e","1c",None,"00101210","00202120"],["3r","1e",None,"00102110","00201220"],["3j","1e",None,"00101120","00202210"],["3a","1e",None,"00201110","00102220"],["2k","2e",None,"00102120","00201210"],["2i","2a",None,"00102210","00201120"],["2e","2a",None,"00101220","00202110"]],["t",["4t","0",None,"00100111","00200222"],["3r","1c",None,"00100112","00200221"],["3y","1e",None,"00100121","00200212"],["3i","1e",None,"00200111","00100222"],["2i","2c",None,"00100212","00200121"],["2k","2a",None,"00100122","00200211"]],["w",["4w","0",None,"00011011","00022022"],["3j","1c",None,"00011012","00022021"],["3q","1e",None,"00011021","00022012"],["2n","2e",None,"00012021","00021012"],["2k","2k",None,"00012012","00021021"],["2a","2a",None,"00011022","00022011"]],["z",["4z","0",None,"00110011","00220022"],["3r","1c",None,"00110012","00220021"],["3q","1e",None,"00110021","00220012"],["2k","2k",None,"00120021","00210012"],["2a","2a",None,"00110022","00220011"],["2i","2n",None,"00120012","00210021"]]],[5,["c",["5c","0",None,"10101011","20202022"],["4e","1c",None,"10101012","20202021"],["4r","1e",None,"10102011","20201022"],["4j","1e",None,"10101021","20202012"],["3j","2e",None,"10102021","20201012"],["3a","2e",None,"10202011","20101022"],["3k","2e",None,"20101021","10202012"],["3e","2k",None,"10102012","20201021"],["3e","2a",None,"10101022","20202011"],["3r","2i",None,"10201021","20102012"]],["e",["5e","0",None,"01010111","02020222"],["4y","1c",None,"01010112","02020221"],["4n","1c",None,"02010111","01020222"],["4c","1e",None,"01010121","02020212"],["3y","2c",None,"01010212","02020121"],["3n","2c",None,"02010112","01020221"],["3i","2c",None,"02020111","01010222"],["3c","2k",None,"02010121","01020212"],["3c","2a",None,"01010122","02020211"],["3q","2n",None,"01020112","02010221"]],["k",["5k","0",None,"01011011","02022022"],["4w","1c",None,"02011011","01022022"],["4k","1c",None,"01011012","02022021"],["4y","1e",None,"01011021","02022012"],["3j","2c",None,"02021011","01012022"],["3c","2e",None,"01012021","02021012"],["3q","2k",None,"02012011","01021022"],["3y","2k",None,"01012012","02021021"],["3n","2a",None,"01011022","02022011"],["3k","2n",None,"01021012","02012021"]],["a",["5a","0",None,"00011111","00022222"],["4w","1c",None,"00011211","00022122"],["4a","1c",None,"00011112","00022221"],["4n","1e",None,"00011121","00022212"],["3j","2c",None,"00011212","00022121"],["3c","2e",None,"00012121","00021212"],["3n","2k",None,"00012112","00021221"],["3q","2a",None,"00011221","00022112"],["3i","2a",None,"00011122","00022211"],["3a","2n",None,"00021112","00012221"]],["i",["5i","0",None,"00111110","00222220"],["4r","1c",None,"00111210","00222120"],["4i","1e",None,"00112110","00221220"],["4a","1e",None,"00111120","00222210"],["3e","2c",None,"00121210","00212120"],["3n","2e",None,"00112120","00221210"],["3j","2k",None,"00121120","00212210"],["3r","2a",None,"00112210","00221120"],["3a","2a",None,"00111220","00222110"],["3i","2i",None,"00211120","00122210"]],["n",["5n","0",None,"00101111","00202222"],["4r","1c",None,"00101112","00202221"],["4j","1c",None,"00101211","00202122"],["4t","1e",None,"00102111","00201222"],["4a","1e",None,"00201111","00102222"],["4k","1e",None,"00101121","00202212"],["3e","2c",None,"00101212","00202121"],["3y","2e",None,"00102121","00201212"],["3i","2e",None,"00202111","00101222"],["3r","2k",None,"00102112","00201221"],["3j","2k",None,"00201211","00102122"],["3a","2k",None,"00201112","00102221"],["3r","2a",None,"00102211","00201122"],["3j","2a",None,"00101122","00202211"],["3k","2a",None,"00101221","00202112"],["3n","2i",None,"00201121","00102212"]],["y",["5y","0",None,"01101011","02202022"],["4j","1c",None,"01101012","02202021"],["4i","1e",None,"01102011","02201022"],["4k","1e",None,"01101021","02202012"],["3e","2c",None,"02101012","01202021"],["3n","2e",None,"01102021","02201012"],["3r","2k",None,"01102012","02201021"],["3k","2k",None,"01201012","02102021"],["3j","2a",None,"01101022","02202011"],["3y","2i",None,"01201021","02102012"]],["q",["5q","0",None,"00111011","00222022"],["4r","1c",None,"00111012","00222021"],["4j","1c",None,"00121011","00212022"],["4z","1e",None,"00112011","00221022"],["4w","1e",None,"00211011","00122022"],["4q","1e",None,"00111021","00222012"],["3q","2e",1,"00112021","00221012"],["3q","2e",2,"00212011","00121022"],["3r","2k",None,"00112012","00221021"],["3j","2k",None,"00211012","00122021"],["3k","2k",None,"00121021","00212012"],["3r","2a",None,"00122011","00211022"],["3j","2a",None,"00221011","00112022"],["3a","2a",None,"00111022","00222011"],["3q","2i",None,"00211021","00122012"],["3e","2n",None,"00121012","00212021"]],["j",["5j","0",None,"00111101","00222202"],["4q","1c",None,"00111201","00222102"],["4a","1c",None,"00111102","00222201"],["4k","1c",None,"00121101","00212202"],["4y","1e",None,"00112101","00221202"],["4n","1e",None,"00211101","00122202"],["3a","2c",None,"00111202","00222101"],["3k","2c",None,"00121201","00212102"],["3c","2e",None,"00212101","00121202"],["3q","2k",None,"00211201","00122102"],["3n","2k",None,"00112102","00221201"],["3i","2k",None,"00211102","00122201"],["3q","2a",None,"00112201","00221102"],["3y","2a",None,"00122101","00211202"],["3n","2a",None,"00221101","00112202"],["3j","2n",None,"00121102","00212201"]],["r",["5r","0",None,"00110111","00220222"],["4z","1c",None,"00110211","00220122"],["4t","1c",None,"00120111","00210222"],["4i","1c",None,"00110112","00220221"],["4y","1e",None,"00110121","00220212"],["4n","1e",None,"00210111","00120222"],["3r","2c",1,"00110212","00220121"],["3r","2c",2,"00120211","00210122"],["3q","2k",None,"00210211","00120122"],["3y","2k",None,"00120121","00210212"],["3n","2k",None,"00210112","00120221"],["3q","2a",None,"00110221","00220112"],["3n","2a",None,"00110122","00220211"],["3i","2a",None,"00220111","00110222"],["3c","2i",None,"00210121","00120212"],["3r","2n",None,"00120112","00210221"]]],[6,["c",["6c","0",None,"10101111","20202222"],["5c","1c",None,"10101112","20202221"],["5y","1e",None,"10101121","20202212"],["5n","1e",None,"10102111","20201222"],["5i","1e",None,"10201111","20102222"],["4e","2c",None,"10101212","20202121"],["4a","2e",None,"20201111","10102222"],["4k","2e",None,"10102121","20201212"],["4r","2k",None,"10201112","20102221"],["4j","2k",None,"10102112","20201221"],["4r","2a",None,"10102211","20201122"],["4j","2a",None,"10101122","20202211"],["4t","2i",None,"20102111","10201222"],["4i","2i",None,"10201121","20102212"],["3j","3a",None,"10102221","20201112"],["3e","3i",None,"10101222","20202111"],["3e","3n",None,"10102212","20201121"],["3e","3y",None,"10201212","20102121"],["3k","3j",None,"10102122","20201211"],["3r","3r$",None,"10201122","20102211"]],["e",["6e","0",None,"01011111","02022222"],["5j","1c",None,"01011112","02022221"],["5a","1c",None,"02011111","01022222"],["5k","1c",None,"01011211","02022122"],["5e","1e",None,"01011121","02022212"],["4a","2c",None,"02021111","01012222"],["4k","2c",None,"01011212","02022121"],["4c","2e",None,"01012121","02021212"],["4y","2k",None,"01012112","02021221"],["4n","2k",None,"02012111","01021222"],["4y","2a",None,"01011221","02022112"],["4n","2a",None,"01011122","02022211"],["4w","2n",None,"02011211","01022122"],["4q","2n",None,"01021112","02012221"],["3k","3c",None,"01021212","02012121"],["3c","3a",None,"01012221","02021112"],["3n","3i",None,"01011222","02022111"],["3y","3n",None,"01012212","02021121"],["3q","3q$",None,"01021122","02012211"],["3c","3j",None,"01012122","02021211"]],["k",["6k","0",None,"01101111","02202222"],["5q","1c",None,"01101112","02202221"],["5y","1c",None,"01101211","02202122"],["5n","1c",None,"02101111","01202222"],["5r","1e",None,"01102111","02201222"],["5j","1e",None,"01201111","02102222"],["5k","1e",None,"01101121","02202212"],["4r","2c",None,"02101112","01202221"],["4j","2c",None,"01101212","02202121"],["4y","2e",None,"01102121","02201212"],["4n","2e",None,"01202111","02101222"],["4z","2k",None,"01102112","02201221"],["4t","2k",None,"02102111","01201222"],["4q","2k",None,"01201112","02102221"],["4k","2k",1,"01201211","02102122"],["4k","2k",2,"02101121","01202212"],["4w","2a",None,"01101122","02202211"],["4i","2a",None,"01102211","02201122"],["4a","2a",None,"02201111","01102222"],["4k","2a",None,"01101221","02202112"],["4y","2i",None,"01201121","02102212"],["4j","2n",None,"02101211","01202122"],["3c","3e",None,"01202121","02101212"],["3q","3k",None,"01202112","02101221"],["3n","3a",None,"01102221","02201112"],["3j","3i",None,"01101222","02202111"],["3r","3n",None,"01102212","02201121"],["3k","3y",None,"01201212","02102121"],["3r","3q",None,"02102211","01201122"],["3q","3j",None,"01102122","02201211"],["3n","3j",None,"01202211","02101122"],["3y","3r",None,"01201221","02102112"]],["a",["6a","0",None,"00111111","00222222"],["5q","1c",None,"00111211","00222122"],["5n","1c",None,"00121111","00212222"],["5i","1c",None,"00111112","00222221"],["5r","1e",None,"00112111","00221222"],["5j","1e",None,"00111121","00222212"],["5a","1e",None,"00211111","00122222"],["4r","2c",None,"00111212","00222121"],["4j","2c",None,"00121211","00212122"],["4y","2e",None,"00112121","00221212"],["4n","2e",None,"00212111","00121222"],["4w","2k",None,"00211211","00122122"],["4i","2k",None,"00112112","00221221"],["4a","2k",None,"00211112","00122221"],["4k","2k",None,"00121121","00212212"],["4z","2a",None,"00112211","00221122"],["4t","2a",None,"00122111","00211222"],["4q","2a",None,"00111221","00222112"],["4a","2a",1,"00111122","00222211"],["4a","2a",2,"00221111","00112222"],["4n","2i",None,"00211121","00122212"],["4r","2n",None,"00121112","00212221"],["3e","3c",None,"00121212","00212121"],["3q","3a",None,"00112221","00221112"],["3r","3i",None,"00122211","00211122"],["3a","3i",None,"00111222","00222111"],["3r","3n",None,"00112212","00221121"],["3k","3n",None,"00121221","00212112"],["3r","3q",None,"00122112","00211221"],["3j","3q",None,"00121122","00212211"],["3y","3j",None,"00122121","00211212"],["3n","3j",None,"00112122","00221211"]],["i",["6i","0",None,"01110111","02220222"],["5r","1c",None,"01110112","02220221"],["5e","1e",None,"01110121","02220212"],["4t","2c",None,"01110212","02220121"],["4i","2c",None,"01120211","02210122"],["4y","2k",None,"01120121","02210212"],["4n","2a",None,"01110122","02220211"],["4c","2i",None,"01210121","02120212"],["4z","2n",None,"01120112","02210221"],["3r","3c",None,"01120212","02210121"],["3i","3i",None,"01110222","02220111"],["3n","3n",None,"01120221","02210112"],["3y","3y",None,"01210212","02120121"],["3q","3q",None,"01120122","02210211"]],["n",["6n","0",None,"10111011","20222022"],["5c","1c",None,"10111012","20222021"],["5q","1e",None,"10111021","20222012"],["4w","2e",None,"10112021","20221012"],["4q","2e",None,"10212011","20121022"],["4j","2k",None,"10112012","20221021"],["4r","2a",None,"10111022","20222011"],["4z","2i",None,"10211021","20122012"],["4e","2n",None,"10121012","20212021"],["3q","3q",None,"10212012","20121021"],["3a","3a",None,"10222011","20111022"],["3e","3q",None,"10121022","20212011"],["3j","3j",None,"10112022","20221011"],["3r","3r",None,"10211022","20122011"]]],[7,["c",["7c","0",None,"10111111","20222222"],["6n","1c",None,"10111211","20222122"],["6c","1c",None,"10111112","20222221"],["6a","1e",None,"10211111","20122222"],["6k","1e",None,"10111121","20222212"],["5c","2c",None,"10111212","20222121"],["5j","2e",None,"10212111","20121222"],["5a","2e",None,"20211111","10122222"],["5k","2e",None,"10112121","20221212"],["5q","2k",None,"10211211","20122122"],["5y","2k",None,"10112112","20221221"],["5n","2k",None,"10211112","20122221"],["5q","2a",None,"10111221","20222112"],["5n","2a",None,"10111122","20222211"],["5i","2a",None,"10221111","20112222"],["5r","2i",None,"10211121","20122212"],["5c","2n",None,"10121112","20212221"],["4e","3c",None,"10121212","20212121"],["4y","3e",None,"10212121","20121212"],["4n","3e",None,"20212111","10121222"],["4w","3k",None,"20211211","10122122"],["4k","3k",None,"10212112","20121221"],["4w","3a",None,"10112221","20221112"],["4a","3a",None,"10222111","20111222"],["4r","3i",None,"10111222","20222111"],["4r","3n",None,"10221211","20112122"],["4j","3n",None,"10112212","20221121"],["4j","3y",None,"10211212","20122121"],["4r","3q",None,"20121112","10212221"],["4j","3q",None,"10121122","20212211"],["4q","3j",None,"10212211","20121122"],["4a","3j",None,"20221111","10112222"],["4k","3j",None,"10112122","20221211"],["4z","3r",None,"10211221","20122112"],["4t","3r",None,"10211122","20122211"],["4i","3r",None,"10221121","20112212"]],["e",["7e","0",None,"01111111","02222222"],["6a","1c",None,"01111112","02222221"],["6k","1c",None,"01111211","02222122"],["6i","1e",None,"01112111","02221222"],["6e","1e",None,"01111121","02222212"],["5y","2c",None,"01121211","02212122"],["5n","2c",None,"01111212","02222121"],["5i","2c",None,"02111112","01222221"],["5e","2e",None,"01112121","02221212"],["5r","2k",None,"01112112","02221221"],["5j","2k",None,"01211112","02122221"],["5k","2k",None,"01121121","02212212"],["5r","2a",None,"01112211","02221122"],["5j","2a",None,"01111221","02222112"],["5a","2a",None,"01111122","02222211"],["5e","2i",None,"01211121","02122212"],["5q","2n",None,"01121112","02212221"],["4r","3c",None,"02121112","01212221"],["4j","3c",None,"01121212","02212121"],["4c","3e",None,"01212121","02121212"],["4y","3k",None,"01212112","02121221"],["4n","3a",None,"01112221","02221112"],["4i","3i",None,"01122211","02211122"],["4a","3i",None,"01111222","02222111"],["4t","3n",None,"01112212","02221121"],["4a","3n",None,"02211112","01122221"],["4k","3n",None,"01121221","02212112"],["4i","3y",None,"02112112","01221221"],["4k","3y",None,"01211212","02122121"],["4z","3q",None,"01122112","02211221"],["4w","3q",None,"01121122","02212211"],["4q","3q",None,"01221112","02112221"],["4y","3j",None,"01122121","02211212"],["4n","3j",None,"01112122","02221211"],["4y","3r",None,"01211221","02122112"],["4n","3r",None,"02211121","01122212"]]],[8,["",["8","0",None,"11111111","22222222"],["7c","1c",None,"11111112","22222221"],["7e","1e",None,"11111121","22222212"],["6c","2c",None,"11111212","22222121"],["6e","2e",None,"11112121","22221212"],["6k","2k",None,"11112112","22221221"],["6a","2a",None,"11111122","22222211"],["6i","2i",None,"11211121","22122212"],["6n","2n",None,"11121112","22212221"],["5c","3c",None,"11121212","22212121"],["5e","3e",None,"11212121","22121212"],["5k","3k",None,"11212112","22121221"],["5a","3a",None,"11112221","22221112"],["5i","3i",None,"11111222","22222111"],["5n","3n",None,"11112212","22221121"],["5y","3y",None,"11211212","22122121"],["5q","3q",None,"11121122","22212211"],["5j","3j",None,"11112122","22221211"],["5r","3r",None,"11211122","22122211"],["4e","4c",None,"12121212","21212121"],["4k","4k",None,"11212212","22121121"],["4a","4a",None,"11112222","22221111"],["4r","4n",None,"11121222","22212111"],["4y","4j",None,"11212122","22121211"],["4i","4t",None,"11211222","22122111"],["4q","4w",None,"11122122","22211211"],["4z","4z",None,"11221122","22112211"]]]]

#functions
def generate_config():
    configstring = "#Execution Options (options for while Golly is running a rule)\n\t#Search Options\n\t\t#the number of searches to run for a rule\n"
    configstring = configstring + "\t\tsearch_number = " + golly.getstring("Number of soups per rule searched:", "10")
    configstring = configstring + "\n\n\t\t#the number of generations to run a search\n\t\tsearch_length = " + golly.getstring("Number of generations to run each soup:", "100")
    configstring = configstring + "\n\n\t\t#stops searching a rule if its bounding box is expanding at c\n\t\t#searches take much less time if this is enabled\n\t\t#rules for which this activates won't have data beyond their name and the fact they tested positive for this check written in the log file\n\t\t"
    cCheck = bool(int(golly.getstring("Do you want to skip the majority of calculations for rules that expand at c? (1=Y/0=N):", "1")))
    configstring = configstring + "\n\t\tstop_run_at_c = " + str(cCheck)
    if cCheck:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives"
        configstring = configstring + "\n\t\t\tc_check_when = " + golly.getstring("After how many generations should expansion rate be checked?","50")
        configstring = configstring + "\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive"
        configstring = configstring + "\n\t\t\tc_density_check = " + golly.getstring("Above which density do you want c-expanding rules to be filtered out? (input a value between 0 and 1):", "0.1")
    else:
        configstring = configstring + "\n\n\t\t\t#the generation at which to conduct the c check\n\t\t\t#higher values save less time, lower values are more likely to get false positives\n\t\t\tc_check_when = 50\n\n\t\t\t#an additional parameter needed for a c check to return positive\n\t\t\t#if the population density (calc: population/bounding box area) is greater than this value, it returns positive\n\t\t\tc_density_check = 0.1"
    configstring = configstring + "\n\n\t\t#otherwise, there is an option to stop running after exceeding a certain population\n\t\t#check is only executed if stop_run_at_c is false\n\t\t#only use this if you don't want to calculate data for exploding rules in general"
    configstring = configstring + "\n\t\tpopulation_stop_check = false"
    configstring = configstring + "\n\n\t\t\tstop_above_population = 15000\n\n#Classification Options\n\t#only starts checking for stability after a specific generation"
    configstring = configstring + "\n\tstart_stability_check_after_gen = 50\n\n\t#classifies a rule as stable if its population has stopped changing by a specific generation\n\t#not affected by start_stability_check_after_gen"
    configstring = configstring + "\n\tstability_change_gen = 50\n\n#Log Options (options for what should be printed to the log)\n\t#writes data collected for stable rules if true"
    configstring = configstring + "\n\twrite_stable_data = true\n\n\t#writes data collected for exploding rules if true\n\twrite_exploding_data = true\n\n\t#writes data for (seemingly) chaotic rules if true\n\twrite_chaotic_data = true\n\n\t#writes the median population for every generation if true\n\twrite_median_population = true\n\n\t#writes the median population change (growth) for every generation if true\n\twrite_median_growth = true\n\n\t#writes the median growth change (acceleration) for every generation if true\n\twrite_median_acceleration = true\n\n\t#writes the median density for every generation if true\n\twrite_median_density = true\n\n\t#writes the summary of the data collected if true\n\twrite_data_summary = true"
    configstring = configstring + "\n\n#visualize rules searched in golly\n#this is much slower than a normal search, so low settings are recommended\nvisualize = false\n\n-\n"

    f = open("rulegenconfig_3StateINT.txt","w")
    f.write(configstring)
    f.close

def generate_rulestring():
    ruleStruct = ruleStructure
    changingFixedRandNums = random.randint(0,1)         #changes every transition
    changingFixedRandConfigs = random.randint(0,1)      #changes every num
    changingFixedRandSubconfigs = random.randint(0,1)   #changes every config
    fixedRandNums = random.uniform(0.1,0.9)
    fixedRandNums2 = random.uniform(0.1,0.9)
    fixedRandConfigs = random.uniform(0.1,0.9)
    fixedRandSubconfigs = random.uniform(0.1,0.9)
    
    rulestring = ""
    transitions = ["BA","/BB","/TA","/TB","/SA","/SB"]
    templist = []

    for numbers in ruleStruct:
        if random.random() < fixedRandNums:
            transitions[0] = transitions[0] + str(numbers[0])
            transitions[1] = transitions[1] + str(numbers[0])

            for configs in numbers:
                if isinstance(configs,int):
                    continue
                if random.random() < fixedRandConfigs:
                    transitions[0] = transitions[0] + configs[0]
                    transitions[1] = transitions[1] + configs[0]

                    for subconfigs in configs:
                        if isinstance(subconfigs,str):
                            continue

                        if random.random() < fixedRandSubconfigs:
                            if random.random() < fixedRandNums2:
                                transitions[0] = transitions[0] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[0] = transitions[0] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[1] = transitions[1] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[1] = transitions[1] + "[" + str(subconfigs[2]) + "]"
                        if random.random() < fixedRandSubconfigs and subconfigs[0] != subconfigs[1]:
                            if random.random() < fixedRandNums2:
                                transitions[0] = transitions[0] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[0] = transitions[0] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[1] = transitions[1] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[1] = transitions[1] + "[" + str(subconfigs[2]) + "]"

                    if changingFixedRandSubconfigs == 1:
                        fixedRandSubconfigs = random.uniform(0.1,0.9)

            if changingFixedRandConfigs == 1:
                fixedRandConfigs = random.uniform(0.1,0.9)

    if changingFixedRandNums == 1:
        fixedRandNums = random.uniform(0.1,0.9)
        fixedRandNums2 = random.uniform(0.1,0.9)


    for numbers in ruleStruct:
        if random.random() < fixedRandNums:
            transitions[2] = transitions[2] + str(numbers[0])
            transitions[4] = transitions[4] + str(numbers[0])

            for configs in numbers:
                if isinstance(configs,int):
                    continue
                if random.random() < fixedRandConfigs:
                    transitions[2] = transitions[2] + configs[0]
                    transitions[4] = transitions[4] + configs[0]

                    for subconfigs in configs:
                        if isinstance(subconfigs,str):
                            continue

                        if random.random() < fixedRandSubconfigs:
                            if random.random() < fixedRandNums2:
                                transitions[2] = transitions[2] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[2] = transitions[2] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[4] = transitions[4] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[4] = transitions[4] + "[" + str(subconfigs[2]) + "]"
                        if random.random() < fixedRandSubconfigs and subconfigs[0] != subconfigs[1]:
                            if random.random() < fixedRandNums2:
                                transitions[2] = transitions[2] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[2] = transitions[2] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[4] = transitions[4] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[4] = transitions[4] + "[" + str(subconfigs[2]) + "]"

                    if changingFixedRandSubconfigs == 1:
                        fixedRandSubconfigs = random.uniform(0.1,0.9)

            if changingFixedRandConfigs == 1:
                fixedRandConfigs = random.uniform(0.1,0.9)

    if changingFixedRandNums == 1:
        fixedRandNums = random.uniform(0.1,0.9)
        fixedRandNums2 = random.uniform(0.1,0.9)


    for numbers in ruleStruct:
        if random.random() < fixedRandNums:
            transitions[3] = transitions[3] + str(numbers[0])
            transitions[5] = transitions[5] + str(numbers[0])

            for configs in numbers:
                if isinstance(configs,int):
                    continue
                if random.random() < fixedRandConfigs:
                    transitions[3] = transitions[3] + configs[0]
                    transitions[5] = transitions[5] + configs[0]

                    for subconfigs in configs:
                        if isinstance(subconfigs,str):
                            continue

                        if random.random() < fixedRandSubconfigs:
                            if random.random() < fixedRandNums2:
                                transitions[3] = transitions[3] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[3] = transitions[3] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[5] = transitions[5] + "(" + subconfigs[0] + subconfigs[1] + ")"
                                if subconfigs[2] != None:
                                    transitions[5] = transitions[5] + "[" + str(subconfigs[2]) + "]"
                        if random.random() < fixedRandSubconfigs and subconfigs[0] != subconfigs[1]:
                            if random.random() < fixedRandNums2:
                                transitions[3] = transitions[3] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[3] = transitions[3] + "[" + str(subconfigs[2]) + "]"
                            else:
                                transitions[5] = transitions[5] + "(" + subconfigs[1] + subconfigs[0] + ")"
                                if subconfigs[2] != None:
                                    transitions[5] = transitions[5] + "[" + str(subconfigs[2]) + "]"

                    if changingFixedRandSubconfigs == 1:
                        fixedRandSubconfigs = random.uniform(0.1,0.9)

            if changingFixedRandConfigs == 1:
                fixedRandConfigs = random.uniform(0.1,0.9)

    if changingFixedRandNums == 1:
        fixedRandNums = random.uniform(0.1,0.9)
        fixedRandNums2 = random.uniform(0.1,0.9)


    if transitions[0][:3] == "BA0":
        transitions[0] = transitions[0][:2] + transitions[0][3:]
    if transitions[0][:4] == "BA()":
        transitions[0] = transitions[0][:2] + transitions[0][4:]
    if transitions[1][:4] == "/BB0":
        transitions[1] = transitions[1][:3] + transitions[1][4:]
    if transitions[1][:5] == "/BB()":
        transitions[1] = transitions[1][:3] + transitions[1][5:]
    if transitions[2][:6] == "/TA0()":
        transitions[2] = transitions[2][:4] + transitions[2][6:]
    if transitions[2][:5] == "/TA()":
        transitions[2] = transitions[2][:3] + transitions[2][5:]
    if transitions[3][:6] == "/TB0()":
        transitions[3] = transitions[3][:4] + transitions[3][6:]
    if transitions[3][:5] == "/TB()":
        transitions[3] = transitions[3][:3] + transitions[3][5:]
    if transitions[4][:6] == "/SA0()":
        transitions[4] = transitions[4][:4] + transitions[4][6:]
    if transitions[4][:5] == "/SA()":
        transitions[4] = transitions[4][:3] + transitions[4][5:]
    if transitions[5][:6] == "/SB0()":
        transitions[5] = transitions[5][:4] + transitions[5][6:]
    if transitions[5][:5] == "/SB()":
        transitions[5] = transitions[5][:3] + transitions[5][5:]
    

    charlist = ["c","e","k","a","i","n","y","q","j","r","t","w","z"]
    numlist = ["1","2","3","4","5","6","7","8"]
    index = 0
    charIndex  = 0
    includeChar = True
    tempStr = ""

    for t in transitions:
        for c in t:
            for d in charlist:
                if c == d:
                    if charIndex + 1 >= len(t) and t[charIndex - 2] != "(":
                        includeChar = False
                    elif charIndex + 2 >= len(t) and t[charIndex - 2] != "(" and t[charIndex + 1] != "(" and t[charIndex + 1] != "$" and  t[charIndex + 1] != ")":
                        includeChar = False
                    elif charIndex + 3 >= len(t) and t[charIndex - 2] != "(" and t[charIndex + 1] != "(" and t[charIndex + 1] != "$" and t[charIndex + 1] != ")" and t[charIndex + 2] != ")":
                        includeChar = False
                    elif t[charIndex + 1] != "(" and t[charIndex - 2] != "(" and t[charIndex + 1] != "$" and t[charIndex + 1] != ")" and t[charIndex + 2] != ")" and t[charIndex + 3] != ")":
                        includeChar = False
            if includeChar:
                tempStr = tempStr + c
            includeChar = True
            charIndex = charIndex + 1
        transitions[index] = tempStr
        tempStr = ""
        charIndex = 0
        index = index + 1
    index = 0

    for t in transitions:
        for c in t:
            for d in numlist:
                if c == d:
                    if charIndex + 1 >= len(t) and t[charIndex - 1] != "(":
                        includeChar = False
                    elif charIndex + 2 >= len(t) and t[charIndex - 1] != "(" and t[charIndex + 1] != "(" and t[charIndex + 1] != ")" and t[charIndex + 1:charIndex + 2] != "]":
                        includeChar = False
                    elif t[charIndex + 1:charIndex + 2] != "(" and t[charIndex + 2:charIndex + 3] != "(" and t[charIndex + 1:charIndex + 2] != ")" and t[charIndex + 1:charIndex + 2] != "]" and t[charIndex + 2:charIndex + 3] != ")" and t[charIndex - 1:charIndex] != "(" and t[charIndex + 2:charIndex + 3] != "$":
                        includeChar = False
            if includeChar and t[charIndex:charIndex+2] != "78":
                tempStr = tempStr + c
            includeChar = True
            charIndex = charIndex + 1
        transitions[index] = tempStr
        tempStr = ""
        charIndex = 0
        index = index + 1


    
    rulestring = transitions[0] + transitions[1] + transitions[2] + transitions[3] + transitions[4] + transitions[5]
    return rulestring

def generate_rulefile(rulestring,filename):
    ruleStruct = ruleStructure
    top = "@RULE " + filename
    header = "\n\n@TABLE\nn_states:3\nneighborhood:Moore\nsymmetries:rotate4reflect\n\nvar a={0,1,2}\nvar ab={0,1,2}\nvar ac={0,1,2}\nvar ad={0,1,2}\nvar ae={0,1,2}\nvar af={0,1,2}\nvar ag={0,1,2}\nvar ah={0,1,2}\nvar ai={0,1,2}\n"
    body = "\n"
    tail = rulestring
    
    currentNum = ""
    currentConfig = ""
    currentSubconfig = ""
    currentVariant = None
    currentPos = 0
    currentBeginEnd = [0,1]
    isInSubconfig = False
    getFullNumList = False
    getFullConfigList = False

    charlist = ["c","e","k","a","i","n","y","q","j","r","t","w","z"]
    
    for c in rulestring:
        if c == "(" or c == "[":
            isInSubconfig = True
        if c == ")" or c == "]":
            isInSubconfig = False
        if isInSubconfig and c != "(":
            currentPos = currentPos + 1
            continue
        #note: am planning on detecting subconfig on "(" and getting the
        #numstring for it on ")"

        if c == "A" or c == "B" or c == "T" or c == "S":
            currentPos = currentPos + 1
            continue
        
        for n in [0,1,2,3,4,5,6,7,8]:
            if c == str(n):
                currentNum = n
                if currentPos + 1 < len(rulestring):
                    if rulestring[currentPos+1] == str(n+1) or rulestring[currentPos+1] == "/":
                        getFullNumList = True
                    else:
                        getFullNumList = False
                else:
                    getFullNumList = True
                
        for k in charlist:
            if c == k:
                currentConfig = c
                if currentPos + 1 < len(rulestring):
                    if rulestring[currentPos+1] != "(":
                        getFullConfigList = True
                    else:
                        getFullConfigList = False
                else:
                    getFullConfigList = True

        if c == "(":
            currentSubconfig = rulestring[currentPos+1:rulestring.find(")",currentPos)]
            if rulestring[rulestring.find(")",currentPos)+1:rulestring.find(")",currentPos)+2] == "[":
                currentVariant = int(rulestring[rulestring.find("[",currentPos)+1:rulestring.find("[",currentPos)+2])
            else:
                currentVariant = None

        if c == "/":
            if rulestring[currentPos:currentPos+3] == "/BB":
                currentBeginEnd = [0,2]
            if rulestring[currentPos:currentPos+3] == "/TA":
                currentBeginEnd = [1,2]
            if rulestring[currentPos:currentPos+3] == "/TB":
                currentBeginEnd = [2,1]
            if rulestring[currentPos:currentPos+3] == "/SA":
                currentBeginEnd = [1,1]
            if rulestring[currentPos:currentPos+3] == "/SB":
                currentBeginEnd = [2,2]
        
        if getFullNumList:
            for numbers in ruleStruct:
                if numbers[0] == currentNum:
                    for configs in numbers:
                        if isinstance(configs,int):
                            continue
                        for subconfigs in configs:
                            if isinstance(subconfigs,str):
                                continue

                            body = body + str(currentBeginEnd[0]) + ","
                            for ch in subconfigs[3]:
                                body = body + ch + ","
                            body = body + str(currentBeginEnd[1]) + "\n"
                            if subconfigs[0] != subconfigs[1]:
                                body = body + str(currentBeginEnd[0]) + ","
                                for ch in subconfigs[4]:
                                    body = body + ch + ","
                                body = body + str(currentBeginEnd[1]) + "\n"
        if getFullConfigList:
            for numbers in ruleStruct:
                if numbers[0] == currentNum:
                    for configs in numbers:
                        if isinstance(configs,int):
                            continue
                        if configs[0] == currentConfig:
                            for subconfigs in configs:
                                if isinstance(subconfigs,str):
                                    continue

                                body = body + str(currentBeginEnd[0]) + ","
                                for ch in subconfigs[3]:
                                    body = body + ch + ","
                                body = body + str(currentBeginEnd[1]) + "\n"
                                if subconfigs[0] != subconfigs[1]:
                                    body = body + str(currentBeginEnd[0]) + ","
                                    for ch in subconfigs[4]:
                                        body = body + ch + ","
                                    body = body + str(currentBeginEnd[1]) + "\n"

        if c == ")":
            for numbers in ruleStruct:
                if numbers[0] == currentNum:
                    for configs in numbers:
                        if isinstance(configs,int):
                            continue
                        if configs[0] == currentConfig:
                            for subconfigs in configs:
                                if isinstance(subconfigs,str):
                                    continue
                                if currentSubconfig == subconfigs[0] + subconfigs[1] and currentVariant == subconfigs[2]:
                                    body = body + str(currentBeginEnd[0]) + ","
                                    for ch in subconfigs[3]:
                                        body = body + ch + ","
                                    body = body + str(currentBeginEnd[1]) + "\n"
                                elif currentSubconfig == subconfigs[1] + subconfigs[0] and currentVariant == subconfigs[2]:
                                    body = body + str(currentBeginEnd[0]) + ","
                                    for ch in subconfigs[4]:
                                        body = body + ch + ","
                                    body = body + str(currentBeginEnd[1]) + "\n"
        

        getFullNumList = False
        getFullConfigList = False
        
        currentPos = currentPos + 1

    body = body + "a,ab,ac,ad,ae,af,ag,ah,ai,0\n"
    numlines = len(rulestring) // 999 + 1
    filestring = top + header + body
    for x in range(numlines):
        filestring = filestring + "#" + tail[900*x:900*(x+1)] +"\n"

    f = open(golly.getdir("rules") + filename + ".rule","w")
    f.write(filestring)
    f.close()


def get_rate_of_change_list(initList):
    finalList = []
    i = 0

    while i < len(initList):
        if i == 0:
            finalList.append(0)
            i = i + 1
            continue
        finalList.append(initList[i] - initList[i-1])
        i = i + 1

    return finalList

def get_median_list(initList):
    finalList = []
    y = []

    for x in initList:
        y = x
        y.sort()
        finalList.append(y[int(len(y)/2)])
    return finalList

def c_check(halfBox,presentBox,presentDensity):
    if halfBox[0] + checkWhen <= presentBox[0] or halfBox[1] + checkWhen <= presentBox[1]:
        if presentDensity > checkDensity:
            return True
    return False
#returns a counterclockwise-rotated list; this is so that values in a sublist
#will be from the same generation, and that subsequent sublists will be from
#subsequent generations
def get_rotated_list(alist):
    a = []
    for x in zip(*alist[::1]):
        a.append(list(x))
    return a

#execution

#getting inputs from user
rulesToSearch = int(golly.getstring("How many rules to test:","100"))

if not os.path.isfile("rulegenconfig_3StateINT.txt"):
    generate_config()

cfile = open("rulegenconfig_3StateINT.txt","r")
config = cfile.read()
cfile.close()
tempstring = ""

tempstring = config[config.find("search_number = ")+16:]
searchesPerRule = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("search_length = ")+16:]
gensPerSearch = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stop_run_at_c = ")+16:]
breakAtC = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
if breakAtC:
    breakAtC = 1
else:
    breakAtC = 0

if breakAtC == 1:
    tempstring = config[config.find("c_check_when = ")+15:]
    checkWhen = int(tempstring[:tempstring.find("\n")])
    if checkWhen % 2 != 0:
        checkWhen = checkWhen - 1

    tempstring = config[config.find("c_density_check = ")+18:]
    checkDensity = float(tempstring[:tempstring.find("\n")])

    popLimitCheck = False
    popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch
else:
    checkWhen = gensPerSearch + 2
    checkDensity = 1

    tempstring = config[config.find("population_stop_check = ")+24:]
    popLimitCheck = bool(util.strtobool(tempstring[:tempstring.find("\n")]))
    if popLimitCheck:
        tempstring = config[config.find("stop_above_population = ")+24:]
        popLimit = int(tempstring[:tempstring.find("\n")])
    else:
        popLimit = 2 * 2 * gensPerSearch * gensPerSearch * gensPerSearch

tempstring = config[config.find("start_stability_check_after_gen = ")+34:]
startStabilityCheck = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("stability_change_gen = ")+23:]
stabilityChangeGen = int(tempstring[:tempstring.find("\n")])

tempstring = config[config.find("write_stable_data = ")+20:]
showStableData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_exploding_data = ")+23:]
showExplodingData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_chaotic_data = ")+21:]
showChaoticData = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_population = ")+26:]
showPopList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_growth = ")+22:]
showGrowthList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_acceleration = ")+28:]
showAccelerationList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_median_density = ")+23:]
showDensityList = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("write_data_summary = ")+21:]
showSummary = bool(util.strtobool(tempstring[:tempstring.find("\n")]))

tempstring = config[config.find("visualize = ")+12:]
visualize = bool(util.strtobool(tempstring[:tempstring.find("\n")]))


for r in range(rulesToSearch):
    #variables - reset for each rule
    rule = generate_rulestring()
    while rule[:6] == "BA/BB/":
        rule = generate_rulestring()
    currentSearch = 0

    isStable = True
    isExploding = False
    maybeExploding = False
    isChaotic = False
    explodesAtC = False
    exceedsPopulationLimit = False

    populationList = []
    populationChangeList = []
    growthAccelerationList = []
    densityList = []
    finalBoxList = []
    
    medianPopulationList = []
    medianPopulationChangeList = []
    medianGrowthAccelerationList = []
    medianDensityList = []
    averageFinalBox = []    #[[x,y]]
    
    averageGrowthAcceleration = 0
    medianFinalPopulation = 0
    averageFinalDensity = 0


    #execution
    generate_rulefile(rule,"temp")

    golly.new("h")
    golly.setrule("temp")
    golly.select([20,20,20,20])

    #start of search loop
    while currentSearch < searchesPerRule:
        #variables - reset for each search
        populationListTemp = []
        densityListTemp = []
        finalBox = []
        populationChangeListTemp = []
        growthAccelerationListTemp = []

        currentGeneration = 0

        #execution
        golly.clear(1)
        golly.randfill(50)
        golly.show("Rule: " + str(r + 1) + " " * (30 - len(str(r + 1))) + "Search: " + str(currentSearch + 1))
        if visualize:
            golly.setpos("30","30")
            golly.setmag(1)
            golly.update()

        #start of sim loop
        while currentGeneration < gensPerSearch:
            #special handling for gen 0
            if currentGeneration == 0:
                populationListTemp.append(int(golly.getpop()))
            currentGeneration = currentGeneration + 1
            golly.run(1)
            if visualize:
                golly.update()

            currentPop = int(golly.getpop())
            if len(golly.getrect()) != 0:
                currentBox = [golly.getrect()[2],golly.getrect()[3]]
                currentDensity = currentPop / (currentBox[0] * currentBox[1])

                if currentBox[0] >= 22 or currentBox[1] >= 22:
                    if currentGeneration >= startStabilityCheck:
                        isStable = False
                #<cCheck>
                if currentGeneration == checkWhen / 2:
                    halBox = currentBox
                if currentGeneration == checkWhen:
                    explodesAtC = c_check(halBox,currentBox,currentDensity)
                if explodesAtC:
                    break
                #</cCheck>
            else:
                currentBox = [0,0]
                currentDensity = 0
            if popLimitCheck and currentPop > popLimit:
                exceedsPopulationLimit = True
                break
            
            populationListTemp.append(currentPop)
            densityListTemp.append(currentDensity)
            finalBox = currentBox
        #end of sim loop

        #<cCheck>
        if explodesAtC or exceedsPopulationLimit:
            break
        #</cCheck>

        populationChangeListTemp = get_rate_of_change_list(populationListTemp)
        growthAccelerationListTemp = get_rate_of_change_list(populationChangeListTemp)

        populationList.append(populationListTemp)
        populationChangeList.append(populationChangeListTemp)
        growthAccelerationList.append(growthAccelerationListTemp)
        densityList.append(densityListTemp)
        finalBoxList.append(finalBox)

        currentSearch = currentSearch + 1
    #end of search loop

    if explodesAtC == False and exceedsPopulationLimit == False:
        populationList = get_rotated_list(populationList)
        populationChangeList = get_rotated_list(populationChangeList)
        growthAccelerationList = get_rotated_list(growthAccelerationList)
        densityList = get_rotated_list(densityList)
        finalBoxList = get_rotated_list(finalBoxList)

        medianPopulationList = get_median_list(populationList)
        medianPopulationChangeList = get_median_list(populationChangeList)
        medianGrowthAccelerationList = get_median_list(growthAccelerationList)
        medianDensityList = get_median_list(densityList)
        averageFinalBox.append(sum(finalBoxList[0])/len(finalBoxList[0]))
        averageFinalBox.append(sum(finalBoxList[1])/len(finalBoxList[1]))

        averageGrowthAcceleration = sum(medianGrowthAccelerationList)/len(medianGrowthAccelerationList)
        medianFinalPopulation = medianPopulationList[len(medianPopulationList)-1]
        averageFinalPopulation = sum(populationList[len(populationList)-1])/len(populationList[len(populationList)-1])
        averageFinalDensity = sum(densityList[len(densityList)-1])/len(densityList[len(densityList)-1])

        positive = 0
        negative = 0
        if medianPopulationChangeList[stabilityChangeGen] == 0 and medianPopulationChangeList[stabilityChangeGen+1] == 0 and medianPopulationChangeList[stabilityChangeGen+2] == 0 and medianPopulationChangeList[stabilityChangeGen+3] == 0 and medianPopulationChangeList[stabilityChangeGen+4] == 0:
            isStable = True
        for x in medianPopulationChangeList:
            if x > 0:
                positive = positive + 1
            else:
                negative = negative + 1
        if positive > negative * 1.1 and isStable == False:
            isExploding = True
        elif positive > negative and isStable == False:
            maybeExploding = True
        if (medianFinalPopulation > gensPerSearch**1.6 or averageFinalPopulation > gensPerSearch**1.6) and golly.getrule()[:2] == "B0":
            isExploding = True
        if isExploding == False and isStable == False and maybeExploding == False:
            isChaotic = True

    logstring = rule + "\n"

    if explodesAtC == False and exceedsPopulationLimit == False:
        if showPopList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "Population:   "
            dex = 0
            for x in medianPopulationList:
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showGrowthList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nGrowth:       "
            dex = 0
            for x in medianPopulationChangeList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showAccelerationList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nAcceleration: "
            dex = 0
            for x in medianGrowthAccelerationList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %d]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1
        if showDensityList and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\nDensity:      "
            dex = 0
            for x in medianDensityList:
                if dex == 0:
                    logstring = logstring + "        " + " " * (6 - len(str(medianPopulationList[0])))
                    dex = dex + 1
                    continue
                logstring = logstring + "[%d: %.3f]" % (dex,x) + " " * (6 - len(str(x)))
                dex = dex + 1

        if showSummary and ((showStableData and isStable) or (showExplodingData and isExploding) or (showChaoticData and isChaotic)):
            logstring = logstring + "\n[Average Acceleration: %f]" % (averageGrowthAcceleration)
            logstring = logstring + "[Median/Average Final Population: %d/%.2f]\n" % (medianFinalPopulation,averageFinalPopulation)
            logstring = logstring + "[Average Final Bounding Box Size: %.2fx%.2f]" % (averageFinalBox[0],averageFinalBox[1])
            logstring = logstring + "[Average Final Density: %f]\n" % (averageFinalDensity)

        if isExploding:
            logstring = logstring + "[Exploding]"
        elif maybeExploding:
            logstring = logstring + "[Possibly Exploding]"
        elif isStable:
            logstring = logstring + "[Stable]"
        else:
            logstring = logstring + "[Possibly Chaotic]"

    else:
        if explodesAtC:
            logstring = logstring + "[Explodes at c]"
        else:
            logstring = logstring + "[Population Limit Exceeded]"
    
    logstring = logstring + "\n\n"

    while logstring.find("\n\n[") != -1:
        logstring = logstring[:logstring.find("\n\n[")] + logstring[logstring.find("\n\n[")+1:]
    if not os.path.isfile("rulelog_detailed_3StateINT.txt"):
        g = open("rulelog_detailed_3StateINT.txt","w")
        g.write("\n")
        g.close()
    g = open("rulelog_detailed_3StateINT.txt","a+")
    g.write(logstring)
    g.close()
    os.remove(golly.getdir("rules") + "temp.rule")

golly.setrule("b3s23")
golly.new("h")
        

I also created a script that will turn a 3-State INT rulestring into a rulefile:

Code: Select all

import string
import os
import golly

rulestring = golly.getstring("Input rulestring:")
longfilename = ""
for c in rulestring:
    if c != "/":
        longfilename = longfilename + c
    else:
        longfilename = longfilename + "_"
filename = longfilename[:150]
ruleStruct = [[0,["",["","",None,"00000000","00000000"]]],[1,["c",["1c","0",None,"00000001","00000002"]],["e",["1e","0",None,"00000010","00000020"]]],[2,["c",["2c","0",None,"00000101","00000202"],["1c","1c",None,"00000102","00000201"]],["e",["2e","0",None,"00001010","00002020"],["1e","1e",None,"00001020","00002010"]],["k",["2k","0",None,"00001001","00002002"],["1e","1c",None,"00001002","00002001"]],["a",["2a","0",None,"00000011","00000022"],["1e","1c",None,"00000012","00000021"]],["i",["2i","0",None,"00100010","00200020"],["1e","1e",None,"00100020","00200010"]],["n",["2n","0",None,"00010001","00020002"],["1c","1c",None,"00010002","00020001"]]],[3,["c",["3c","0",None,"00010101","00020202"],["2n","1c",None,"00010201","00020102"],["2c","1c",None,"00010102","00020201"]],["e",["3e","0",None,"00101010","00202020"],["2i","1c",None,"00102010","00201020"],["2e","1e",None,"00101020","00202010"]],["k",["3k","0",None,"00101001","00202002"],["2e","1c",None,"00101002","00202001"],["2k","1e",None,"00102001","00201002"]],["a",["3a","0",None,"00001110","00002220"],["2e","1c",None,"00001210","00002120"],["2a","1e",None,"00001120","00002210"]],["i",["3i","0",None,"00000111","00000222"],["2a","1c",None,"00000112","00000221"],["2c","1e",None,"00000121","00000212"]],["n",["3n","0",None,"00001101","00002202"],["2a","1c",None,"00001102","00002201"],["2k","1c",None,"00001201","00002102"],["2c","1e",None,"00002101","00001202"]],["y",["3y","0",None,"00100101","00200202"],["2k","1c",None,"00100102","00200201"],["2c","1e",None,"00200101","00100202"]],["q",["3q","0",None,"00010011","00020022"],["2a","1c",None,"00020011","00010022"],["2k","1c",None,"00010012","00020021"],["2n","1e",None,"00010021","00020012"]],["j",["3j","0",None,"00001011","00002022"],["2e","1c",None,"00001012","00002021"],["2a","1e",None,"00002011","00001022"],["2k","1e",None,"00001021","00002012"]],["r",["3r","0",None,"00100011","00200022"],["2i","1c",None,"00100012","00200021"],["2a","1e",None,"00200011","00100022"],["2k","1e",None,"00100021","00200012"]]],[4,["c",["4c","0",None,"01010101","02020202"],["3c","1c",None,"01010102","02020201"],["2c","2c",None,"01010202","02020101"],["2n","2n",None,"01020102","02010201"]],["e",["4e","0",None,"10101010","20202020"],["3e","1e",None,"10101020","20202010"],["2e","2e",None,"10102020","20201010"],["2i","2i",None,"10201020","20102010"]],["k",["4k","0",None,"00101101","00202202"],["3j","1c",None,"00101102","00202201"],["3k","1c",None,"00101201","00202102"],["3y","1e",None,"00102101","00201202"],["3n","1e",None,"00201101","00102202"],["2e","2c",None,"00101202","00202101"],["2k","2k$",None,"00102102","00201201"],["2k","2a",None,"00102201","00201102"]],["a",["4a","0",None,"00001111","00002222"],["3j","1c",None,"00001211","00002122"],["3a","1c",None,"00001112","00002221"],["3n","1e",None,"00001121","00002212"],["3i","1e",None,"00002111","00001222"],["2e","2c",None,"00001212","00002121"],["2a","2a$",None,"00001122","00002211"],["2k","2a",None,"00001221","00002112"]],["i",["4i","0",None,"00110110","00220220"],["3r","1c",None,"00110210","00220120"],["3n","1e",None,"00110120","00220210"],["2i","2c",None,"00120210","00210120"],["2k","2k",None,"00120120","00210210"],["2a","2a",None,"00110220","00220110"]],["n",["4n","0",None,"00010111","00020222"],["3q","1c",None,"00010211","00020122"],["3n","1c",None,"00010112","00020221"],["3i","1c",None,"00020111","00010222"],["3c","1e",None,"00010121","00020212"],["2k","2c",None,"00010212","00020121"],["2n","2a",None,"00010221","00020112"],["2c","2a",None,"00010122","00020211"]],["y",["4y","0",None,"00110101","00220202"],["3q","1c",None,"00110201","00220102"],["3y","1c",None,"00120101","00210202"],["3n","1c",None,"00110102","00220201"],["3c","1e",None,"00210101","00120202"],["2a","2c",None,"00110202","00220101"],["2k","2c",None,"00120201","00210102"],["2k","2n",None,"00120102","00210201"]],["q",["4q","0",None,"00111001","00222002"],["3a","1c",None,"00111002","00222001"],["3k","1c",None,"00121001","00212002"],["3q","1e",None,"00112001","00221002"],["2a","2k",None,"00112002","00221001"],["2e","2n",None,"00121002","00212001"]],["j",["4j","0",None,"00101011","00202022"],["3e","1c",None,"00101012","00202021"],["3r","1e",None,"00102011","00201022"],["3j","1e",None,"00201011","00102022"],["3k","1e",None,"00101021","00202012"],["2k","2e",None,"00102021","00201012"],["2i","2k",None,"00102012","00201021"],["2e","2a",None,"00101022","00202011"]],["r",["4r","0",None,"00101110","00202220"],["3e","1c",None,"00101210","00202120"],["3r","1e",None,"00102110","00201220"],["3j","1e",None,"00101120","00202210"],["3a","1e",None,"00201110","00102220"],["2k","2e",None,"00102120","00201210"],["2i","2a",None,"00102210","00201120"],["2e","2a",None,"00101220","00202110"]],["t",["4t","0",None,"00100111","00200222"],["3r","1c",None,"00100112","00200221"],["3y","1e",None,"00100121","00200212"],["3i","1e",None,"00200111","00100222"],["2i","2c",None,"00100212","00200121"],["2k","2a",None,"00100122","00200211"]],["w",["4w","0",None,"00011011","00022022"],["3j","1c",None,"00011012","00022021"],["3q","1e",None,"00011021","00022012"],["2n","2e",None,"00012021","00021012"],["2k","2k",None,"00012012","00021021"],["2a","2a",None,"00011022","00022011"]],["z",["4z","0",None,"00110011","00220022"],["3r","1c",None,"00110012","00220021"],["3q","1e",None,"00110021","00220012"],["2k","2k",None,"00120021","00210012"],["2a","2a",None,"00110022","00220011"],["2i","2n",None,"00120012","00210021"]]],[5,["c",["5c","0",None,"10101011","20202022"],["4e","1c",None,"10101012","20202021"],["4r","1e",None,"10102011","20201022"],["4j","1e",None,"10101021","20202012"],["3j","2e",None,"10102021","20201012"],["3a","2e",None,"10202011","20101022"],["3k","2e",None,"20101021","10202012"],["3e","2k",None,"10102012","20201021"],["3e","2a",None,"10101022","20202011"],["3r","2i",None,"10201021","20102012"]],["e",["5e","0",None,"01010111","02020222"],["4y","1c",None,"01010112","02020221"],["4n","1c",None,"02010111","01020222"],["4c","1e",None,"01010121","02020212"],["3y","2c",None,"01010212","02020121"],["3n","2c",None,"02010112","01020221"],["3i","2c",None,"02020111","01010222"],["3c","2k",None,"02010121","01020212"],["3c","2a",None,"01010122","02020211"],["3q","2n",None,"01020112","02010221"]],["k",["5k","0",None,"01011011","02022022"],["4w","1c",None,"02011011","01022022"],["4k","1c",None,"01011012","02022021"],["4y","1e",None,"01011021","02022012"],["3j","2c",None,"02021011","01012022"],["3c","2e",None,"01012021","02021012"],["3q","2k",None,"02012011","01021022"],["3y","2k",None,"01012012","02021021"],["3n","2a",None,"01011022","02022011"],["3k","2n",None,"01021012","02012021"]],["a",["5a","0",None,"00011111","00022222"],["4w","1c",None,"00011211","00022122"],["4a","1c",None,"00011112","00022221"],["4n","1e",None,"00011121","00022212"],["3j","2c",None,"00011212","00022121"],["3c","2e",None,"00012121","00021212"],["3n","2k",None,"00012112","00021221"],["3q","2a",None,"00011221","00022112"],["3i","2a",None,"00011122","00022211"],["3a","2n",None,"00021112","00012221"]],["i",["5i","0",None,"00111110","00222220"],["4r","1c",None,"00111210","00222120"],["4i","1e",None,"00112110","00221220"],["4a","1e",None,"00111120","00222210"],["3e","2c",None,"00121210","00212120"],["3n","2e",None,"00112120","00221210"],["3j","2k",None,"00121120","00212210"],["3r","2a",None,"00112210","00221120"],["3a","2a",None,"00111220","00222110"],["3i","2i",None,"00211120","00122210"]],["n",["5n","0",None,"00101111","00202222"],["4r","1c",None,"00101112","00202221"],["4j","1c",None,"00101211","00202122"],["4t","1e",None,"00102111","00201222"],["4a","1e",None,"00201111","00102222"],["4k","1e",None,"00101121","00202212"],["3e","2c",None,"00101212","00202121"],["3y","2e",None,"00102121","00201212"],["3i","2e",None,"00202111","00101222"],["3r","2k",None,"00102112","00201221"],["3j","2k",None,"00201211","00102122"],["3a","2k",None,"00201112","00102221"],["3r","2a",None,"00102211","00201122"],["3j","2a",None,"00101122","00202211"],["3k","2a",None,"00101221","00202112"],["3n","2i",None,"00201121","00102212"]],["y",["5y","0",None,"01101011","02202022"],["4j","1c",None,"01101012","02202021"],["4i","1e",None,"01102011","02201022"],["4k","1e",None,"01101021","02202012"],["3e","2c",None,"02101012","01202021"],["3n","2e",None,"01102021","02201012"],["3r","2k",None,"01102012","02201021"],["3k","2k",None,"01201012","02102021"],["3j","2a",None,"01101022","02202011"],["3y","2i",None,"01201021","02102012"]],["q",["5q","0",None,"00111011","00222022"],["4r","1c",None,"00111012","00222021"],["4j","1c",None,"00121011","00212022"],["4z","1e",None,"00112011","00221022"],["4w","1e",None,"00211011","00122022"],["4q","1e",None,"00111021","00222012"],["3q","2e",1,"00112021","00221012"],["3q","2e",2,"00212011","00121022"],["3r","2k",None,"00112012","00221021"],["3j","2k",None,"00211012","00122021"],["3k","2k",None,"00121021","00212012"],["3r","2a",None,"00122011","00211022"],["3j","2a",None,"00221011","00112022"],["3a","2a",None,"00111022","00222011"],["3q","2i",None,"00211021","00122012"],["3e","2n",None,"00121012","00212021"]],["j",["5j","0",None,"00111101","00222202"],["4q","1c",None,"00111201","00222102"],["4a","1c",None,"00111102","00222201"],["4k","1c",None,"00121101","00212202"],["4y","1e",None,"00112101","00221202"],["4n","1e",None,"00211101","00122202"],["3a","2c",None,"00111202","00222101"],["3k","2c",None,"00121201","00212102"],["3c","2e",None,"00212101","00121202"],["3q","2k",None,"00211201","00122102"],["3n","2k",None,"00112102","00221201"],["3i","2k",None,"00211102","00122201"],["3q","2a",None,"00112201","00221102"],["3y","2a",None,"00122101","00211202"],["3n","2a",None,"00221101","00112202"],["3j","2n",None,"00121102","00212201"]],["r",["5r","0",None,"00110111","00220222"],["4z","1c",None,"00110211","00220122"],["4t","1c",None,"00120111","00210222"],["4i","1c",None,"00110112","00220221"],["4y","1e",None,"00110121","00220212"],["4n","1e",None,"00210111","00120222"],["3r","2c",1,"00110212","00220121"],["3r","2c",2,"00120211","00210122"],["3q","2k",None,"00210211","00120122"],["3y","2k",None,"00120121","00210212"],["3n","2k",None,"00210112","00120221"],["3q","2a",None,"00110221","00220112"],["3n","2a",None,"00110122","00220211"],["3i","2a",None,"00220111","00110222"],["3c","2i",None,"00210121","00120212"],["3r","2n",None,"00120112","00210221"]]],[6,["c",["6c","0",None,"10101111","20202222"],["5c","1c",None,"10101112","20202221"],["5y","1e",None,"10101121","20202212"],["5n","1e",None,"10102111","20201222"],["5i","1e",None,"10201111","20102222"],["4e","2c",None,"10101212","20202121"],["4a","2e",None,"20201111","10102222"],["4k","2e",None,"10102121","20201212"],["4r","2k",None,"10201112","20102221"],["4j","2k",None,"10102112","20201221"],["4r","2a",None,"10102211","20201122"],["4j","2a",None,"10101122","20202211"],["4t","2i",None,"20102111","10201222"],["4i","2i",None,"10201121","20102212"],["3j","3a",None,"10102221","20201112"],["3e","3i",None,"10101222","20202111"],["3e","3n",None,"10102212","20201121"],["3e","3y",None,"10201212","20102121"],["3k","3j",None,"10102122","20201211"],["3r","3r$",None,"10201122","20102211"]],["e",["6e","0",None,"01011111","02022222"],["5j","1c",None,"01011112","02022221"],["5a","1c",None,"02011111","01022222"],["5k","1c",None,"01011211","02022122"],["5e","1e",None,"01011121","02022212"],["4a","2c",None,"02021111","01012222"],["4k","2c",None,"01011212","02022121"],["4c","2e",None,"01012121","02021212"],["4y","2k",None,"01012112","02021221"],["4n","2k",None,"02012111","01021222"],["4y","2a",None,"01011221","02022112"],["4n","2a",None,"01011122","02022211"],["4w","2n",None,"02011211","01022122"],["4q","2n",None,"01021112","02012221"],["3k","3c",None,"01021212","02012121"],["3c","3a",None,"01012221","02021112"],["3n","3i",None,"01011222","02022111"],["3y","3n",None,"01012212","02021121"],["3q","3q$",None,"01021122","02012211"],["3c","3j",None,"01012122","02021211"]],["k",["6k","0",None,"01101111","02202222"],["5q","1c",None,"01101112","02202221"],["5y","1c",None,"01101211","02202122"],["5n","1c",None,"02101111","01202222"],["5r","1e",None,"01102111","02201222"],["5j","1e",None,"01201111","02102222"],["5k","1e",None,"01101121","02202212"],["4r","2c",None,"02101112","01202221"],["4j","2c",None,"01101212","02202121"],["4y","2e",None,"01102121","02201212"],["4n","2e",None,"01202111","02101222"],["4z","2k",None,"01102112","02201221"],["4t","2k",None,"02102111","01201222"],["4q","2k",None,"01201112","02102221"],["4k","2k",1,"01201211","02102122"],["4k","2k",2,"02101121","01202212"],["4w","2a",None,"01101122","02202211"],["4i","2a",None,"01102211","02201122"],["4a","2a",None,"02201111","01102222"],["4k","2a",None,"01101221","02202112"],["4y","2i",None,"01201121","02102212"],["4j","2n",None,"02101211","01202122"],["3c","3e",None,"01202121","02101212"],["3q","3k",None,"01202112","02101221"],["3n","3a",None,"01102221","02201112"],["3j","3i",None,"01101222","02202111"],["3r","3n",None,"01102212","02201121"],["3k","3y",None,"01201212","02102121"],["3r","3q",None,"02102211","01201122"],["3q","3j",None,"01102122","02201211"],["3n","3j",None,"01202211","02101122"],["3y","3r",None,"01201221","02102112"]],["a",["6a","0",None,"00111111","00222222"],["5q","1c",None,"00111211","00222122"],["5n","1c",None,"00121111","00212222"],["5i","1c",None,"00111112","00222221"],["5r","1e",None,"00112111","00221222"],["5j","1e",None,"00111121","00222212"],["5a","1e",None,"00211111","00122222"],["4r","2c",None,"00111212","00222121"],["4j","2c",None,"00121211","00212122"],["4y","2e",None,"00112121","00221212"],["4n","2e",None,"00212111","00121222"],["4w","2k",None,"00211211","00122122"],["4i","2k",None,"00112112","00221221"],["4a","2k",None,"00211112","00122221"],["4k","2k",None,"00121121","00212212"],["4z","2a",None,"00112211","00221122"],["4t","2a",None,"00122111","00211222"],["4q","2a",None,"00111221","00222112"],["4a","2a",1,"00111122","00222211"],["4a","2a",2,"00221111","00112222"],["4n","2i",None,"00211121","00122212"],["4r","2n",None,"00121112","00212221"],["3e","3c",None,"00121212","00212121"],["3q","3a",None,"00112221","00221112"],["3r","3i",None,"00122211","00211122"],["3a","3i",None,"00111222","00222111"],["3r","3n",None,"00112212","00221121"],["3k","3n",None,"00121221","00212112"],["3r","3q",None,"00122112","00211221"],["3j","3q",None,"00121122","00212211"],["3y","3j",None,"00122121","00211212"],["3n","3j",None,"00112122","00221211"]],["i",["6i","0",None,"01110111","02220222"],["5r","1c",None,"01110112","02220221"],["5e","1e",None,"01110121","02220212"],["4t","2c",None,"01110212","02220121"],["4i","2c",None,"01120211","02210122"],["4y","2k",None,"01120121","02210212"],["4n","2a",None,"01110122","02220211"],["4c","2i",None,"01210121","02120212"],["4z","2n",None,"01120112","02210221"],["3r","3c",None,"01120212","02210121"],["3i","3i",None,"01110222","02220111"],["3n","3n",None,"01120221","02210112"],["3y","3y",None,"01210212","02120121"],["3q","3q",None,"01120122","02210211"]],["n",["6n","0",None,"10111011","20222022"],["5c","1c",None,"10111012","20222021"],["5q","1e",None,"10111021","20222012"],["4w","2e",None,"10112021","20221012"],["4q","2e",None,"10212011","20121022"],["4j","2k",None,"10112012","20221021"],["4r","2a",None,"10111022","20222011"],["4z","2i",None,"10211021","20122012"],["4e","2n",None,"10121012","20212021"],["3q","3q",None,"10212012","20121021"],["3a","3a",None,"10222011","20111022"],["3e","3q",None,"10121022","20212011"],["3j","3j",None,"10112022","20221011"],["3r","3r",None,"10211022","20122011"]]],[7,["c",["7c","0",None,"10111111","20222222"],["6n","1c",None,"10111211","20222122"],["6c","1c",None,"10111112","20222221"],["6a","1e",None,"10211111","20122222"],["6k","1e",None,"10111121","20222212"],["5c","2c",None,"10111212","20222121"],["5j","2e",None,"10212111","20121222"],["5a","2e",None,"20211111","10122222"],["5k","2e",None,"10112121","20221212"],["5q","2k",None,"10211211","20122122"],["5y","2k",None,"10112112","20221221"],["5n","2k",None,"10211112","20122221"],["5q","2a",None,"10111221","20222112"],["5n","2a",None,"10111122","20222211"],["5i","2a",None,"10221111","20112222"],["5r","2i",None,"10211121","20122212"],["5c","2n",None,"10121112","20212221"],["4e","3c",None,"10121212","20212121"],["4y","3e",None,"10212121","20121212"],["4n","3e",None,"20212111","10121222"],["4w","3k",None,"20211211","10122122"],["4k","3k",None,"10212112","20121221"],["4w","3a",None,"10112221","20221112"],["4a","3a",None,"10222111","20111222"],["4r","3i",None,"10111222","20222111"],["4r","3n",None,"10221211","20112122"],["4j","3n",None,"10112212","20221121"],["4j","3y",None,"10211212","20122121"],["4r","3q",None,"20121112","10212221"],["4j","3q",None,"10121122","20212211"],["4q","3j",None,"10212211","20121122"],["4a","3j",None,"20221111","10112222"],["4k","3j",None,"10112122","20221211"],["4z","3r",None,"10211221","20122112"],["4t","3r",None,"10211122","20122211"],["4i","3r",None,"10221121","20112212"]],["e",["7e","0",None,"01111111","02222222"],["6a","1c",None,"01111112","02222221"],["6k","1c",None,"01111211","02222122"],["6i","1e",None,"01112111","02221222"],["6e","1e",None,"01111121","02222212"],["5y","2c",None,"01121211","02212122"],["5n","2c",None,"01111212","02222121"],["5i","2c",None,"02111112","01222221"],["5e","2e",None,"01112121","02221212"],["5r","2k",None,"01112112","02221221"],["5j","2k",None,"01211112","02122221"],["5k","2k",None,"01121121","02212212"],["5r","2a",None,"01112211","02221122"],["5j","2a",None,"01111221","02222112"],["5a","2a",None,"01111122","02222211"],["5e","2i",None,"01211121","02122212"],["5q","2n",None,"01121112","02212221"],["4r","3c",None,"02121112","01212221"],["4j","3c",None,"01121212","02212121"],["4c","3e",None,"01212121","02121212"],["4y","3k",None,"01212112","02121221"],["4n","3a",None,"01112221","02221112"],["4i","3i",None,"01122211","02211122"],["4a","3i",None,"01111222","02222111"],["4t","3n",None,"01112212","02221121"],["4a","3n",None,"02211112","01122221"],["4k","3n",None,"01121221","02212112"],["4i","3y",None,"02112112","01221221"],["4k","3y",None,"01211212","02122121"],["4z","3q",None,"01122112","02211221"],["4w","3q",None,"01121122","02212211"],["4q","3q",None,"01221112","02112221"],["4y","3j",None,"01122121","02211212"],["4n","3j",None,"01112122","02221211"],["4y","3r",None,"01211221","02122112"],["4n","3r",None,"02211121","01122212"]]],[8,["",["8","0",None,"11111111","22222222"],["7c","1c",None,"11111112","22222221"],["7e","1e",None,"11111121","22222212"],["6c","2c",None,"11111212","22222121"],["6e","2e",None,"11112121","22221212"],["6k","2k",None,"11112112","22221221"],["6a","2a",None,"11111122","22222211"],["6i","2i",None,"11211121","22122212"],["6n","2n",None,"11121112","22212221"],["5c","3c",None,"11121212","22212121"],["5e","3e",None,"11212121","22121212"],["5k","3k",None,"11212112","22121221"],["5a","3a",None,"11112221","22221112"],["5i","3i",None,"11111222","22222111"],["5n","3n",None,"11112212","22221121"],["5y","3y",None,"11211212","22122121"],["5q","3q",None,"11121122","22212211"],["5j","3j",None,"11112122","22221211"],["5r","3r",None,"11211122","22122211"],["4e","4c",None,"12121212","21212121"],["4k","4k",None,"11212212","22121121"],["4a","4a",None,"11112222","22221111"],["4r","4n",None,"11121222","22212111"],["4y","4j",None,"11212122","22121211"],["4i","4t",None,"11211222","22122111"],["4q","4w",None,"11122122","22211211"],["4z","4z",None,"11221122","22112211"]]]]
top = "@RULE " + filename
header = "\n\n@TABLE\nn_states:3\nneighborhood:Moore\nsymmetries:rotate4reflect\n\nvar a={0,1,2}\nvar ab={0,1,2}\nvar ac={0,1,2}\nvar ad={0,1,2}\nvar ae={0,1,2}\nvar af={0,1,2}\nvar ag={0,1,2}\nvar ah={0,1,2}\nvar ai={0,1,2}\n"
body = "\n"
tail = rulestring

currentNum = ""
currentConfig = ""
currentSubconfig = ""
currentPos = 0
currentBeginEnd = [0,1]
isInSubconfig = False
getFullNumList = False
getFullConfigList = False

charlist = ["c","e","k","a","i","n","y","q","j","r","t","w","z"]

for c in rulestring:
    if c == "(" or c == "[":
        isInSubconfig = True
    if c == ")" or c == "]":
        isInSubconfig = False
    if isInSubconfig and c != "(":
        currentPos = currentPos + 1
        continue
    #note: am planning on detecting subconfig on "(" and getting the
    #numstring for it on ")"

    if c == "A" or c == "B" or c == "T" or c == "S":
        currentPos = currentPos + 1
        continue
    
    for n in [0,1,2,3,4,5,6,7,8]:
        if c == str(n):
            currentNum = n
            if currentPos + 1 < len(rulestring):
                if rulestring[currentPos+1] == str(n+1) or rulestring[currentPos+1] == "/":
                    getFullNumList = True
                else:
                    getFullNumList = False
            else:
                getFullNumList = True
            
    for k in charlist:
        if c == k:
            currentConfig = c
            if currentPos + 1 < len(rulestring):
                if rulestring[currentPos+1] != "(":
                    getFullConfigList = True
                else:
                    getFullConfigList = False
            else:
                getFullConfigList = True

    if c == "(":
        currentSubconfig = rulestring[currentPos+1:rulestring.find(")",currentPos)]
        if rulestring[rulestring.find(")",currentPos)+1:rulestring.find(")",currentPos)+2] == "[":
            currentVariant = int(rulestring[rulestring.find("[",currentPos)+1:rulestring.find("[",currentPos)+2])
        else:
            currentVariant = None

    if c == "/":
        if rulestring[currentPos:currentPos+3] == "/BB":
            currentBeginEnd = [0,2]
        if rulestring[currentPos:currentPos+3] == "/TA":
            currentBeginEnd = [1,2]
        if rulestring[currentPos:currentPos+3] == "/TB":
            currentBeginEnd = [2,1]
        if rulestring[currentPos:currentPos+3] == "/SA":
            currentBeginEnd = [1,1]
        if rulestring[currentPos:currentPos+3] == "/SB":
            currentBeginEnd = [2,2]
    
    if getFullNumList:
        for numbers in ruleStruct:
            if numbers[0] == currentNum:
                for configs in numbers:
                    if isinstance(configs,int):
                        continue
                    for subconfigs in configs:
                        if isinstance(subconfigs,str):
                            continue

                        body = body + str(currentBeginEnd[0]) + ","
                        for ch in subconfigs[3]:
                            body = body + ch + ","
                        body = body + str(currentBeginEnd[1]) + "\n"
                        if subconfigs[0] != subconfigs[1]:
                            body = body + str(currentBeginEnd[0]) + ","
                            for ch in subconfigs[4]:
                                body = body + ch + ","
                            body = body + str(currentBeginEnd[1]) + "\n"
    if getFullConfigList:
        for numbers in ruleStruct:
            if numbers[0] == currentNum:
                for configs in numbers:
                    if isinstance(configs,int):
                        continue
                    if configs[0] == currentConfig:
                        for subconfigs in configs:
                            if isinstance(subconfigs,str):
                                continue

                            body = body + str(currentBeginEnd[0]) + ","
                            for ch in subconfigs[3]:
                                body = body + ch + ","
                            body = body + str(currentBeginEnd[1]) + "\n"
                            if subconfigs[0] != subconfigs[1]:
                                body = body + str(currentBeginEnd[0]) + ","
                                for ch in subconfigs[4]:
                                    body = body + ch + ","
                                body = body + str(currentBeginEnd[1]) + "\n"

    if c == ")":
        for numbers in ruleStruct:
            if numbers[0] == currentNum:
                for configs in numbers:
                    if isinstance(configs,int):
                        continue
                    if configs[0] == currentConfig:
                        for subconfigs in configs:
                            if isinstance(subconfigs,str):
                                continue
                            if currentSubconfig == subconfigs[0] + subconfigs[1]and currentVariant == subconfigs[2]:
                                body = body + str(currentBeginEnd[0]) + ","
                                for ch in subconfigs[3]:
                                    body = body + ch + ","
                                body = body + str(currentBeginEnd[1]) + "\n"
                            elif currentSubconfig == subconfigs[1] + subconfigs[0]and currentVariant == subconfigs[2]:
                                body = body + str(currentBeginEnd[0]) + ","
                                for ch in subconfigs[4]:
                                    body = body + ch + ","
                                body = body + str(currentBeginEnd[1]) + "\n"
    

    getFullNumList = False
    getFullConfigList = False
    
    currentPos = currentPos + 1

body = body + "a,ab,ac,ad,ae,af,ag,ah,ai,0\n"
numlines = len(rulestring) // 999 + 1
filestring = top + header + body
for x in range(numlines):
    filestring = filestring + "#" + tail[900*x:900*(x+1)] +"\n"

f = open(golly.getdir("rules") + filename + ".rule","w")
f.write(filestring)
f.close()

golly.setrule(filename)

Last edited by Cyclotrons on March 2nd, 2021, 9:46 am, edited 2 times in total.
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

GoldfishBert
Posts: 4
Joined: February 12th, 2021, 6:10 am

Re: Golly scripts

Post by GoldfishBert » March 1st, 2021, 8:20 pm

Awesome scripts, I'm having heaps of fun exploring the rulesets they generate! The one for the 3-state isotropic nontotalistic rules gives an error when I'm trying to run it in Golly, though:
Golly error.png
Golly error.png (8.79 KiB) Viewed 1419 times
I'm by no means skilled in using Python so I can't really help in that regard, but do let me know if I can test something :)

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » March 2nd, 2021, 12:10 am

GoldfishBert wrote:
March 1st, 2021, 8:20 pm
Awesome scripts, I'm having heaps of fun exploring the rulesets they generate! The one for the 3-state isotropic nontotalistic rules gives an error when I'm trying to run it in Golly, though:

Golly error.png

I'm by no means skilled in using Python so I can't really help in that regard, but do let me know if I can test something :)
Hmmm... The file definitely should be being written.

Maybe it has something to do with permissions or your antivirus? I don't know much about this, but it may possible that your computer has it set so that new files can't be written by unknown programs? Or it may be that your antivirus is automatically removing it when it's created.
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

User avatar
yujh
Posts: 2228
Joined: February 27th, 2020, 11:23 pm
Location: 我不觉得我迷路了,我可能在K2-146 b上 (@bibunsekibun)
Contact:

Re: Golly scripts

Post by yujh » March 2nd, 2021, 5:47 am

Cyclotrons wrote:
March 2nd, 2021, 12:10 am
GoldfishBert wrote:
March 1st, 2021, 8:20 pm
Awesome scripts, I'm having heaps of fun exploring the rulesets they generate! The one for the 3-state isotropic nontotalistic rules gives an error when I'm trying to run it in Golly, though:

Golly error.png

I'm by no means skilled in using Python so I can't really help in that regard, but do let me know if I can test something :)
Hmmm... The file definitely should be being written.

Maybe it has something to do with permissions or your antivirus? I don't know much about this, but it may possible that your computer has it set so that new files can't be written by unknown programs? Or it may be that your antivirus is automatically removing it when it's created.
I do have that problem(but not for the first time when opening golly)

GoldfishBert
Posts: 4
Joined: February 12th, 2021, 6:10 am

Re: Golly scripts

Post by GoldfishBert » March 2nd, 2021, 6:20 am

Cyclotrons wrote:
March 2nd, 2021, 12:10 am
Hmmm... The file definitely should be being written.

Maybe it has something to do with permissions or your antivirus? I don't know much about this, but it may possible that your computer has it set so that new files can't be written by unknown programs? Or it may be that your antivirus is automatically removing it when it's created.
Tried running the script again while making sure all file permissions are set correctly, but the same error pops up right after it prompts for the number of rules to be tested (regardless of the antivirus settings). I also tried saving the code through both Notepad++ and Python itself (IDLE), but this didn't make a difference. I did notice that I left out some rather important details in my previous post (serves me right for typing out details like this half-asleep :P): this is in Windows 10, running Python 3.9.2 and Golly 4.0.

User avatar
dvgrn
Moderator
Posts: 7987
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Golly scripts

Post by dvgrn » March 2nd, 2021, 8:56 am

GoldfishBert wrote:
March 2nd, 2021, 6:20 am
Tried running the script again while making sure all file permissions are set correctly, but the same error pops up right after it prompts for the number of rules to be tested (regardless of the antivirus settings). I also tried saving the code through both Notepad++ and Python itself (IDLE), but this didn't make a difference. I did notice that I left out some rather important details in my previous post (serves me right for typing out details like this half-asleep :P): this is in Windows 10, running Python 3.9.2 and Golly 4.0.
I just tried putting the script in the same place that GoldfishBert put the script (inside the Python Rule-Generators folder) and running it. I'm getting the exact same error.

The converter script is similar -- oddly enough, there were no errors if I gave it a rulestring that Golly could recognize, like "B3/S23-q", because then it doesn't actually end up using the generated rulestring ... but it fails with a "Given rule is not valid in any algorithm" error if I try putting in something like

BA/BB({1e},)(,3)/TA/TB(,7)/SA/SB(,2)(,3)

The rule files are actually being created with no problem, but no path is specified on line 416 of the generator script, so the rule files end up in the same directory as the script, and Golly doesn't know to look for rules there (unless you save the script inside one of Golly's Rules folders, but a script is not a rule so that's kind of a weird thing to do).

Here's one way to fix the problem: change line 416 to

Code: Select all

    f = open(golly.getdir("rules") + filename + ".rule","w")
Similarly, line 764 becomes

Code: Select all

    os.remove(golly.getdir("rules") + "temp.rule")
Probably there's an unstated assumption in the converter script also, about where the script should be saved. At least, the assumption doesn't seem to be stated here -- I haven't read through the source thread very carefully.

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » March 2nd, 2021, 9:36 am

dvgrn wrote:
March 2nd, 2021, 8:56 am

Code: Select all

    f = open(golly.getdir("rules") + filename + ".rule","w")
Similarly, line 764 becomes

Code: Select all

    os.remove(golly.getdir("rules") + "temp.rule")
Probably there's an unstated assumption in the converter script also, about where the script should be saved. At least, the assumption doesn't seem to be stated here -- I haven't read through the source thread very carefully.
Thank you for the fix! I wish I had known that Golly had a getdir function.

dvgrn wrote:
March 2nd, 2021, 8:56 am

The converter script is similar -- oddly enough, there were no errors if I gave it a rulestring that Golly could recognize, like "B3/S23-q", because then it doesn't actually end up using the generated rulestring ... but it fails with a "Given rule is not valid in any algorithm" error if I try putting in something like

BA/BB({1e},)(,3)/TA/TB(,7)/SA/SB(,2)(,3)
Are you using the rulefile generation script listed here? If you are, just a heads up: the one here is an old version (and will remain so until I update my generator to work with the revision to my notation). The one that works with the revision is included in the post detailing the revision, so use that one.
Last edited by Cyclotrons on March 2nd, 2021, 9:41 am, edited 1 time in total.
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

GoldfishBert
Posts: 4
Joined: February 12th, 2021, 6:10 am

Re: Golly scripts

Post by GoldfishBert » March 2nd, 2021, 10:49 am

These two lines indeed fix it, the script works flawlessly now. Thanks for you both!

Cyclotrons
Posts: 27
Joined: January 26th, 2021, 12:19 am

Re: Golly scripts

Post by Cyclotrons » March 4th, 2021, 9:03 pm

I created an automated rulegolfing script for 2-State INT rules.

It changes the entered rule by 1 transition and then shows you the resulting rules.

The tested rules are saved in a txt file containing the starting rule's rulestring.

Code: Select all

import os
import string
import golly
import random

def mutate(string,numMutations):
    newString = string
    nums = "012345678"
    chars = "cekainyqjrtwz"
    combs = ["0","1c","1e","2c","2e","2k","2a","2i","2n","3c","3e","3k","3a","3i","3n","3y","3q","3j","3r","4c","4e","4k","4a","4i","4n","4y","4q","4j","4r","4t","4w","4z","5c","5e","5k","5a","5i","5n","5y","5q","5j","5r","6c","6e","6k","6a","6i","6n","7c","7e","8"]
    isInB = []
    isInS = []
    mutationType = random.randint(0,2)
    #0 - insertion
    #1 - in-place
    #2 - deletion
    currentPosition = 0
    for x in range(2):
        if x == 0:
            B = string[1:string.find("/")]
            currentNum = 0
            currentChar = ""
            tempNum = -1
            inverted = False
            for c in B:
                if c in nums:
                    currentNum = int(c)
                    if currentNum == 0:
                        isInB.append("0")
                    if currentNum == 8:
                        isInB.append("8")
                    if B[currentPosition+1:currentPosition+2] in nums or B[currentPosition+1:currentPosition+2] == "":
                        for v in combs:
                            if str(currentNum) in v:
                                isInB.append(v)
                if c == "-":
                    tempNum = currentNum
                    tempList = []
                    inverted = True
                if c in chars:
                    currentChar = c
                if tempNum == currentNum and c in chars:
                    tempList.append(str(currentNum) + currentChar)
                elif tempNum != currentNum and inverted == True:
                    inverted = False
                    for cs in combs:
                        if cs[0] in tempList[0] and (cs in tempList) == False:
                            isInB.append(cs)
                else:
                    if c in chars:
                        isInB.append(str(currentNum) + c)
                currentPosition = currentPosition + 1
        if x == 1:
            S = string[string.find("/")+1:]
            currentNum = 0
            currentChar = ""
            tempNum = -1
            inverted = False
            for c in S:
                if c in nums:
                    currentNum = int(c)
                    if currentNum == 0:
                        isInS.append("0")
                    if currentNum == 8:
                        isInS.append("8")
                    if S[currentPosition+1:currentPosition+2] in nums or S[currentPosition+1:currentPosition+2] == "":
                        for v in combs:
                            if str(currentNum) in v:
                                isInS.append(v)
                if c == "-":
                    tempNum = currentNum
                    tempList = []
                    inverted = True
                if c in chars:
                    currentChar = c
                if tempNum == currentNum and c in chars:
                    tempList.append(str(currentNum) + currentChar)
                elif tempNum != currentNum and inverted == True:
                    inverted = False
                    for cs in combs:
                        if cs[0] in tempList[0] and (cs in tempList) == False:
                            isInS.append(cs)
                else:
                    if c in chars:
                        isInS.append(str(currentNum) + c)
                currentPosition = currentPosition + 1
        currentPosition = 0


    for n in range(numMutations):
        if mutationType == 0:
            if random.random() < 0.5:
                transition = combs[random.randint(1,len(combs)-1)]
                while transition in isInB:
                    transition = combs[random.randint(1,len(combs)-1)]
                isInB.append(transition)
            else:
                transition = combs[random.randint(0,len(combs)-1)]
                while transition in isInS:
                    transition = combs[random.randint(0,len(combs)-1)]
                isInS.append(transition)
        if mutationType == 1:
            if random.random() < 0.5:
                currentPos = random.randint(0,len(isInB)-1)
                transition = combs[random.randint(1,len(combs)-1)]
                while transition in isInB:
                    transition = combs[random.randint(1,len(combs)-1)]
                isInB[currentPos] = transition
            else:
                currentPos = random.randint(0,len(isInS)-1)
                transition = combs[random.randint(0,len(combs)-1)]
                while transition in isInS:
                    transition = combs[random.randint(0,len(combs)-1)]
                isInS[currentPos] = transition
        if mutationType == 2:
            if random.random() < 0.5:
                currentPos = random.randint(0,len(isInB)-1)
                tempB = []
                for trs in range(len(isInB)):
                    if trs != currentPos:
                        tempB.append(isInB[trs])
                isInB = tempB
            else:
                currentPos = random.randint(0,len(isInS)-1)
                tempS = []
                for trs in range(len(isInS)):
                    if trs != currentPos:
                        tempS.append(isInS[trs])
                isInS = tempS
        mutationType = random.randint(0,2)

    tempListB = []
    tempListS = []
    for d in combs:
        for e in isInB:
            if d == e:
                tempListB.append(e)
        for f in isInS:
            if d == f:
                tempListS.append(f)
    isInB = tempListB
    isInS = tempListS

    newString = "B"
    currentNum = "-1"
    for b in isInB:
        if currentNum != b[0]:
            newString = newString + b[0]
            currentNum = b[0]
        newString = newString + b[1:2]
    newString = newString + "/S"
    for s in isInS:
        if currentNum != s[0]:
            newString = newString + s[0]
            currentNum = s[0]
        newString = newString + s[1:2]

    return newString

rule = golly.getstring("Input rule:")
currentRule = rule
numOfMutations = 1
golfWithPrevious = golly.getstring("Do you want each rule to be a variant of the input rule(0),\n or do you want it to be a variant of the previous displayed rule(1)?","0")
numGenerations = 500
golly.setpos("75","50")
golly.autoupdate(True)
currentNumRule = 1

while True:
    if golfWithPrevious == "0":
        currentRule = mutate(rule,numOfMutations)
    else:
        currentRule = mutate(currentRule,numOfMutations)
    golly.setrule(currentRule)
    golly.select([0,0,150,100])
    golly.randfill(50)
    golly.clear(1)
    golly.setbase(10)
    golly.setstep(0)
    golly.setgen("0")
    golly.show("Current Rule: " + str(currentNumRule))
    while int(golly.getgen()) < numGenerations:
        if golly.getpop() == "0":
            break
        golly.step()
    currentRule = golly.getrule()
    frule = rule[:rule.find("/")] + "_" + rule[rule.find("/")+1:]
    f = open(frule + "_variants.txt","a+")
    f.write(str(currentNumRule) + ": " + currentRule + "\n")
    f.close()
    currentNumRule = currentNumRule + 1
    
I wrote random rule generators for 3-State Outer-Totalistic rules and for 2-State Isotropic Non-Totalistic rules!

I created a notation for the 3-State Isotropic Non-Totalistic rulespace and wrote a generator for it!

User avatar
goldenratio
Posts: 291
Joined: July 26th, 2020, 10:39 pm

Re: Golly scripts

Post by goldenratio » May 5th, 2021, 3:55 pm

Breaking my inactivity for a bit to post this 1-glider cleanup (for syntheses) script:

Code: Select all

import golly as g
import math

min_interact = 4
max_interact = 120
neglider = ["b2o$obo$2bo!", "2o$b2o$o!", "3o$2bo$bo!", "bo$b2o$obo!"]
nwglider = ["3o$o$bo!", "bo$2o$obo!", "2o$obo$o!", "b2o$2o$2bo!"]
seglider = ["bo$2bo$3o!", "obo$b2o$bo!", "2bo$obo$b2o!", "o$b2o$2o!"]
swglider = ["bo$o$3o!", "obo$2o$bo!", "o$obo$2o!", "2bo$2o$b2o!"]
layerindex = g.getlayer()
objperiod = 0
min_x = 0
min_y = 0
max_x = 0
max_y = 0
popseq = [0] * (max_interact + 1)
pathash = 0
minpop = 2147483647
minpoppat = []
minpopgen = 0
reaction = g.getcells(g.getrect())
selrect = g.getselrect()
blank = False
checkminpop = g.getstring("Check minpop? (only recommended if a 1G cleanup is not likely to exist)", "No")
if checkminpop == "Yes" and not g.getselrect():
    g.exit("No selection!")
g.setlayer(layerindex + 1)
if not g.getrect():
    blank = True
def clear_layer():
    r = g.getrect()
    if r:
        g.select(r)
        g.clear(0)
    return

def findperiod():
    period = 1
    if blank == False:
        inithash = g.hash(g.getrect())
        while True:
            g.run(1)
            period += 1
            if g.hash(g.getrect()) == inithash:
                break
            if period > 500:
                g.exit("Period is too large! Are you sure the next layer is correct?")
    return period

def seqpattern():
    global objperiod, pathash
    try:
        g.setlayer(layerindex + 1)
        objperiod = findperiod()
        g.reset()
        if g.getrect():
            pathash = g.hash(g.getrect())
        g.setlayer(layerindex)
        for i in range(max_interact + 1):
            g.run(1)
            popseq[i] = int(g.getpop())
    except:
        g.exit("No pattern layer provided!")
        
def findrect():
    global min_x, min_y, max_x, max_y
    clear_layer()
    g.putcells(reaction)
    patternrect = g.getrect()
    min_x = patternrect[0]
    min_y = patternrect[1]
    max_x = min_x + patternrect[2]
    max_y = min_y + patternrect[3]
    
def placeglider(direct, phase, coord_x, coord_y):
    clear_layer()
    g.putcells(reaction)
    if direct == "ne":
        gliderlist = g.parse(neglider[phase])
    elif direct == "nw":
        gliderlist = g.parse(nwglider[phase])
    elif direct == "se":
        gliderlist = g.parse(seglider[phase])
    elif direct == "sw":
        gliderlist = g.parse(swglider[phase])
    g.putcells(gliderlist, int(coord_x), int(coord_y))
    
def run():
    global minpop, minpoppat, minpopgen
    badrun = False
    for i in range(min_interact + 1):
        g.run(1)
        if int(g.getpop()) != popseq[i] + 5:
            badrun = True
    if badrun == False:
        badrun = True
        for i in range(max_interact - min_interact + 1):
            g.run(1)
            if int(g.getpop()) != popseq[i] + 5:
                badrun = False
    if badrun == False:
        g.run(1000 + max_interact)
        for i in range(int(objperiod)):
            if g.getrect() and blank == False:
                if g.hash(g.getrect()) == pathash:
                    g.reset()
                    g.exit("1-glider solution found")
            if blank == True:
                if g.empty() == True:
                    g.reset()
                    g.exit("1-glider destruction found")
    g.reset()
    if checkminpop == "Yes" and badrun == False:
        g.select(selrect)
        g.run(min_interact)
        for i in range(500 + max_interact):
            g.run(1)
            if int(g.getpop()) < minpop and g.hash(g.getselrect()) == pathash:
                minpopgen = int(g.getgen())
                minpop = int(g.getpop())
                g.reset()
                minpoppat = g.getcells(g.getrect())
                break
        g.reset()
        event = g.getevent()
        if event.startswith("key q"):
            clear_layer()
            g.putcells(minpoppat)
            g.exit("Minimum population of " + str(minpop) + " occurs at generation " + str(minpopgen) + " of this pattern.")

def enum():
    count = 0
    midpx = math.floor((max_x + min_x)/2)
    midpy = math.floor((max_y + min_y)/2)
    xrange = 2 * math.floor(max_interact/4) + (max_x - min_x)
    yrange = 2 * math.floor(max_interact/4) + (max_y - min_y)
    for i in range(int(xrange)):
        for j in range(int(yrange)):
            for k in range(4):
                xcoord = midpx - math.floor(max_interact/4) - 20 + i
                ycoord = midpy - math.floor(max_interact/4) - 20 + j
                placeglider("ne", k, xcoord, ycoord)
                run()
                placeglider("nw", k, xcoord, ycoord)
                run()
                placeglider("se", k, xcoord, ycoord)
                run()
                placeglider("sw", k, xcoord, ycoord)
                #g.update()
                run()
                count = count + 4
                g.show(str(count) + "/" + str(int(16 * xrange * yrange)) + " collisions tried.")

seqpattern()
findrect()
enum()

if checkminpop == "Yes":
    clear_layer()
    g.putcells(minpoppat)
    g.show("Minimum population of " + str(minpop) + " occurs at generation " + str(minpopgen) + " of this pattern.")
Change min_interact and max_interact if necessary - min_interact probably doesn't need to be changed very much (only reason it's there is to prevent the script from recording a "solution" which is a glider placed in the middle which couldn't have possibly gotten there from infinity), but increasing max_interact makes the script slower, so if you know a cleanup glider isn't going to interact for a couple hundred ticks, you should probably just advance the pattern and run the script on that.
Since obviously the script doesn't know which object you're trying to synthesize, you'll have to create another layer with that object before you run.

EDIT: Updated code to find minimum population (you can choose whether or not to find it, since it's redundant if there exists a 1G cleanup.) Press Q if the script isn't done to immediately display the collision with minimum population. You'll have select where the object you want appears at the end before running if you want to activate this option.
Time to get active again... temporarily disconnecting yourself from the community is a very bad experience, so if you want to leave, please do so permanently.

Help expand or create new tutorials on LifeWiki!

Post Reply