Code: Select all
import lifelib
conwayLifeTree = lifelib.load_rules("b3s23").lifetree()
gpseLifelib = conwayLifeTree.pattern("bo$2o$obo62$65bo$64b2o$64bobo23$171bo$170bobo2$170bo2bo$172b2o$173bo2$187bo$182b2o3bo$182b2o3bo$170b2o13b2o$172bo9b2ob2o$171b2o10bo$170b3o$170b2o$169b3obo$170bo3bo$172bo2bo$172b3o$173b2o3$203b2o$203b2o3$192bo$191bobo$191b2o2$211b2o$211b2o3$183b2o$183b2o4$129bo$128b2o77b2o$128bobo75bo2b2o7b2o$205b6o7bobo$206b2obo9bo$208bo$197bo9bo$196bobo8b3o$196bo2bo5bo3b2o$197b2o6bo2b3ob2o$191bo13bo2bo3b2o$187bo2b3o13b2o2bo2bo$185bobo3b2o15b2o2bo$185bobo20b4o$184b2obobo$185bo3b2o44b2o$186bo4b2o42b2o2$187bo5bo$224bo$187bo4bo30bobo$187bo2bo19bo12b2o30b2o$188b3o18bobo42bo2bo$209b2o44b2o4$219bo$218bobo$218b2o3$249bo$248bobo$248bo2bo$249b2o5$219b2o$218bo2bo$219b2o2$215b3o2$219bo$219bo$219bo2$221b2o$221b2o$231b3o$287b2o$286bo2bo$287b2o$240bo$240bo$240bo$251bo$250bobo$250b2o2$213b2o$213b2o66bo$280bobo$280bo2bo$281b2o4$209b2o$209b2o40b2o$250bo2bo$251b2o2$247b3o2$251bo$251bo$251bo$219b2o$218bobo32b2o$218b2o33b2o$263b3o$319b2o$318bo2bo$319b2o$272bo$272bo$272bo$283bo$282bobo$282b2o2$245b2o$245b2o66bo$312bobo$312bo2bo$313b2o4$241b2o$241b2o40b2o$282bo2bo$283b2o2$279b3o2$283bo$283bo$283bo$251b2o$250bobo32b2o$250b2o33b2o$295b3o4$304bo$304bo$304bo5$277b2o$277b2o7$273b2o$273b2o9$283b2o$282bobo$282b2o!")
def test_sending_glider(laneDisplacement, timeOffset):
testBoard = gpseLifelib[timeOffset].__or__(conwayLifeTree.pattern("obo$b2o$bo").shift(0, 1-laneDisplacement))
sidewaysSpaceship = False
generationsElapsed = timeOffset + 1000
lastLocalDifference = frozenset(tuple(differentPoint) for differentPoint in gpseLifelib[generationsElapsed].__xor__(testBoard[generationsElapsed-timeOffset]).coords() if abs(differentPoint[0]+differentPoint[1]-400)<=400 and abs(differentPoint[0]-differentPoint[1])<=400)
generationsElapsed += 30
currentSymmetricDifference = frozenset(tuple(differentPoint) for differentPoint in gpseLifelib[generationsElapsed].__xor__(testBoard[generationsElapsed-timeOffset]).coords())
currentLocalDifference = frozenset(tuple(differentPoint) for differentPoint in currentSymmetricDifference if abs(differentPoint[0]+differentPoint[1]-400)<=400 and abs(differentPoint[0]-differentPoint[1])<=400)
while currentLocalDifference != lastLocalDifference:
if any(abs(differentPoint[0]-differentPoint[1])>400 for differentPoint in currentSymmetricDifference):
sidewaysSpaceship = True
break
else:
lastLocalDifference = currentLocalDifference.copy()
generationsElapsed += 30
currentSymmetricDifference = frozenset(tuple(differentPoint) for differentPoint in gpseLifelib[generationsElapsed].__xor__(testBoard[generationsElapsed-timeOffset]).coords())
currentLocalDifference = frozenset(tuple(differentPoint) for differentPoint in currentSymmetricDifference if abs(differentPoint[0]+differentPoint[1]-400)<=400 and abs(differentPoint[0]-differentPoint[1])<=400)
if sidewaysSpaceship or any(abs(differentPoint[0]-differentPoint[1])>400 for differentPoint in currentSymmetricDifference):
return "X" # Escaping sideways gliders (or XWSSes) render the collision unusable.
else:
generationsElapsed += (-generationsElapsed)%256
removedEscapingCells = frozenset(tuple(removedCell) for removedCell in (gpseLifelib[generationsElapsed]-testBoard[generationsElapsed-timeOffset]).coords() if removedCell[0]+removedCell[1]<0)
escapingSymmetricDifference = frozenset(tuple(differentPoint) for differentPoint in gpseLifelib[generationsElapsed].__xor__(testBoard[generationsElapsed-timeOffset]).coords() if differentPoint[0]+differentPoint[1]<0) # Because of the nature of the sets, the symmetric difference could also be used.
unaccountedRemovedCells = set(removedEscapingCells.copy())
unaccountedEscapingDifference = set(escapingSymmetricDifference.copy())
gliderDifferenceDict = {"created": set(), "destroyed": 0}
normalCells = frozenset(tuple(normalCell) for normalCell in gpseLifelib[generationsElapsed].coords())
for missingCellPosition in removedEscapingCells: # detects missing gliders
if missingCellPosition[0]-missingCellPosition[1]==1 and (missingCellPosition[0]+missingCellPosition[1])%128==1 and missingCellPosition in unaccountedRemovedCells and {(missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]-1, missingCellPosition[1]+2), (missingCellPosition[0]+1, missingCellPosition[1]+2)}<=normalCells:
gliderDifferenceDict["destroyed"] += 1
unaccountedRemovedCells -= {missingCellPosition, (missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]-1, missingCellPosition[1]+2), (missingCellPosition[0]+1, missingCellPosition[1]+2)}
unaccountedEscapingDifference ^= {missingCellPosition, (missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]-1, missingCellPosition[1]+2), (missingCellPosition[0]+1, missingCellPosition[1]+2)} # It uses the symmetric difference instead of simply a difference in case a created glider partially overlaps a removed glider.
elif missingCellPosition[1]-missingCellPosition[0]==1 and (missingCellPosition[0]+missingCellPosition[1])%128==1 and missingCellPosition in unaccountedRemovedCells and {(missingCellPosition[0]+1, missingCellPosition[1]-1), (missingCellPosition[0]+1, missingCellPosition[1]), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]+2, missingCellPosition[1]+1)}<=normalCells:
gliderDifferenceDict["destroyed"] += 1
unaccountedRemovedCells -= {(missingCellPosition[0]+1, missingCellPosition[1]-1), missingCellPosition, (missingCellPosition[0]+1, missingCellPosition[1]), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]+2, missingCellPosition[1]+1)}
unaccountedEscapingDifference ^= {(missingCellPosition[0]+1, missingCellPosition[1]-1), missingCellPosition, (missingCellPosition[0]+1, missingCellPosition[1]), (missingCellPosition[0], missingCellPosition[1]+1), (missingCellPosition[0]+2, missingCellPosition[1]+1)}
elif missingCellPosition[0]==missingCellPosition[1] and (missingCellPosition[0]+missingCellPosition[1])%128==2 and missingCellPosition in unaccountedRemovedCells and {(missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]), (missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0]+1, missingCellPosition[1]+1)}<=normalCells:
gliderDifferenceDict["destroyed"] += 1
unaccountedRemovedCells -= {(missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]), missingCellPosition, (missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0]+1, missingCellPosition[1]+1)}
unaccountedEscapingDifference ^= {(missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]), missingCellPosition, (missingCellPosition[0]-1, missingCellPosition[1]+1), (missingCellPosition[0]+1, missingCellPosition[1]+1)}
elif missingCellPosition[1]-missingCellPosition[0]==2 and (missingCellPosition[0]+missingCellPosition[1])%128==2 and missingCellPosition in unaccountedRemovedCells and {(missingCellPosition[0]+1, missingCellPosition[1]-2), (missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]+1, missingCellPosition[1]-1), (missingCellPosition[0]+2, missingCellPosition[1])}<=normalCells:
gliderDifferenceDict["destroyed"] += 1
unaccountedRemovedCells -= {(missingCellPosition[0]+1, missingCellPosition[1]-2), (missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]+1, missingCellPosition[1]-1), missingCellPosition, (missingCellPosition[0]+2, missingCellPosition[1])}
unaccountedEscapingDifference ^= {(missingCellPosition[0]+1, missingCellPosition[1]-2), (missingCellPosition[0], missingCellPosition[1]-1), (missingCellPosition[0]+1, missingCellPosition[1]-1), missingCellPosition, (missingCellPosition[0]+2, missingCellPosition[1])}
elif missingCellPosition[0]==missingCellPosition[1] and (missingCellPosition[0]+missingCellPosition[1])%128==4 and missingCellPosition in unaccountedRemovedCells and {(missingCellPosition[0]-1, missingCellPosition[1]-2), (missingCellPosition[0]-2, missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]-1), (missingCellPosition[0]-2, missingCellPosition[1])}<=normalCells:
gliderDifferenceDict["destroyed"] += 1
unaccountedRemovedCells -= {(missingCellPosition[0]-1, missingCellPosition[1]-2), (missingCellPosition[0]-2, missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]-1), (missingCellPosition[0]-2, missingCellPosition[1]), missingCellPosition}
unaccountedEscapingDifference ^= {(missingCellPosition[0]-1, missingCellPosition[1]-2), (missingCellPosition[0]-2, missingCellPosition[1]-1), (missingCellPosition[0]-1, missingCellPosition[1]-1), (missingCellPosition[0]-2, missingCellPosition[1]), missingCellPosition}
if bool(unaccountedRemovedCells): # returns true if and only if the set is not empty
print("\n")
return "?1" # This line reports that the program was unable to identify the difference. It probably means that the bounds for checking for either escaping spaceships or when the reaction has settled should be changed.
else: # There would be a line unaccountedAddedCells=unaccountedRemovedCells^unaccountedEscapingDifference (taking the symmetric difference), but it's not necessary because this section will only execute if unaccountedRemovedCells is empty, so the program can use unaccountedEscapingDifference instead of unaccountedAddedCells.
for addedCellPosition in unaccountedEscapingDifference.copy(): # The .copy() is necessary in order to prevent modifying a set while looping through it.
if {addedCellPosition, (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]-1, addedCellPosition[1]+2), (addedCellPosition[0]+1, addedCellPosition[1]+2)} <= unaccountedEscapingDifference:
gliderDifferenceDict["created"].add((addedCellPosition[0]-addedCellPosition[1]-1, 4*addedCellPosition[0]-4))
unaccountedEscapingDifference -= {addedCellPosition, (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]-1, addedCellPosition[1]+2), (addedCellPosition[0]+1, addedCellPosition[1]+2)}
elif {addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]+2, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+2)} <= unaccountedEscapingDifference:
gliderDifferenceDict["created"].add((addedCellPosition[0]-addedCellPosition[1], 4*addedCellPosition[0]-1))
unaccountedEscapingDifference -= {addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]+2, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+2)}
elif {addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]+1, addedCellPosition[1]+2)} <= unaccountedEscapingDifference:
gliderDifferenceDict["created"].add((addedCellPosition[0]-addedCellPosition[1], 4*addedCellPosition[0]-2))
unaccountedEscapingDifference -= {addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+1), (addedCellPosition[0]+1, addedCellPosition[1]+2)}
elif {(addedCellPosition[0]-1, addedCellPosition[1]), addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+2)} <= unaccountedEscapingDifference:
gliderDifferenceDict["created"].add((addedCellPosition[0]-addedCellPosition[1], 4*addedCellPosition[0]-3))
unaccountedEscapingDifference -= {(addedCellPosition[0]-1, addedCellPosition[1]), addedCellPosition, (addedCellPosition[0]+1, addedCellPosition[1]), (addedCellPosition[0]-1, addedCellPosition[1]+1), (addedCellPosition[0], addedCellPosition[1]+2)}
if bool(unaccountedEscapingDifference): # returns true if and only if the set is not empty
return "?2" # As before, this line reports that the program was unable to identify the difference and probably means that the bounds for checking for either escaping spaceships or when the reaction has settled should be changed.
else:
gliderDifferenceDict["backwards gliders"] = any(testCell[0]+testCell[1]>800 for testCell in testBoard[generationsElapsed-timeOffset].coords())
return gliderDifferenceDict
print({(laneOffset, frozenset((generationsToWait, generationsToWait+256))):frozenset((str(test_sending_glider(laneOffset, generationsToWait)), str(test_sending_glider(laneOffset, generationsToWait+256)))) for laneOffset in range(7, 8) for generationsToWait in range(256)})
This took slightly over half of a minute to run on my computer, so the complete search (range(7, 45) instead of range(7, 8
)) should take about twenty minutes. I've already started the complete search, and once it's done, we'll hopefully have the necessary information to develop a 3-GPSE RCT-based universal constructor.
By the way, during debugging, I realized that I forgot to break out of a while loop when the program detected escaping sideways spaceships. If I had remembered to do that (and fixed the bugs that would have occurred had the program gotten to them), then the program wouldn't have been as slow as I thought that it was, but I'm sure that using lifelib is faster.
Edit: After about eighteen minutes and five rounds of garbage collection, Python has returned this:
Now we need to analyze it in order to find out which pairs of collisions will be the most useful. All of the complete ideas for a 3-GPSE RCT-based universal constructor
here have GPSEs A and C feeding into each other, so we should focus on pairs of signal pairs that have the same lane displacement and whose time offsets add up to a multiple of eight.
Another edit: Here are the results in a format that is probably easier to work with.
Now that the search is done, a 2-GPSE solution seems more plausible to me, so I'll look for that first.
Yet another edit: I ran the following program to search for signal pairs that could have worked for my 2-GPSE idea. Here is the specific code:
Code: Select all
from pickle import load
fileObject = open("signalReflectionResults.pydata", "rb")
searchResults = load(fileObject)
fileObject.close()
print(searchResults)
for laneOffset in range(7, 45):
for generationOffset in range(256):
relevantResult = searchResults[(laneOffset, frozenset((generationOffset, generationOffset+256)))]
if relevantResult[0]!="X" and relevantResult[1]!="X" and relevantResult[0]!="?1" and relevantResult[1]!="?1" and relevantResult[0]!="?2" and relevantResult[1]!="?2" and relevantResult[0]["created"] and relevantResult[0]["destroyed"] and relevantResult[1]["created"] and relevantResult[1]["destroyed"] and set((createdGliderTuple[0], createdGliderTuple[1]%2) for createdGliderTuple in relevantResult[0]["created"])!=set((createdGliderTuple[0], createdGliderTuple[1]%2) for createdGliderTuple in relevantResult[1]["created"]):
print({(laneOffset, frozenset((generationOffset, generationOffset+256))): relevantResult})
It didn't print any possibilities, which surprised me. (There were nineteen cases where the program returned a question mark, indicating that it could not completely identify the differences, but they're probably either escaping XWSSes or involve the GPSE getting destroyed, turned into a BLSE, or made to produce gliders on the other side.) However, I know that it's possible to increase the lane displacement at least a little more without destroying the GPSE, so I replaced the end of my program with the following code and ran it.
Code: Select all
for laneOffset in range(45, 50):
for generationOffset in range(256):
relevantResult = (test_sending_glider(laneOffset, generationOffset), test_sending_glider(laneOffset, generationOffset+256))
if relevantResult[0]!="X" and relevantResult[1]!="X" and relevantResult[0]!="?1" and relevantResult[1]!="?1" and relevantResult[0]!="?2" and relevantResult[1]!="?2" and relevantResult[0]["created"] and relevantResult[0]["destroyed"] and relevantResult[1]["created"] and relevantResult[1]["destroyed"] and set((createdGliderTuple[0], createdGliderTuple[1]%2) for createdGliderTuple in relevantResult[0]["created"])!=set((createdGliderTuple[0], createdGliderTuple[1]%2) for createdGliderTuple in relevantResult[1]["created"]):
print({(laneOffset, frozenset((generationOffset, generationOffset+256))): relevantResult})
This printed the following:
Code: Select all
{(48, frozenset({359, 103})): ({'created': {(206, -389), (-14, -1467)}, 'destroyed': 1, 'backwards gliders': True}, {'created': {(-54, -384)}, 'destroyed': 1, 'backwards gliders': False})}
Unforutnately, I think that glider lanes 40 half-diagonals apart will be too far apart to allow universal construction (although if they are, please let me know). Unless I'm interpreting this incorrectly, or there's a bug in my code (and I doubt that either is the case), I suspect that this means that a 2-GPSE RCT-based universal constructor, at least following the only idea for one that I've had so far, is impossible.
Fourth edit: I decided to run the program for lane displacements in range(50, 55). The program printed {(52, frozenset({185, 441})): ({'created': {(34, -467)}, 'destroyed': 2, 'backwards gliders': False}, {'created': {(149, -57)}, 'destroyed': 2, 'backwards gliders': False})}, which also doesn't seem conducive to unversal construction, but I noticed that running the program produced a lot of blank lines (including one group of over 60 in a row) in my terminal. This had happened before, but only this time did the possibility occur to me that they might be more results that didn't appear due to some bug. I think that this is unlikely, but I'd like confirmation that lifelib could do this just to make sure that we're not throwing away any perfectly valid 2-GPSE RCT-based universal constructor possibilities.