diff --git a/constructs.py b/constructs.py deleted file mode 100644 index c7d5ce7..0000000 --- a/constructs.py +++ /dev/null @@ -1,225 +0,0 @@ -import lifelib -sess = lifelib.load_rules("b3s23") -lt = sess.lifetree(n_layers=1) - -def minpop(pat): - return min(pat[i].population for i in range(pat.period)) - -rect1 = lt.pattern("""8bo$7bobo$8bo2$6b5o$5bo4bo$4bo2bo$bo2bob2o$obobo5bo$bo2bo4bobo$4b2o2bo -2bo$9b2o9$17b2o$17b2o8$7b2o22b2o$8bo21bo2bo$5b3o23b2o$5bo6$14b2o$15bo$12b3o$12bo""") -rect2 = rect1("rot180",52,74) -grect = lt.pattern("bo$2o$obo")(17,23) - -def rectifier_loop(p): - if p%4 == 0 or p < 106: - return None - elif p%4 == 1: - n_gliders, mpop = (2, 116) if p >= 133 else (10, 156) - elif p%4 == 3: - n_gliders, mpop = (6, 136) - elif p%8 == 2: - n_gliders = 1 if p >= 266 else 5 - mpop = 113 if p >= 266 else 133 if p >= 210 else None - else: - n_gliders, mpop = (3, 123 if p >= 206 else None) - exts = (p*n_gliders - 266) // 8 - pat = rect1 + rect2(exts,exts) - for _ in range(n_gliders): - pat = (pat+grect)[p] - return (pat, mpop) - -p4b1 = lt.pattern("""14b2o$6bo7bo9bo$6b3o3bobo7b3o$9bob3o7bo$8bob2o9b2o$8bo2bo$17bo$16bobo$ -9bo2bo2bo2bo$10b2o4b2o$b2o$2bo15b2o$2bobo13bobo$3b2o13bo3$7b2o$6bo2bo$ -7bobo$o7bo$3o$3bo$2bo$2bo2bo2b2o$5bo2b2o$3bobo$2bobo$2bo$b2o""") -p4b2 = lt.pattern("""22b2o$22bo$20bobo$18b2obo$15b6o$15b3o$21b2o$21bo$22b3o$16bo7bo$15bobo$ -15bo2bo$16b2o3$20b2o$20bobo$22bo$22b2o$7b2o4b2o$6bo2bo3b2o$6bobo$7bo6b -o$14b2o$2b2o12bo$3bo7b2o2bo$3o7bobo3b3o$o9bo7bo$9b2o""")(10,6) - -# works down to p36 but fixed oscillators beat it (and it requires more gliders) below p140 -def p4_bumper_loop(p): - if p%8 != 4 or p < 140: - return None - exts = (p-140) // 8 - return (p4b1 + p4b2(exts,exts), 141) - -mold = lt.pattern("3b3o$bob3o$obobo$o2bo$b2o")(-1,15) - -def mold_rectifier_loop(p): - if p%8 != 4 or p < 212: - return None - pat, mpop = rectifier_loop(p//2) - return (pat+mold, mpop+12 if mpop != None else None) - -p8b1 = lt.pattern("""10b2o$10b2o$25bo$23b3o$9b3o10bo$9b2o11b2o$12b2o$11b3o4bo$10bobo4bobo$ -10b2o4bo2bo$17b2o2$3b2o5b2o$4bo5b2o$4bobo$5b2o7bo$9b2ob2o$8bobo2b2o$9b -o$5bo$4b3o$3bob3o$2bo3bo$bo3bo$3obo$b3o$2bo""") -p8b2 = lt.pattern("""13bo2b2o$12bo3b2o2b2o$12bo7b2o$13b4o4$12bo$11bobo$11bo2bo$12b2o3$16b2o -$16bobo$18bo$2o16b2o$2o2b2o$4bobo$5bo$8b3o$2b2o3bo$3bo3bo3bo$3o4bo2bob -o$o8bobo2bo$10bo3bo$14bo$11b3o""")(14,8) - -# works down to p40 but fixed oscillators beat it (and it requires more gliders) below p104 -def p8_loop(p): - if p%8 or p < 104: - return None - exts = p//8 - 13 - par = exts%2 - return (p8b1 + p8b2(exts,exts)[4*par], 120+par) - -snark1 = lt.pattern("""9b2o$8bobo$2b2o4bo$o2bo2b2ob4o$2obobobobo2bo$3bobobobo$3bobob2o$4bo2$ -17b2o$8b2o7bo$8b2o5bobo$15b2o7$5b2o$6bo$3b3o$3bo""") -snark2 = snark1("rot90",-20,30) -snark3 = snark1("rot180",10,50) -snark4 = snark1("rot270",30,20) -gsnark = lt.pattern("bo$2o$obo")(7,13) - -# only potentially SKOP from p43 to p105 inclusive; -# at periods divisible by 4 is beaten by fixed oscillators -def snark_loop(p): - mpop = None - if not (43 <= p < 106 and p%4): - return None - elif p%2: - n_gliders, mpop = (8, 228) - i = (p-37) // 4 if p%4 == 1 else (p-29) // 2 if p <= 75 else (p-79) // 4 - else: - if p >= 58: - n_gliders, mpop = (4, 208) - i = (p-58) // 4 - else: - n_gliders, mpop = (8, 230) - i = (2,3,0)[(p-46)//4] - slack = p*n_gliders//8 - 29 - pat = snark1 + snark2(-i,i) + snark3(slack-2*i,slack) + snark4(slack-i,slack-i) - for _ in range(n_gliders): - pat = (pat+gsnark)[p] - return (pat, mpop) - -p6b1 = lt.pattern("4b2o14bo$2o2b2o12b3o$2o2bob2o9bo$5b3o9b2o2$13bo$5b2o5bobo$5b2o4bo2bo$12b2o") -p6b2 = p6b1("rot90",-2,28)[5] -p6b3 = p6b1("rot180",26,30)[4] -p6b4 = p6b1("rot270",28,2)[3] -gp6b = lt.pattern("2o$obo$o")(14,10) - -def p6_bumper_loop(p): - if p%6 or not p%8 or p < 36: - return None - elif p%4: - n_gliders, mpop = (2, 122) if p >= 66 else (6, 130) - i = (p-30) // 4 if p <= 54 else 0 - else: - n_gliders, mpop = (1, 123) if p >= 132 else (3, 127) if p >= 60 else (5, 140) - i = (1,0,3,6)[(p-36)//24] if p <= 108 else 0 - slack = (p*n_gliders - 124) // 8 - pat = p6b1 + p6b2(-i,i)[2*i] + p6b3(slack-2*i,slack)[2*slack] + p6b4(slack-i,slack-i)[2*(slack+i)] - for _ in range(n_gliders): - pat = (pat+gp6b)[p] - return (pat, mpop) - -pd0_1 = lt.pattern("""16b3o$15bo3bo$15bo3bo$16b3o5$16b3o$15bo3bo$15bo3bo$16b3o4$2o3bo2bo3b2o -$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") -pd0_2 = lt.pattern("""6b2o3bo2bo3b2o$6b5o4b5o$6b2o3bo2bo3b2o4$b3o$o3bo$o3bo$b3o5$b3o$o3bo$o -3bo$b3o""")(11,12) - -pd15_1 = lt.pattern("""16b3o$15bo3bo$15bo3bo$16b3o5$16b3o$15bo3bo$15bo3bo$16b3o4$2o3bo2bo3b2o -$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") -pd15_2 = lt.pattern("""11b2o$9bo4bo$8bo6bo$7bo8bo$7bo8bo$7bo8bo$8bo6bo$bo7bo4bo$bo9b2o2$bo$ob -o2$3o3$3o2$obo$bo2$bo$bo""")(14,11) -# 30+120n: buckaroo shuttle -pd30_1 = lt.pattern("""o$3o$3bo$2b2o$9b2o$5bo2b2o$4bobo3bo$3bo3bo$3b5o$2b2o3b2o$3b5o$4b3o$5bo -9$4b2o$4b2o""") -pd30_2 = lt.pattern("3b2o$3b2o3$3bo$2bobo$bo3bo$b5o$2o3b2o$b5o$2b3o$3bo8$5b2o$5bo$6b3o$8bo")(13,-11) - -pd45_1 = lt.pattern("""17bo$17bo$16b3o3$16b3o$17bo$17bo$17bo$17bo$16b3o3$16b3o$17bo$17bo2$2o -3bo2bo3b2o$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") -pd45_2 = lt.pattern("""7b2o6b2o$6bo2bo4bo2bo$6bo2bo4bo2bo$6bo2bo4bo2bo$7b2o6b2o4$bo$bo$obo$bo -$bo$bo$bo$obo$bo$bo""")(18,19) -# 60+120n: p60 glider shuttle extensions -pd60_1 = lt.pattern("17b3o$2bo2bo4bo2bo3bo$3o2b6o2b3o2bo$2bo2bo4bo2bo") -pd60_2 = lt.pattern("2bo2bo4bo2bo$3o2b6o2b3o$2bo2bo4bo2bo")(25,5) -# 75+120n: 6 bits extensions -pd75_1 = lt.pattern("""20bo$19bobo3$19b3o$19b3o$20bo3$20bo$19b3o$19b3o3$19bobo$20bo$2b2o6b2o$ -bo2bo4bo2bo4b3o$6o2b6o3bo$bo2bo4bo2bo5bo$2b2o6b2o""") -pd75_2 = lt.pattern("bo2b2o4b2o2bo$o3b3o2b3o3bo$bo2b2o4b2o2bo")(26,23) -# 90+120n: Elkies–Simkin 1hd reflector with pentadecathlons (p90 version is Silverstream) -pd90_1 = lt.pattern("""7b2o4bo$7b2o3bobo$13bo3$2o2b2o2b2o$2o2b2ob2o$9bo3$3b3o$3b3o$4bo$4bo$4b -o$3bobo3$3bobo$4bo$4bo$4bo$3b3o$3b3o""") -pd90_2 = lt.pattern("""9b3o$9b3o$10bo$10bo$10bo$9bobo3$9bobo$10bo$10bo$10bo$9b3o$9b3o4$9b2o2b -2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(13,-2) - -def pd_shuttle(p): - if p%15 or p < 30: - return None - q = p//15 - exts, r = divmod(q, 8) - exts *= 15 - if r == 0: - return (pd0_1 + pd0_2(exts,exts), 67) - elif r == 1: - return (pd15_1 + pd15_2(exts,exts), 75) - elif r == 2: - return (pd30_1 + pd30_2(exts,exts), 51) - elif r == 3: - return (pd45_1 + pd45_2(exts,exts), 75) - elif r == 4: - return (pd60_1 + pd60_2(exts,exts), 29) - elif r == 5: - return (pd75_1 + pd75_2(exts,exts), 49) - elif r == 6: - return (pd90_1 + pd90_2(exts,exts), 61) - else: - return None - -# 90+24n: Elkies–Simkin 1hd reflector with p6 thumbs (p114 version is Ocellus) -p6ts1 = lt.pattern("""7b2o4bo$7b2o3bobo$13bo3$2o2b2o2b2o$2o2b2ob2o$9bo3$3b2o$2bo2b2o$2bo4b2o -$2o3bob2o$bobo$bob6o$2bo5bo$3b3o$5bo""") -p6ts2 = lt.pattern("""9bo$9b3o$6bo5bo$6b6obo$11bobo$6b2o2bo2b2o$6bobo2b2o$10bobo$11bo4$9b2o -2b2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(13,3) - -def p6thumb_shuttle(p): - if p%24 != 18 or p < 90: - return None - return (p6ts1 + p6ts2((p-90)//8, (p-90)//8), 92) - -# 98+56n: Elkies–Simkin 1hd reflector with 34P14 shuttles (p98 version is Gallus) -p14_1 = lt.pattern("""12b2o4bo$12b2o3bobo$18bo3$5b2o2b2o2b2o$5b2o2b2ob2o$14bo3$9bo$8b3o$2o4b -2o3bob2o$2o4bo2b2o2b2o$6bob2o4$6bob2o$2o4bo2b2o2b2o$2o4b2o3bob2o$8b3o$9bo""") -p14_2 = lt.pattern("""14bo$13b3o$5b2o4b2o3bob2o$5b2o4bo2b2o2b2o$11bob2o4$11bob2o$5b2o4bo2b2o -2b2o$5b2o4b2o3bob2o$13b3o$14bo4$9b2o2b2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(19,0) - -def p14_shuttle(p): - if p%56 != 42 or p < 98: - return None - return (p14_1 + p14_2((p-98)//8, (p-98)//8), 105) - -# 184+92n: Hickerson's stop-and-go reaction -tbs0_1 = lt.pattern("""2b2o5b2o$2b2o5b2o6$2b3o3b3o$bo2bo3bo2bo$bo3bobo3bo$2obobobobob2o$2ob2o -3b2ob2o$b3o5b3o6$3bo$2bobo$bo3bo$b5o$obobobo$bo3bo2$bo3bo$obobobo$b5o -3b2o$bo3bo3b2o$2bobo$3bo2$2b3o$2bo$3bo""") -tbs0_2 = lt.pattern("""19b2o$18b5o$2b2o14bo4bo5b2o$2b2o14b3o2bo5b2o$19bo2b2o$20b2o$4bo3bo$2b -2obobob2o9b2o$bob2o3b2obo7bo2b2o$o2bo5bo2bo5b3o2bo5b2o$bob2o3b2obo6bo -4bo5b2o$2b2obobob2o7b5o$4bo3bo10b2o""")(-8,10) -tbs92_2 = lt.pattern("""9bo$8bobo$2b2o3bo3bo$2b2o3b5o$6bobobobo$7bo3bo2$7bo3bo$6bobobobo$7b5o$ -7bo3bo$8bobo$9bo6$b3o5b3o$2ob2o3b2ob2o$2obobobobob2o$bo3bobo3bo$bo2bo -3bo2bo$2b3o3b3o6$2b2o$2b2o""")(-8,33) - -# 138+184n: jslife reflectors-180.lif, 5hd class 1 -tbs138_1 = lt.pattern("""b2o5b2o$b2o5b2o13$bo7bo$obo5bobo$3bo3bo$o2bo3bo2bo$bobo3bobo$3b2ob2o$ -2ob2ob2ob2o$2o2bobo2b2o$b3o3b3o2b3o$2bo5bo3bo$13bo3$b2o$b2o""") -tbs138_2 = lt.pattern("""3bo$2bobo2$o5bo2b2o$3bo5b2o$2o3b2o4$2o3b2o$3bo$o5bo2$2bobo$3bo3$2bo7bo -$bobo5bobo$4bo3bo$bo2bo3bo2bo$2bobo3bobo$4b2ob2o$b2ob2ob2ob2o$b2o2bobo -2b2o$2b3o3b3o$3bo5bo4$2b2o5b2o$2b2o5b2o""")(21,25) - -def twinbees_shuttle(p): - if p%46 or p < 138: - return None - q = p//46 - exts, r = divmod(q, 4) - exts *= 23 - if r == 0: - return (tbs0_1 + tbs0_2(exts,exts), 60) - elif r == 2: - return (tbs0_1 + tbs92_2(exts,exts), 77) - elif r == 3: - return (tbs138_1 + tbs138_2(exts,exts), 72) - else: - return None diff --git a/readme.md b/readme.md index 995b1f2..a6f6c9a 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ In May and June 2021 David Raucci (hotdogPi) on the ConwayLife forums discovered This repository started out as an independent script in my glider synthesis database [Shinjuku](https://gitlab.com/parclytaxel/Shinjuku) as an automatic generator of the aforementioned glider loops and collector of other **s**mallest **k**nown **o**scillators of **p**eriods _n_ – hence the name Skopje, North Macedonia's capital. It still requires [lifelib](https://gitlab.com/apgoucher/lifelib) however. -To get the smallest known oscillator of period 109 together with its minimum population: +To get the smallest known oscillator of period 109 in Conway's Life together with its minimum population: ```python from skopje import skop @@ -13,4 +13,8 @@ skop109, minpop109 = skop(109)[0] `skop()` returns a _list_ of pairs (lifelib Pattern, minimum population) to allow ties as in the p4, p7 and p92 cases; to get the pattern's apgcode and RLE use `skop109.apgcode` and `skop109.rle_string()` respectively. The function compares several different constructions as well as a list of fixed oscillators, LCM or otherwise, to arrive at its final result. -Each function producing oscillators of a certain type for an infinite number of periods should accept the desired period as its argument and return `None` if the construction is not applicable to that period or (Pattern, minimum population) otherwise. The latter element can itself be `None` to indicate that the period is in a range where the population cannot easily be calculated. +SKOPs in other rules can be found by passing the `rule=` argument; the supplied string gets run through lifelib's `sanirule()` function before being mapped to a filename. The file itself should contain three objects: + +* `lt`, the lifetree under which computations are performed. +* `fixeds`, a multiline string where each line gives the period of a fixed oscillator, its apgcode and its minimum population, all separated by spaces. +* `cfuncs`, a sequence of functions each accepting a period and returning `None` if its corresponding construction is not applicable to that period or (Pattern, minimum population) otherwise. The latter element can itself be `None` to indicate that the period is in a range where the population does not follow a defined formula. diff --git a/skopje.py b/skopje.py deleted file mode 100755 index 16688bf..0000000 --- a/skopje.py +++ /dev/null @@ -1,35 +0,0 @@ -from constructs import * - -def read_oscfiles(*fns): - """Read oscillators and their minimum populations from provided files - and return a dictionary.""" - res = {} - for fn in fns: - with open(fn, 'r') as f: - for l in f: - period, apgcode, mpop = l.split() - period = int(period) - res[period] = res.get(period, tuple()) + ((lt.pattern(apgcode), int(mpop)),) - return res - -def skop(p): - """Return a list of pairs (Pattern, minimum population) representing - the smallest known oscillators of the specified period.""" - fixed = read_oscfiles("fixedoscs") - cands = list(fixed.get(p, [])) - cands.append(rectifier_loop(p)) - cands.append(mold_rectifier_loop(p)) - cands.append(p4_bumper_loop(p)) - cands.append(p8_loop(p)) - cands.append(snark_loop(p)) - cands.append(p6_bumper_loop(p)) - cands.append(pd_shuttle(p)) - cands.append(p6thumb_shuttle(p)) - cands.append(p14_shuttle(p)) - cands.append(twinbees_shuttle(p)) - cands = list(filter(bool, cands)) - if not cands: - return [] - cands = [pair if pair[1] else (pair[0], minpop(pair[0])) for (i, pair) in enumerate(cands)] - mp = min(pair[1] for pair in cands) - return list(filter(lambda pair: pair[1] == mp, cands)) diff --git a/skopje/__init__.py b/skopje/__init__.py new file mode 100644 index 0000000..dcefb0e --- /dev/null +++ b/skopje/__init__.py @@ -0,0 +1,2 @@ +# What to do upon calling "import skopje" +from .skop import skop diff --git a/fixedoscs b/skopje/life.py similarity index 52% rename from fixedoscs rename to skopje/life.py index d041652..93a3834 100644 --- a/fixedoscs +++ b/skopje/life.py @@ -1,4 +1,8 @@ -1 xs4_33 4 +import lifelib +sess = lifelib.load_rules("b3s23") +lt = sess.lifetree(n_layers=1) + +fixeds = """1 xs4_33 4 1 xs4_252 4 2 xp2_7 3 3 xp3_4hh186z07 12 @@ -138,4 +142,226 @@ 512 xp512_yr33y166z0ggy033y16426ye33y0ccz011w66yuggz66yz56zzzy2mqyz66zygckgk2a21ya66woozy133y0ccye6426y1cczy566y1cc 100 576 xp576_y32560652y92560652zy03diiie0eiiid3y33diiie0eiiid3zyboozzykgk34zooyj16w6y5oozz33y5cwcgyj33zy94o51zzyk33zy0om999e0e999moy3om999e0e999mozy38kc0ck8y98kc0ck8 184 690 xp690_0ooy9vtvzyc757zok4sxs4kozzzz033y133 40 -1008 xp1008_y48oyc356zy432yi696y9o8gozyjc871zzy12552yg2bq4y4o8zca6y3oggyp113y3ca6zy823ys8kk8zzyogs26zx3123y9cicyi8ozymckoyc32 107 +1008 xp1008_y48oyc356zy432yi696y9o8gozyjc871zzy12552yg2bq4y4o8zca6y3oggyp113y3ca6zy823ys8kk8zzyogs26zx3123y9cicyi8ozymckoyc32 107""" + +rect1 = lt.pattern("""8bo$7bobo$8bo2$6b5o$5bo4bo$4bo2bo$bo2bob2o$obobo5bo$bo2bo4bobo$4b2o2bo +2bo$9b2o9$17b2o$17b2o8$7b2o22b2o$8bo21bo2bo$5b3o23b2o$5bo6$14b2o$15bo$12b3o$12bo""") +rect2 = rect1("rot180",52,74) +grect = lt.pattern("bo$2o$obo")(17,23) + +def rectifier_loop(p): + if p%4 == 0 or p < 106: + return None + elif p%4 == 1: + n_gliders, mpop = (2, 116) if p >= 133 else (10, 156) + elif p%4 == 3: + n_gliders, mpop = (6, 136) + elif p%8 == 2: + n_gliders = 1 if p >= 266 else 5 + mpop = 113 if p >= 266 else 133 if p >= 210 else None + else: + n_gliders, mpop = (3, 123 if p >= 206 else None) + exts = (p*n_gliders - 266) // 8 + pat = rect1 + rect2(exts,exts) + for _ in range(n_gliders): + pat = (pat+grect)[p] + return (pat, mpop) + +p4b1 = lt.pattern("""14b2o$6bo7bo9bo$6b3o3bobo7b3o$9bob3o7bo$8bob2o9b2o$8bo2bo$17bo$16bobo$ +9bo2bo2bo2bo$10b2o4b2o$b2o$2bo15b2o$2bobo13bobo$3b2o13bo3$7b2o$6bo2bo$ +7bobo$o7bo$3o$3bo$2bo$2bo2bo2b2o$5bo2b2o$3bobo$2bobo$2bo$b2o""") +p4b2 = lt.pattern("""22b2o$22bo$20bobo$18b2obo$15b6o$15b3o$21b2o$21bo$22b3o$16bo7bo$15bobo$ +15bo2bo$16b2o3$20b2o$20bobo$22bo$22b2o$7b2o4b2o$6bo2bo3b2o$6bobo$7bo6b +o$14b2o$2b2o12bo$3bo7b2o2bo$3o7bobo3b3o$o9bo7bo$9b2o""")(10,6) + +# works down to p36 but fixed oscillators beat it (and it requires more gliders) below p140 +def p4_bumper_loop(p): + if p%8 != 4 or p < 140: + return None + exts = (p-140) // 8 + return (p4b1 + p4b2(exts,exts), 141) + +mold = lt.pattern("3b3o$bob3o$obobo$o2bo$b2o")(-1,15) + +def mold_rectifier_loop(p): + if p%8 != 4 or p < 212: + return None + pat, mpop = rectifier_loop(p//2) + return (pat+mold, mpop+12 if mpop != None else None) + +p8b1 = lt.pattern("""10b2o$10b2o$25bo$23b3o$9b3o10bo$9b2o11b2o$12b2o$11b3o4bo$10bobo4bobo$ +10b2o4bo2bo$17b2o2$3b2o5b2o$4bo5b2o$4bobo$5b2o7bo$9b2ob2o$8bobo2b2o$9b +o$5bo$4b3o$3bob3o$2bo3bo$bo3bo$3obo$b3o$2bo""") +p8b2 = lt.pattern("""13bo2b2o$12bo3b2o2b2o$12bo7b2o$13b4o4$12bo$11bobo$11bo2bo$12b2o3$16b2o +$16bobo$18bo$2o16b2o$2o2b2o$4bobo$5bo$8b3o$2b2o3bo$3bo3bo3bo$3o4bo2bob +o$o8bobo2bo$10bo3bo$14bo$11b3o""")(14,8) + +# works down to p40 but fixed oscillators beat it (and it requires more gliders) below p104 +def p8_loop(p): + if p%8 or p < 104: + return None + exts = p//8 - 13 + par = exts%2 + return (p8b1 + p8b2(exts,exts)[4*par], 120+par) + +snark1 = lt.pattern("""9b2o$8bobo$2b2o4bo$o2bo2b2ob4o$2obobobobo2bo$3bobobobo$3bobob2o$4bo2$ +17b2o$8b2o7bo$8b2o5bobo$15b2o7$5b2o$6bo$3b3o$3bo""") +snark2 = snark1("rot90",-20,30) +snark3 = snark1("rot180",10,50) +snark4 = snark1("rot270",30,20) +gsnark = lt.pattern("bo$2o$obo")(7,13) + +# only potentially SKOP from p43 to p105 inclusive; +# at periods divisible by 4 is beaten by fixed oscillators +def snark_loop(p): + mpop = None + if not (43 <= p < 106 and p%4): + return None + elif p%2: + n_gliders, mpop = (8, 228) + i = (p-37) // 4 if p%4 == 1 else (p-29) // 2 if p <= 75 else (p-79) // 4 + else: + if p >= 58: + n_gliders, mpop = (4, 208) + i = (p-58) // 4 + else: + n_gliders, mpop = (8, 230) + i = (2,3,0)[(p-46)//4] + slack = p*n_gliders//8 - 29 + pat = snark1 + snark2(-i,i) + snark3(slack-2*i,slack) + snark4(slack-i,slack-i) + for _ in range(n_gliders): + pat = (pat+gsnark)[p] + return (pat, mpop) + +p6b1 = lt.pattern("4b2o14bo$2o2b2o12b3o$2o2bob2o9bo$5b3o9b2o2$13bo$5b2o5bobo$5b2o4bo2bo$12b2o") +p6b2 = p6b1("rot90",-2,28)[5] +p6b3 = p6b1("rot180",26,30)[4] +p6b4 = p6b1("rot270",28,2)[3] +gp6b = lt.pattern("2o$obo$o")(14,10) + +def p6_bumper_loop(p): + if p%6 or not p%8 or p < 36: + return None + elif p%4: + n_gliders, mpop = (2, 122) if p >= 66 else (6, 130) + i = (p-30) // 4 if p <= 54 else 0 + else: + n_gliders, mpop = (1, 123) if p >= 132 else (3, 127) if p >= 60 else (5, 140) + i = (1,0,3,6)[(p-36)//24] if p <= 108 else 0 + slack = (p*n_gliders - 124) // 8 + pat = p6b1 + p6b2(-i,i)[2*i] + p6b3(slack-2*i,slack)[2*slack] + p6b4(slack-i,slack-i)[2*(slack+i)] + for _ in range(n_gliders): + pat = (pat+gp6b)[p] + return (pat, mpop) + +pd0_1 = lt.pattern("""16b3o$15bo3bo$15bo3bo$16b3o5$16b3o$15bo3bo$15bo3bo$16b3o4$2o3bo2bo3b2o +$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") +pd0_2 = lt.pattern("""6b2o3bo2bo3b2o$6b5o4b5o$6b2o3bo2bo3b2o4$b3o$o3bo$o3bo$b3o5$b3o$o3bo$o +3bo$b3o""")(11,12) + +pd15_1 = lt.pattern("""16b3o$15bo3bo$15bo3bo$16b3o5$16b3o$15bo3bo$15bo3bo$16b3o4$2o3bo2bo3b2o +$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") +pd15_2 = lt.pattern("""11b2o$9bo4bo$8bo6bo$7bo8bo$7bo8bo$7bo8bo$8bo6bo$bo7bo4bo$bo9b2o2$bo$ob +o2$3o3$3o2$obo$bo2$bo$bo""")(14,11) +# 30+120n: buckaroo shuttle +pd30_1 = lt.pattern("""o$3o$3bo$2b2o$9b2o$5bo2b2o$4bobo3bo$3bo3bo$3b5o$2b2o3b2o$3b5o$4b3o$5bo +9$4b2o$4b2o""") +pd30_2 = lt.pattern("3b2o$3b2o3$3bo$2bobo$bo3bo$b5o$2o3b2o$b5o$2b3o$3bo8$5b2o$5bo$6b3o$8bo")(13,-11) + +pd45_1 = lt.pattern("""17bo$17bo$16b3o3$16b3o$17bo$17bo$17bo$17bo$16b3o3$16b3o$17bo$17bo2$2o +3bo2bo3b2o$5o4b5o$2o3bo2bo3b2ob3o$15bo$16bo""") +pd45_2 = lt.pattern("""7b2o6b2o$6bo2bo4bo2bo$6bo2bo4bo2bo$6bo2bo4bo2bo$7b2o6b2o4$bo$bo$obo$bo +$bo$bo$bo$obo$bo$bo""")(18,19) +# 60+120n: p60 glider shuttle extensions +pd60_1 = lt.pattern("17b3o$2bo2bo4bo2bo3bo$3o2b6o2b3o2bo$2bo2bo4bo2bo") +pd60_2 = lt.pattern("2bo2bo4bo2bo$3o2b6o2b3o$2bo2bo4bo2bo")(25,5) +# 75+120n: 6 bits extensions +pd75_1 = lt.pattern("""20bo$19bobo3$19b3o$19b3o$20bo3$20bo$19b3o$19b3o3$19bobo$20bo$2b2o6b2o$ +bo2bo4bo2bo4b3o$6o2b6o3bo$bo2bo4bo2bo5bo$2b2o6b2o""") +pd75_2 = lt.pattern("bo2b2o4b2o2bo$o3b3o2b3o3bo$bo2b2o4b2o2bo")(26,23) +# 90+120n: Elkies–Simkin 1hd reflector with pentadecathlons (p90 version is Silverstream) +pd90_1 = lt.pattern("""7b2o4bo$7b2o3bobo$13bo3$2o2b2o2b2o$2o2b2ob2o$9bo3$3b3o$3b3o$4bo$4bo$4b +o$3bobo3$3bobo$4bo$4bo$4bo$3b3o$3b3o""") +pd90_2 = lt.pattern("""9b3o$9b3o$10bo$10bo$10bo$9bobo3$9bobo$10bo$10bo$10bo$9b3o$9b3o4$9b2o2b +2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(13,-2) + +def pd_shuttle(p): + if p%15 or p < 30: + return None + q = p//15 + exts, r = divmod(q, 8) + exts *= 15 + if r == 0: + return (pd0_1 + pd0_2(exts,exts), 67) + elif r == 1: + return (pd15_1 + pd15_2(exts,exts), 75) + elif r == 2: + return (pd30_1 + pd30_2(exts,exts), 51) + elif r == 3: + return (pd45_1 + pd45_2(exts,exts), 75) + elif r == 4: + return (pd60_1 + pd60_2(exts,exts), 29) + elif r == 5: + return (pd75_1 + pd75_2(exts,exts), 49) + elif r == 6: + return (pd90_1 + pd90_2(exts,exts), 61) + else: + return None + +# 90+24n: Elkies–Simkin 1hd reflector with p6 thumbs (p114 version is Ocellus) +p6ts1 = lt.pattern("""7b2o4bo$7b2o3bobo$13bo3$2o2b2o2b2o$2o2b2ob2o$9bo3$3b2o$2bo2b2o$2bo4b2o +$2o3bob2o$bobo$bob6o$2bo5bo$3b3o$5bo""") +p6ts2 = lt.pattern("""9bo$9b3o$6bo5bo$6b6obo$11bobo$6b2o2bo2b2o$6bobo2b2o$10bobo$11bo4$9b2o +2b2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(13,3) + +def p6thumb_shuttle(p): + if p%24 != 18 or p < 90: + return None + return (p6ts1 + p6ts2((p-90)//8, (p-90)//8), 92) + +# 98+56n: Elkies–Simkin 1hd reflector with 34P14 shuttles (p98 version is Gallus) +p14_1 = lt.pattern("""12b2o4bo$12b2o3bobo$18bo3$5b2o2b2o2b2o$5b2o2b2ob2o$14bo3$9bo$8b3o$2o4b +2o3bob2o$2o4bo2b2o2b2o$6bob2o4$6bob2o$2o4bo2b2o2b2o$2o4b2o3bob2o$8b3o$9bo""") +p14_2 = lt.pattern("""14bo$13b3o$5b2o4b2o3bob2o$5b2o4bo2b2o2b2o$11bob2o4$11bob2o$5b2o4bo2b2o +2b2o$5b2o4b2o3bob2o$13b3o$14bo4$9b2o2b2o$9b2o2b2o3$bo$obo3b2o$bo4b2o""")(19,0) + +def p14_shuttle(p): + if p%56 != 42 or p < 98: + return None + return (p14_1 + p14_2((p-98)//8, (p-98)//8), 105) + +# 184+92n: Hickerson's stop-and-go reaction +tbs0_1 = lt.pattern("""2b2o5b2o$2b2o5b2o6$2b3o3b3o$bo2bo3bo2bo$bo3bobo3bo$2obobobobob2o$2ob2o +3b2ob2o$b3o5b3o6$3bo$2bobo$bo3bo$b5o$obobobo$bo3bo2$bo3bo$obobobo$b5o +3b2o$bo3bo3b2o$2bobo$3bo2$2b3o$2bo$3bo""") +tbs0_2 = lt.pattern("""19b2o$18b5o$2b2o14bo4bo5b2o$2b2o14b3o2bo5b2o$19bo2b2o$20b2o$4bo3bo$2b +2obobob2o9b2o$bob2o3b2obo7bo2b2o$o2bo5bo2bo5b3o2bo5b2o$bob2o3b2obo6bo +4bo5b2o$2b2obobob2o7b5o$4bo3bo10b2o""")(-8,10) +tbs92_2 = lt.pattern("""9bo$8bobo$2b2o3bo3bo$2b2o3b5o$6bobobobo$7bo3bo2$7bo3bo$6bobobobo$7b5o$ +7bo3bo$8bobo$9bo6$b3o5b3o$2ob2o3b2ob2o$2obobobobob2o$bo3bobo3bo$bo2bo +3bo2bo$2b3o3b3o6$2b2o$2b2o""")(-8,33) + +# 138+184n: jslife reflectors-180.lif, 5hd class 1 +tbs138_1 = lt.pattern("""b2o5b2o$b2o5b2o13$bo7bo$obo5bobo$3bo3bo$o2bo3bo2bo$bobo3bobo$3b2ob2o$ +2ob2ob2ob2o$2o2bobo2b2o$b3o3b3o2b3o$2bo5bo3bo$13bo3$b2o$b2o""") +tbs138_2 = lt.pattern("""3bo$2bobo2$o5bo2b2o$3bo5b2o$2o3b2o4$2o3b2o$3bo$o5bo2$2bobo$3bo3$2bo7bo +$bobo5bobo$4bo3bo$bo2bo3bo2bo$2bobo3bobo$4b2ob2o$b2ob2ob2ob2o$b2o2bobo +2b2o$2b3o3b3o$3bo5bo4$2b2o5b2o$2b2o5b2o""")(21,25) + +def twinbees_shuttle(p): + if p%46 or p < 138: + return None + q = p//46 + exts, r = divmod(q, 4) + exts *= 23 + if r == 0: + return (tbs0_1 + tbs0_2(exts,exts), 60) + elif r == 2: + return (tbs0_1 + tbs92_2(exts,exts), 77) + elif r == 3: + return (tbs138_1 + tbs138_2(exts,exts), 72) + else: + return None + +cfuncs = (rectifier_loop, mold_rectifier_loop, p4_bumper_loop, p8_loop, snark_loop, + p6_bumper_loop, pd_shuttle, p6thumb_shuttle, p14_shuttle, twinbees_shuttle) diff --git a/skopje/skop.py b/skopje/skop.py new file mode 100755 index 0000000..eea3391 --- /dev/null +++ b/skopje/skop.py @@ -0,0 +1,30 @@ +from importlib import import_module +from lifelib.genera import sanirule + +aliases = {"b3s23": "life"} + +def minpop(pat): + """Compute the minimum population of the given pattern. + Because the latter is already associated with a rule no + further arguments are required.""" + return min(pat[i].population for i in range(pat.period)) + +def skop(p, rule="b3s23"): + """Return a list of pairs (Pattern, minimum population) representing + the smallest known oscillators of the specified period in the given rule. + Assumes that the local installation of lifelib knows about said rule.""" + rule = sanirule(rule) + rmod = import_module(f"..{aliases.get(rule, rule)}", __name__) + cands = [] + for line in rmod.fixeds.split("\n"): + lp, apg, mp = line.split() + if int(lp) == p: + cands.append((rmod.lt.pattern(apg), int(mp))) + for cfunc in rmod.cfuncs: + cands.append(cfunc(p)) + cands = list(filter(bool, cands)) + if not cands: + return [] + cands = [pair if pair[1] else (pair[0], minpop(pair[0])) for (i, pair) in enumerate(cands)] + mp = min(pair[1] for pair in cands) + return list(filter(lambda pair: pair[1] == mp, cands)) diff --git a/skopje/symbiosis.py b/skopje/symbiosis.py new file mode 100644 index 0000000..6708be0 --- /dev/null +++ b/skopje/symbiosis.py @@ -0,0 +1,353 @@ +import lifelib +sess = lifelib.load_rules("xsymbiosis") +lt = sess.lifetree() + +fixeds = """1 xs2_1_2 2 +2 xp2_7 3 +3 xp3_f_g 5 +4 xp4_ic_1z1 6 +5 xp5_ci_1z1 6 +6 xp6_2we4_1y0a 7 +7 xp7_usz1_1z2 7 +8 xp8_acs_1zw1 7 +9 xp9_02b7_28 7 +10 xp10_61e_1 7 +11 xp11_02s_21z01 7 +12 xp12_22go_1zx1 7 +13 xp13_oez1_018z02 9 +14 xp14_02208c_410g 8 +15 xp15_02b4_4g 7 +16 xp16_2og_01z1 6 +17 xp17_043h_8hzw1 9 +18 xp18_ka4_w12z1 8 +19 xp19_9312_g04 8 +20 xp20_iem_1z01 8 +21 xp21_028s_1zw1 7 +22 xp22_028e_4g1 7 +23 xp23_2g0cc_1y0gz1 8 +24 xp24_030186_2y14 8 +25 xp25_2cq8_1w2o 10 +26 xp26_2k6z1_g1 8 +27 xp27_05126z01_5g 8 +28 xp28_0224c_14xg 8 +29 xp29_012413_2w8w1 9 +30 xp30_1gee_21 9 +31 xp31_01j84zg_x18z0g 10 +32 xp32_03104_2xgo 8 +33 xp33_0273c_1y0a 10 +34 xp34_2gozwg_1wgz08 8 +35 xp35_031zxe43_4y1gzy0g 11 +36 xp36_0d1i_izw1 9 +37 xp37_2oo_1wk 8 +38 xp38_024e_18g 8 +39 xp39_o428g_21z1x23 12 +40 xp40_21w6571_14w8x1 12 +41 xp41_0gqw8_w1x4z1 8 +42 xp42_03ho_1xg 8 +43 xp43_0i974_1y04z1 9 +44 xp44_cquz02_01z1 11 +45 xp45_0222484_201w2 9 +46 xp46_01z039w8_w2z3wg0g 11 +47 xp47_023h6_1xg2 10 +48 xp48_6kz02_018z2 8 +49 xp49_w8acz1_081w2z2 10 +50 xp50_09glfw2_9y16 12 +51 xp51_0gk2zy32_w1z1y21 8 +52 xp52_620fx8_10gy166 13 +53 xp53_4g028zx2297_8w104z104x8 16 +54 xp54_020gzxh54wg_41zx2gx8 13 +55 xp55_079czw1_1g 10 +56 xp56_01408d32_208x402 13 +57 xp57_03bozw1y02_1gzy21 11 +58 xp58_ws33gsg04z1_g01x208 15 +59 xp59_25b30g_8gzy21 12 +60 xp60_0139y28_140ggy04 11 +61 xp61_27az2_x4z1 9 +62 xp62_14e6o_2y08 10 +63 xp63_4a97_h 10 +64 xp64_1318_40g 7 +65 xp65_01b4zy81_1gy5g 9 +66 xp66_01a4x8_1gy04 8 +67 xp67_0lvz31_x9z4021 15 +68 xp68_02sozg_w10oz8 11 +69 xp69_08xge6w1zx4_8y3802zw2x1 13 +70 xp70_xg88m8z041_y04w16za1 16 +71 xp71_01z08y2801zx2314_w1z4y2801zw4x84 15 +72 xp72_qf1_w82z1 10 +73 xp73_iz13ek_1agzx18zw1 15 +74 xp74_02bja4_1y0g4 10 +75 xp75_y31z0evzx1_0gy02zkz012 16 +76 xp76_010g8gz4ehrgfy18zx1zy8okc_2g84zy23xg 30 +77 xp77_044172c_4w8wg4 13 +78 xp78_y18048sicz1_y1402w1z2 13 +79 xp79_gs6kz01z41_w10az2z3 14 +80 xp80_g0588cz0kgf7_0g2zkxgz1 21 +81 xp81_02y0648gzy2gg843z01y01_2y21248zy5g81z1y221 25 +82 xp82_08026z040gox1_401z8y21zw1 14 +83 xp83_0o4226z0f8_g2w1z2 16 +84 xp84_ohgzb0iz01_160gz0k 16 +85 xp85_18ogs2ux1_2hy1gx1 15 +86 xp86_08zy06d132_8y633zy1gx2 14 +87 xp87_1c22ey64_g1ggy64 12 +88 xp88_0g0842zxa7aw8a_g04x1zx4xg 19 +89 xp89_06208bk8z4c_01xgw4z0g2 17 +90 xp90_1ggzwh54_01zw2g 11 +91 xp91_g1z178ge4_02z2gxg8 16 +92 xp92_0212gozwg_1x10gzw8 11 +93 xp93_0106aicy5g_108gy78zy11 15 +94 xp94_0cdw5zx1_2w1w2zw2 12 +95 xp95_0315963_208x41 15 +96 xp96_0o46z63_k21z8 13 +97 xp97_0351e4_408wg2 13 +98 xp98_0gy2108z0348_gy220gz08g 12 +99 xp99_020ksg8y28_1y04y48 12 +100 xp100_1y18066zwgw6b_2y0401w4zy2g4z01 18 +101 xp101_060oo273y02_21gxokx1zx1 21 +102 xp102_01e16c4_24wgw4 13 +103 xp103_wdiszg28j_05gz8g1zx1 20 +104 xp104_02so_1x4c 9 +105 xp105_0g0ks81_gx22zzx8k8 14 +106 xp106_029qwgz02zx8_2x2wgz2zxg 14 +107 xp107_0igz8m5_014zg085z1 16 +108 xp108_4y3q8gz26y29_8y31w8zogy1802 17 +109 xp109_02phby24_2y08y18zx1 14 +110 xp110_4g0s9zz01_802w2zz1 12 +111 xp111_010os21364_20gx4w12 17 +113 xp113_a0ehvh_hw4 17 +114 xp114_08sir204z0c_421x58 18 +115 xp115_0g0g01z0fgvp6_w8wg2z2 18 +116 xp116_0g8mey82z801zy98_04x1y74z082zy84 17 +117 xp117_w8w1zgy25d4_x842xgz0gy44cz01 17 +118 xp118_4253123w48_2x4x880g 17 +119 xp119_0g8mez801yl33_g4x1z082 17 +120 xp120_0dczw1yagzy32_821z02yb8zy31 14 +121 xp121_080i64z021_0gdx2z2 14 +122 xp122_1g8yf4zw137_g14ye4z02x8 14 +123 xp123_03192w31_1wgx4 12 +124 xp124_06208zx1a6y1g_4w104zx40gy08 16 +125 xp125_4agxka4zy02_hy3g4z01w4w1 17 +126 xp126_y0ihz9d9t1zw103_wgg421zigzx24 27 +127 xp127_ym46zyfg8oo8oyf66zy666y213464zzyhogprhe4y233z33zye31_ym1zye84w4w4zyd248zymgzynh2zyg1w1w1zyf4 64 +128 xp128_04ug2gszw1x1y14_4g10102gz01y58 21 +129 xp129_2okex4_4x1x8 10 +130 xp130_y42z08k17y81z4_y1gw1z02yb2z041 15 +131 xp131_0gy966zx24ey2cc_gya104zw480gy2g08 22 +132 xp132_om26cko_21x248z1 19 +133 xp133_22z0hqek201zy266_41zxgw5zy12 20 +134 xp134_1f0349430f1_g048gw840g 23 +136 xp136_0134468zzyb1_14w80gzyag 13 +137 xp137_0dcz1_1w4z02 9 +138 xp138_0g0og2z4qr7_gx41z840g 19 +139 xp139_10248gz3lkm21z320h96z201_22w24zx80k1zy1g2z11w1 37 +140 xp140_0gp0eo08x4zw232_8042wcx8 21 +141 xp141_02uaz0a_09z50g 14 +142 xp142_01ozx131_14y0oo 13 +143 xp143_y91z08n1o1f_y81zg3y0g2zx1 19 +144 xp144_0gacg04x4zw7rpp_04x8y08x33z18zw1 24 +145 xp145_4ogx2zy61_2w8w1xgzzyaoo 14 +146 xp146_y666zwagz030rpzx4_y521zx5z10czx2 24 +147 xp147_y284qa64zz13ybcc_y142w1w2zz04 22 +148 xp148_c60246zyb1_2w1w9zya1 14 +149 xp149_0g846z1348oya1_8421z48gyb1zx1 22 +150 xp150_y262cz0ly31z01_gy11w4zay51zw2 17 +151 xp151_y85z0gu2wuzw101_y92zg6y14zw20201 22 +152 xp152_0884676488zz2y52zy0cic_8021x1208zz02y32zzy11 27 +153 xp153_4y3q8gz26y29_8y31w8z8y2802 15 +154 xp154_xo8y61w4zx302zx6giey718fzy42_oo04y62w2zx404y8g0gzygg0cczy21w1 41 +156 xp156_g0cguzw8y233zzyq2zyof1601_82w1z0gzzyr1zyogw82 27 +158 xp158_0249he4y31_48gxg8y11 15 +159 xp159_8021bezx2y1204_3h0gzx4y2a 17 +162 xp162_y0iis1zym1z804czw1_xdx2zyl1z48gzx1 23 +164 xp164_y1g842248gzy01y41z4yc4_xg84201w248gzz8yc8 23 +165 xp165_324aic4_48gxg8 16 +166 xp166_058gzxv96z0a1_10248gzy1g2z80421 25 +167 xp167_20gy4g02zx126aa621_1y81z012480gw8421 25 +169 xp169_1w206aacw4_2x2y028 14 +170 xp170_03pmzx13yc31_4gzy04yd4 16 +171 xp171_x8gaggsy166z0136_gx401wiz248 24 +173 xp173_370696_408w1 14 +176 xp176_0o42s8z074zy766_42w124z9zy7802 25 +177 xp177_08n1s1f_82y0g2zw101 20 +178 xp178_2xsc4y9gzy0vogy94z1_01y0gy88zy1343y78z02 26 +180 xp180_x8658gz201xg065zx1_w4x248gz05y1ow4zw2 26 +182 xp182_4y61zx12646_04y42zw24x8 13 +183 xp183_0gyc24ckozy88xg0j_gyd1w20gzy7408zyb10101 21 +184 xp184_ok2w4z010jy04wgz04y124_01y02z1y4sa4ozw3y04 29 +185 xp185_02zxsy84zzxgw4a533_104zyb2zy7gzx8040g08 22 +186 xp186_4w1y258ai48w4_8w2y1106x408 19 +187 xp187_0g846y74z1348o_8421y78z48gzx1 22 +190 xp190_0o8e1w6iszw4y31cc_841w101w4z0204y310k 28 +191 xp191_1w2skc22y1gzggg_021y09y1g 19 +193 xp193_0o42s8z074_42w124z9 18 +194 xp194_g088ca2kzxc041_8402w10izxg8w2 22 +195 xp195_0o4aa6z0fbzy5quzy2caa43_g2w1z2gzy618zy3gw81 40 +198 xp198_0cirqcz9876_gzi1 19 +202 xp202_0g042d2y22zx24f_g021x1y04zw480g 20 +203 xp203_648085_8g0gw5 12 +204 xp204_1y6ggwgy61zy64f0817_01yj1zy5410gw81 22 +209 xp209_1ydk8gggzyep9gpmfz8yd21_02yd248zyk6z04yd421 32 +210 xp210_0208g853_10gxg84 13 +214 xp214_x6gugewegug6zzz6acy8ca6_w2w1w22w1w2 38 +215 xp215_o8429248oz4_421x124z2 20 +217 xp217_y180s272s08z09yb9zzy51_y08021x1208z9yd9zy5g 26 +219 xp219_0auy4sz011zz0lvy4e_81z02z0gz4z01 28 +220 xp220_040419b033w2_6020gg04cw2 22 +224 xp224_6lh6sy04zx1_g8x2x88 19 +228 xp228_046073cy4c37064_4w8wg4y24gw8w4 26 +231 xp231_031313y631313_2w4w2y42w4w2 22 +232 xp232_08xsu48gz08xvvj873zy21_8y21248z8y4g81zy321 29 +234 xp234_4auzx28_gw1z014w8 14 +236 xp236_08y24aqox2zy2ooy4g88gz02y941121_0gy21w4x14zy4gy78z14y8802 35 +237 xp237_0oc463gzy11zw1_8421zwgx21 18 +238 xp238_0g86z134oy71zy18w8_8401z48y81zx10gwg 22 +241 xp241_4c1zil42z01_0hkzx82z021 20 +242 xp242_01zy033z20gz01238y88_w2zz028z14xgy74y166 24 +243 xp243_01y7ogzyb1iczy9621z01_1y948gzyd14zya8421z1 25 +245 xp245_w8ed8gz01xi045zw1_04x248gz1y1ow4z02 26 +246 xp246_01c8gzy0g96z0831_1w248gzy2g2z8w421 25 +248 xp248_4y2253xc4gzyb21gzykgzy3g_8y72x8zyb4g1zzy48ya1 23 +249 xp249_0abg62xgy04_c0g0804w8x4 18 +251 xp251_0p1684zw3y7ggzy08y61_px102zx4zx40gy52 23 +253 xp253_0g8kc801z1348_84y12z48g 19 +255 xp255_0ok48acylcczy28g0gzzyb31_g02w1zy18zy41zyc4 26 +256 xp256_8y528zgz08kozx12y82_0gy31048z0gz802z01x4y71 21 +258 xp258_g80ccw2z1azyc4_cx2x1zkzyb2 18 +263 xp263_wg144z05w13x4_081w2kz2y32 17 +265 xp265_042033z01x4y02_21y01z2y02x1 15 +268 xp268_2yc2zy0124ckkc421_1yc1zx248gy0g842zy41 25 +269 xp269_gs21e4z1z01_81x12z0g 16 +270 xp270_g4a2gf5044zzp8gxgy14w2z010102_0gw1x202z0hz2y78041z02w201 37 +277 xp277_0at1ezzy369a4_1y08 17 +279 xp279_wg04kpx28gz1011_08x2x1w8z2 18 +284 xp284_gi6tz1x8_8w2zxo 15 +285 xp285_040ixggz180134c_g09y18gz04020gzyaga6zy7321 30 +288 xp288_0p1684zw3yi1zy08y65zya4_px102yegzx4zx40gy52zyb4 26 +290 xp290_14iqg44_2x1084 11 +291 xp291_01y0610f7_12x280gwa 16 +295 xp295_0g8meyng02z801yned21_g4x1ym82z082ymgx41 28 +303 xp303_0mcz02y18s2zy211_801z5y4g2zy22 20 +304 xp304_21252k2zx49ny110c_04x11xgzw8hy5izy01 27 +305 xp305_04y3648gz0gy3gg843zy51_4y51248zgy7g81zy621 25 +308 xp308_0g846k2z13c843yeogzzzzy966y44zy34_842w102z48yj4zzzzya8y32zy28 37 +311 xp311_0g86201z124pgy38zwg_842x2z48gy48zxg 24 +319 xp319_wgosqqsogz01y41z2y62_g84201w248gzz4y64 25 +320 xp320_yf68e86w68e86z4yzy54zzyf2y62_ye2w1w22w1w2z08yzy38zzyf12y421 34 +335 xp335_y6g8466648gzy1g8421y51248gz0os21yf12soz01348gydg8431zy21248gy3g8421zy71266621_y4g842w1w248gzxg8421y91248gzg21yj12gz048gyhg84zy01248gy7g8421zy5124w8w421 112 +341 xp341_0g862y22z124pgzy21_842y45z48gxg 23 +349 xp349_8g2252wkxg_g01x1w8zy71 15 +350 xp350_y0gy31y7gz8k6xe48zzx1_y08y41z21y1128y91z1zx2 24 +356 xp356_gx8c21ac8zyd2zy92_0gw21x124zyc2zy91 20 +367 xp367_w8x66y288848zya4444545z2y46a8_xg0201y2440208zyb8w8w5z01y28x8 39 +368 xp368_ym1zy1gz0okedozwhaaahz035em3zy11zymg_yl2zxgz421w11zha04z48gwggzx1zyl8 51 +384 xp384_y55og4gzay01x86328w82z0201yb104_y0gx201080gz4y4gw4w44z012yd102 35 +385 xp385_04y5648gz0gy5gg843zy71_4y71248zgy9g81zy821 25 +387 xp387_zxgz0258vy5252_33z0g8y8gz08gy88zx1 25 +388 xp388_8k6xmt2zw1221_21y282z1204w21 24 +389 xp389_0g0ca4w1zyc8zz04_w2010402z1ybgzzw4 16 +398 xp398_2x125boob521x2_1w248gy0g842w1zy41 25 +400 xp400_yt1zykgg0go48czyl121zyk4zyzy6gy3gzyzyc25d8gz8yzye32y28zy35bogzy5621y14zyzxgzzyvo8gc6164zzyt2_yu2zyjgw84y04zzyl2zyzye8zyzy71y324z04y1gyzy84y1gzy3gzy418y22zzyzw1zyugy0g8w4zzys1 76 +408 xp408_02yg1zyj8z0g8gzx352w8_w2ye2zyi4z8zw4080204 20 +420 xp420_0g04612zx26ay24_g021x1zw480gy1208 21 +437 xp437_0g86601yc4z134ogy38zy08g_842x2yb8z48gy48zy0g 28 +446 xp446_06608zy0axhzyc2_4w104zx4gzy51y11 17 +456 xp456_2x253zzzybange_4zzzyagy02 19 +473 xp473_go26y32zx570101_8y521zw28w21 20 +494 xp494_2y54ehne8a4zya1_1yb1g4zy92 21 +511 xp511_xoxmcz4fjy22gzw1310gw4_0g8y01zhy31wgz01248gx8zy21 35 +521 xp521_0s224ogz0vy1buz075y057_8x1248gz9y49z28y282 45 +527 xp527_xgw2y0ca4wgz0s4y5cxdjwuzy81y482_y08w2x1w48z4y68y21w4zw1y42y48w2 37 +548 xp548_0o42226k8z036c8c_8201w1024z280gwg 28 +554 xp554_y366068zgaxf6gz35_w33y11w4z6y0gzw2y11 26 +561 xp561_y9s22gagzy9vy0j1uzy0gy475y047ydo4ozgw1yv1z01zygacy14_y88x1048gzy89y49zy08y328y282zw1z1zyf20gy18 65 +590 xp590_wg842w12ydgz278gyi1x3248gzy013w42yhg8431zym12w1_g8421w124yegwgz8gyk21y01248zw124w421ycgy2g84zyn12w21 66 +603 xp603_0o8429248oxgz2y91zy54y92z08_0421x124y0gz02zy4202y71z048 31 +643 xp643_0os02mksywskm20soz07f0gqafywfaqg0f7_8201w12yw21w1028z5gy1gywgy1g5zx1w1yy1w1 76 +681 xp681_0g04212y0gzx24e_g021x1y0gzw480g 18 +695 xp695_0eau804fqfbzksms804uksz1101_w4xg04zw8021w8 57 +781 xp781_y62zyhgz0ooz2zy08zy01x4ige1_y61zzw4yd1z12zxkzx2y481zy51 27 +795 xp795_wgy0g1zg0gx6ary08zw3708zy91w9_y36cz080101x8wgz0c8wgzy9ca401 38 +829 xp829_1o6hl6acw1zx111_21y1gx2z01x2 21 +835 xp835_y020gp8g28zx4axc3_y128014w4zx1y0g8y266zz252 30 +921 xp921_4xkwg8ccw42w2zy32x8_22w8y24a0401zy21x4 23 +1157 xp1157_y7s020szy687x7cy6gzz36zzyf4_y64w1w4zy581y11gzgyl1z8zzye2 33 +1243 xp1243_0g862o1y72z137sy4gzx6fgzygg_842x2y624z48gy48zy0gy21zyfg 31 +1302 xp1302_0ky4g2zw26454553_62y513z148w88w841 28 +1316 xp1316_xg8425bz4cszx1258b6_w8421x8zgz01248gx1 34 +1660 xp1660_y0gx4x2y3gzw8y0gocs4wo8oz04y13hhhxhhhoy14zy4323w47631y02zy01y38x4x1_x8y02x1y48z08y3gy1gz4y6248y64zy51y11y32zx2y4gx8y02 71 +1798 xp1798_gxb124ogzy4u012zxda421_8w8x1248gzy83zw1x8421 38 +2394 xp2394_yd4y16azzzyzy866zyigggggg84yj10gz0ooye78gg888myqoo1z4zyp8ozzzyzytggzyyccwgyp118zyzyagwcc_yd2y1108zzzyzy9g8zyj8w8402ykgzyg6gy0gyo1y024z12yh1y01zzyq1zzzyz0g8yr42zyzyb8g 87 +2486 xp2486_ye1zy0g8ya8zxo8y38z4ehzx3304y72_yd2zy08ya4z0g8y4kzhz012x2y621 33 +2740 xp2740_xg8425bz6210ozy11379d_0g8421x8z1zx1248x1 35 +3155 xp3155_0b1248gzy2pc4z0da621_8x1248zy4g8z1x8421 33 +3389 xp3389_y1gg84aqqk8ggz0g8k3y66f48gz034bny7jb43zy01237agmm8421_y1804w1w408zw41y914z108yb801zx10408y18021zy61 76""" + +m0small_1 = lt.pattern("11.A$11.B$7.A$6.B2A$AB2$6.A$2.2A2.A.A$2.2A2.2A$3.B") +m0small_2 = lt.pattern("8.B$8.2A$8.2A3$10.BA$3.2AB$3.2A$B$A")(4,6) + +m0_1 = lt.pattern("13.2B$2B9.2A.B$B10.2A$.2A$.2A") +m0_2 = lt.pattern("12.A$12.A$12.2A2$13.A$2.2A10.B$B.2A9.2B$2B")(1,8) + +m1_1 = lt.pattern("12.A$12.B$8.A$7.B2A2$AB$7.A$7.A.A$2.2A3.2A$2.2A$3.B") +m1_2 = lt.pattern("9.2A$9.2AB3$8.A$8.B$3.2AB$3.2A$B$A")(5,8) + +m2small_1 = lt.pattern("5.B$A4.2A$B5.A2$A3.A$2A4.A$B5.A$3.A.2A") +m2small_2 = lt.pattern("6.B$5.2A$5.2A2$2A4.B$2A4.A$.B")(6,6) + +m2_1 = lt.pattern("2.2B$2.B$3.2A$B3.A$.3A2$.B$2.AB") +m2_2 = lt.pattern("BA$2.B2$2.A$2A.B$A$.B$2B")(16,1) + +m3_1 = lt.pattern("13.A$13.B$9.2A$8.B2A3$3.B$3.A3$B2A$.2A") +m3_2 = lt.pattern("9.A$9.A$9.2A2$10.A$11.B$10.2B$3.2AB$3.2A$B$A")(4,9) + +m4_1 = lt.pattern("13.A$13.B$9.2A$8.B2A3$3.B$3.A3$B2A$.2A") +m4_2 = lt.pattern("10.A$10.A$10.2A2$11.A$12.B$BA9.2B$4.2A$4.2A$4.B")(3,9) + +m5_1 = lt.pattern("10.B$9.2A$9.2A$13.AB2$3.B$3.A3$B2A$.2A") +m5_2 = lt.pattern("10.A$10.A$10.2A2$11.A$12.B$BA9.2B$4.2A$4.2A$4.B")(4,9) + +m6_1 = lt.pattern("8.B$7.2A$7.2A$2B9.AB$B$.2A$.2A") +m6_2 = lt.pattern("10.A$10.A$10.2A2$11.A$12.B$BA9.2B$4.2A$4.2A$4.B")(2,9) + +m7_1 = lt.pattern("13.2B$2B9.2A.B$B10.2A$.2A$.2A") +m7_2 = lt.pattern("10.A$10.A$10.2A2$11.A$12.B$BA9.2B$4.2A$4.2A$4.B")(3,8) + +def universal_reflector_loop(p): + n, r = divmod(p, 8) + if r == 0: + if p < 56: + return None + elif p < 144: + return (m0small_1 + m0small_2(n-7,n-7), 32) + return (m0_1 + m0_2(n-18,n-18), 29) + elif r == 1: + if p < 65: + return None + return (m1_1 + m1_2(n-8,n-8), 32) + elif r == 2: + # there's a p18+8n shuttle akin to Elkies's Life reflector, + # but it's always beaten by fixed oscillators before p106 + if p < 106: + return None + return (m2_1 + m2_2(n-13,n-13), 24) + elif r == 3: + if p < 91: + return None + return (m3_1 + m3_2(n-11,n-11), 29) + elif r == 4: + if p < 84: + return None + return (m4_1 + m4_2(n-10,n-10), 29) + elif r == 5: + if p < 85: + return None + return (m5_1 + m5_2(n-10,n-10), 29) + elif r == 6: + if p < 102: + return None + return (m6_1 + m6_2(n-12,n-12), 29) + else: + if p < 127: + return None + return (m7_1 + m7_2(n-15,n-15), 29) + +cfuncs = (universal_reflector_loop,)