The p1024 was a lot faster, I don't remember the exact time but with 4GB allotted it was more on the order of hours than days. The HashLife friendliness of p1024 helps a lot.
In cases where the period isn't divisible by 8, it's possible to remove some of the ships since we can create more different phases of ship with the same recipe by constructing it later on in a different phase. This allows for periods 943 and 945+, and also should make the oscillators smaller in the non-multiple-of-8 case. Here's an updated script:
Code: Select all
import golly as g
from glife import *
from time import time
import copy
periodString = g.getstring("Enter period either 943 or greater than or equal to 945", "943")
goalPeriod = 943
if not validint(periodString):
g.exit("Not a valid integer")
goalPeriod = int(periodString)
numPhaseTypes = 1 if goalPeriod % 2 == 1 else (2 if goalPeriod % 4 == 2 else (4 if goalPeriod % 8 == 4 else 8))
if goalPeriod - numPhaseTypes < 941:
g.exit("Does not work for this period")
startTime = time()
g.new("P" + str(goalPeriod) + " Strictly Volatile Loop")
g.setalgo("HashLife")
g.setrule("B3/S23")
block = pattern("2o$2o!")
glider = pattern("bo$2o$obo!") #NW-facing
#W-facing
convoyUnitWest0 = pattern("72b5o$72bo4bo$72bo$73bo3bo$75bo3$61b4o$61bo3bo$42b2o17bo$41b2ob3o15bo2bo$42b5o$43b3o5$4b2o$2bo4bo3b2o56b6o$bo8b2ob3o53bo5bo$bo5bo3b5o53bo$b6o5b3o55bo4bo$72b2o6$3bo$bo3bo$o$o4bo$5o3$11b3o25b6o$10b5o24bo5bo$9b2ob3o5b5o14bo$10b2o8bo4bo14bo4bo$20bo21b2o$21bo3bo$23bo!", 0, -17)
convoyUnitWest1 = pattern("59b5o24b4o$59bo4bo22b6o$59bo26b2ob4o$60bo3bo22b2o$62bo4$72b5o$72bo4bo$72bo$73bo3bo$75bo3$61b4o$61bo3bo$61bo$62bo2bo7$4b2o23b2o$2bo4bo3b2o15b2ob2o36b6o$bo8b2ob3o13b4o36bo5bo$bo5bo3b5o14b2o37bo$b6o5b3o55bo4bo$72b2o6$3bo$bo3bo$o$o4bo$5o3$11b3o17b6o$10b5o16bo5bo$9b2ob3o5b5o6bo$10b2o8bo4bo6bo4bo14b4o$20bo13b2o16bo3bo$21bo3bo26bo$23bo29bo2bo!", 0, -25)
convoyUnitEast0 = pattern("72b5o$72bo4bo$72bo$73bo3bo$75bo3$61b4o$61bo3bo$61bo$62bo2bo7$4b2o$2bo4bo3b2o56b6o$bo8b2ob3o53bo5bo$bo5bo3b5o53bo$b6o5b3o55bo4bo$72b2o6$3bo$bo3bo$o$o4bo$5o$39b2o$37bo4bo$11b3o22bo$10b5o21bo5bo$9b2ob3o5b5o11b6o$10b2o8bo4bo$20bo29b5o$21bo3bo24bo4bo$23bo26bo$51bo3bo$53bo!", 0, -17)
convoyUnitEast1 = pattern("72b5o$72bo4bo$72bo$73bo3bo$75bo3$61b4o$61bo3bo$61bo$62bo2bo7$4b2o14b4o$2bo4bo3b2o7bo3bo44b6o$bo8b2ob3o4bo48bo5bo$bo5bo3b5o5bo2bo44bo$b6o5b3o55bo4bo$72b2o6$3bo$bo3bo$o$o4bo$5o3$11b3o23b6o$10b5o22bo5bo$9b2ob3o5b5o12bo$10b2o8bo4bo12bo4bo$20bo19b2o$21bo3bo4bo2bo$23bo5bo$29bo3bo$29b4o!", 0, -17)
lwssRecipe = [[[[0,0]],0],[[[-9,0]],0],[[[-2,0]],1],[[[11,0]],1],[[[12,0]],0],[[[16,0]],0],[[[8,49],[11,33],[2,0]],0]]
mwssRecipe = [[[[9,0]],0],[[[18,0]],0],[[[9,0]],0],[[[13,0]],0],[[[20,0]],0],[[[20,0]],1],[[[19,0]],-1],[[[15,0]],1],[[[17,0]],-1],[[[8,0]],2],[[[12,0]],0],[[[14,0]],0],[[[17,0]],2],[[[11,0],[18,15]],0]]
hwssRecipeHighClearance = [[[[0,0]],0],[[[8,0]],0],[[[1,0]],1],[[[16,0]],1],[[[-5,0]],-1],[[[-14,0]],1],[[[-17,0]],1],[[[-16,0]],0],[[[-13,0]],2],[[[-5,0]],-2],
[[[-26,0]],0],[[[-24,0]],2],[[[-10,0]],-2],[[[-4,0]],0],[[[-3,0]],0],[[[0,0]],0],[[[-3,0]],3],[[[-4,75],[-18,41],[7,41],[-6,0]],-3]]
#determines the distance between furthest lanes
def getRecipeWidth(recipeList):
minPoint = recipeList[0][0][0][0]
maxPoint = minPoint
for i in range(1, len(recipeList)):
for j in recipeList[i][0]:
if minPoint > j[0]:
minPoint = j[0]
if maxPoint < j[0]:
maxPoint = j[0]
return maxPoint - minPoint
maxRecipeWidth = max(getRecipeWidth(lwssRecipe), getRecipeWidth(mwssRecipe), getRecipeWidth(hwssRecipeHighClearance))
convoyUnitPart2Offset = max(0, maxRecipeWidth - 48)
#adjusts xWSS recipe positions
convoyDataAdjustments = [[0, 0, 0], [118, 3, 0], [279, 3, 0]]
def adjustConvoyData(convoyData):
newConvoyData = []
for xWSSData in convoyData:
newXWSSData = [xWSSData[0], xWSSData[1] + convoyDataAdjustments[xWSSData[0]][0], xWSSData[2] + convoyDataAdjustments[xWSSData[0]][1] - 100, (xWSSData[3] + convoyDataAdjustments[xWSSData[0]][2]) % 4]
if xWSSData[3] + convoyDataAdjustments[xWSSData[0]][2] >= 4:
#rollover
newXWSSData[1] += 2
newConvoyData.append(newXWSSData)
return newConvoyData
convoyUnitDataWest0 = adjustConvoyData([[1, 0, 0, 0], [1, 29, -9, 3], [0, 11, -7, 0], [2, 70, -19, 2], [1, 60, -18, 3], [2, 3, -18, 0], [1, 71, -30, 2], [1, 62, -36, 1], [1, 52, -37, 0], [2, 33, -35, 0]])
convoyUnitDataWest1 = adjustConvoyData([[1, 13, 8, 0], [2, -15, 7, 1], [1, 0, 0, 0], [0, 11, -7, 0], [2, 70, -19, 2], [1, 60, -18, 3], [0, 42, -17, 3], [2, 3, -18, 0], [1, 71, -30, 2], [1, 62, -36, 1], [1, 52, -37, 0], [2, 41, -35, 0], [0, 20, -38, 0]])
convoyUnitDataEast0 = adjustConvoyData([[1, 0, 0, 0], [0, 11, -7, 0], [2, 70, -19, 2], [1, 60, -18, 3], [2, 3, -18, 0], [1, 71, -30, 2], [1, 62, -36, 1], [1, 52, -37, 0], [2, 35, -35, 2], [1, 22, -39, 0]])
convoyUnitDataEast1 = adjustConvoyData([[1, 0, 0, 0], [0, 11, -7, 0], [2, 70, -19, 2], [1, 60, -18, 3], [0, 52, -17, 0], [2, 3, -18, 0], [1, 71, -30, 2], [1, 62, -36, 1], [1, 52, -37, 0], [2, 35, -35, 0], [0, 42, -41, 2]])
#equality check
def equals(patternA, patternB):
return g.evolve(patternA, 0) == g.evolve(patternB, 0)
#find glider locations that are too close to be generated (up to a maximum lane offset)
def gliderLocationsTooClose(testRange):
gliderPositionBlacklist = []
for nextGliderGen in range(4):
gliderPositionBlacklist.append({})
for x in range(-testRange, testRange+1):
gliderPositionBlacklist[nextGliderGen][x] = set()
for y in range(-testRange, testRange+1):
g.show("Blacklisting gliders: Gen " + str(nextGliderGen) + ", x = " + str(x) + ", y = " + str(y))
testForGliderClosenessPattern = convoyUnitWest0[2](-30 + convoyUnitPart2Offset, 16 - convoyUnitPart2Offset)
testForGliderClosenessPattern += block[0](-31 + convoyUnitPart2Offset, 24 - convoyUnitPart2Offset)
#this will generate a new glider at (0,0) at gen 160 + convoyUnitPart2Offset * 4
gensToMakeGlider = 160 + convoyUnitPart2Offset * 4
testForGliderClosenessPattern += glider[nextGliderGen](x + gensToMakeGlider // 4, y + gensToMakeGlider // 4)
testForGliderClosenessGoal = convoyUnitWest0[2](-30 + convoyUnitPart2Offset - gensToMakeGlider, 16 - convoyUnitPart2Offset)
testForGliderClosenessGoal += block[0](-31 + convoyUnitPart2Offset, 24 - convoyUnitPart2Offset)
testForGliderClosenessGoal += glider[0](-(gensToMakeGlider // 4), -(gensToMakeGlider // 4))
testForGliderClosenessGoal += glider[nextGliderGen](x - (gensToMakeGlider // 4), y - (gensToMakeGlider // 4))
if not equals(testForGliderClosenessPattern[gensToMakeGlider * 2], testForGliderClosenessGoal):
#glider cannot be placed here, so add position to blacklist
gliderPositionBlacklist[nextGliderGen][x].add(y)
return gliderPositionBlacklist
gliderRelativePositionBlacklist = []
#makes a maximally compacted recipe demonstration, assuming a given blacklist
def makeCompactRecipeWithBlacklist(recipeList, finalParity = -1):
finalParityIndicator = recipeList[len(recipeList)-1][1] #the parity type (either a positive, a negative, or 0) of the final gliderset
recipe = block[0](1,-3)
parities = {}
if finalParityIndicator != 0 and finalParity != -1:
parities[abs(finalParityIndicator)] = finalParity if finalParityIndicator > 0 else 1 - finalParity
recipeCurrentState = recipe
recipeCurrentGen = 0
recipeGliders = []
for i in range(len(recipeList)):
g.show("Generating salvo: Gliderset " + str(i))
#do we currently know the parity to use
parityKnown = False
parity = 0
if i == len(recipeList) - 1 and finalParity != -1:
#parity specified by finalParity
parityKnown = True
parity = finalParity
elif abs(recipeList[i][1]) in parities:
#parity of this glider is already specified
parityKnown = True
parity = parities[abs(recipeList[i][1])] if recipeList[i][1] > 0 else 1 - parities[abs(recipeList[i][1])]
#figure out what the interaction should look like
recipeNextStateTemplate = pattern(recipeCurrentState)
#minimum distance not touching bounding box
boundingBox = getminbox(recipeNextStateTemplate)
diagonalBoundingBoxOffset = max(boundingBox[0] + boundingBox[2] + 32, boundingBox[1] + boundingBox[3] + 32)
currentParity = (parity + recipeCurrentGen) % 2
for gliderData in recipeList[i][0]:
recipeNextStateTemplate += glider[currentParity + gliderData[1]](diagonalBoundingBoxOffset + gliderData[0], diagonalBoundingBoxOffset)
recipeNextStateTemplate = (recipeNextStateTemplate[512] + recipeNextStateTemplate[513])[0]
gensAdvancedFromTemplatePosition = 0
#test advancing the new glider's start position to see where it stops matching up
while True:
gensAdvancedFromTemplatePosition += 2 if parityKnown else 1
#glider, advanced recipeCurrentGen gens, should be gensAdvancedFromTemplatePosition ahead of the template glider
thisGliderSetGen = ((currentParity + gensAdvancedFromTemplatePosition - recipeCurrentGen) % 4 + 4) % 4
thisGliderSetAdvancement = (currentParity + gensAdvancedFromTemplatePosition - recipeCurrentGen) // 4
thisGliderSetOffset = -thisGliderSetAdvancement + diagonalBoundingBoxOffset
gliderBlacklisted = False
for gliderData in recipeList[i][0]:
thisGliderInGlidersetGen = ((currentParity + gensAdvancedFromTemplatePosition + gliderData[1] - recipeCurrentGen) % 4 + 4) % 4
thisGliderInGlidersetAdvancement = (currentParity + gensAdvancedFromTemplatePosition + gliderData[1] - recipeCurrentGen) // 4
thisGliderInGlidersetOffset = -thisGliderInGlidersetAdvancement + diagonalBoundingBoxOffset
for oldGliderData in recipeGliders:
#get modified new glider data for when gliderData is moved to (0,0) and state 0
modifiedGliderGen = (thisGliderInGlidersetGen + 4 - oldGliderData[2]) % 4
offsetFromGens = 1 if thisGliderInGlidersetGen < oldGliderData[2] else 0
modifiedGliderX = thisGliderInGlidersetOffset + gliderData[0] - oldGliderData[0] + offsetFromGens
modifiedGliderY = thisGliderInGlidersetOffset - oldGliderData[1] + offsetFromGens
if modifiedGliderX in gliderRelativePositionBlacklist[modifiedGliderGen]:
if modifiedGliderY in gliderRelativePositionBlacklist[modifiedGliderGen][modifiedGliderX]:
gliderBlacklisted = True
break
if gliderBlacklisted:
break
if gliderBlacklisted:
#this doesn't match, so the previous one is valid
gensAdvancedFromTemplatePosition -= 2 if parityKnown else 1
break
recipeNextStateStart = pattern(recipe)
for gliderData in recipeList[i][0]:
recipeNextStateStart += glider[thisGliderSetGen + gliderData[1]](thisGliderSetOffset + gliderData[0], thisGliderSetOffset)
recipeNextStateStart = recipeNextStateStart[recipeCurrentGen + 512 - gensAdvancedFromTemplatePosition]
#matches allow for phase shifted versions
if not equals(recipeNextStateStart[0] + recipeNextStateStart[1], recipeNextStateTemplate):
#this doesn't match, so the previous one is valid
gensAdvancedFromTemplatePosition -= 2 if parityKnown else 1
break
gliderGen = ((currentParity + gensAdvancedFromTemplatePosition - recipeCurrentGen) % 4 + 4) % 4
gliderAdvancement = (currentParity + gensAdvancedFromTemplatePosition - recipeCurrentGen) // 4
gliderOffset = -gliderAdvancement + diagonalBoundingBoxOffset
for gliderData in recipeList[i][0]:
recipe += glider[gliderGen + gliderData[1]](gliderOffset + gliderData[0], gliderOffset)
recipeGliders.append([gliderOffset + gliderData[0] - (gliderGen + gliderData[1]) // 4, gliderOffset - (gliderGen + gliderData[1]) // 4, (gliderGen + gliderData[1]) % 4])
recipeCurrentState = pattern(recipe[0])
recipeCurrentGen = 0
while True:
if equals(recipeCurrentState[0] + recipeCurrentState[1], recipeNextStateTemplate):
#we have a match
break
recipeCurrentGen += 1
recipeCurrentState = recipeCurrentState[1]
if recipeList[i][1] != 0 and not abs(recipeList[i][1]) in parities:
#add new parity specification
parities[abs(recipeList[i][1])] = gliderGen % 2 if recipeList[i][1] > 0 else 1 - gliderGen % 2
return recipeGliders
#gets all the slow salvo recipes for a xwss
def getRecipesWithActivationTimes(recipeList, minExtraActivationTime):
finalParityIndicator = recipeList[len(recipeList)-1][1]
requireSeparateRecipeForParity = False
if recipeList[len(recipeList)-1][1] != 0:
for i in range(len(recipeList) - 1):
if abs(recipeList[i][1]) == abs(recipeList[len(recipeList)-1][1]):
requireSeparateRecipeForParity = True
break
finalGliderSetSize = len(recipeList[len(recipeList)-1][0])
output = []
if requireSeparateRecipeForParity:
#requires two separate recipes, one for each parity, so things are a bit more complicated
recipe0 = makeCompactRecipeWithBlacklist(recipeList, 0)
recipe1 = makeCompactRecipeWithBlacklist(recipeList, 1)
if recipe0[len(recipe0) - 1][0] < recipe1[len(recipe1) - 1][0] or (recipe0[len(recipe0) - 1][0] == recipe1[len(recipe1) - 1][0] and recipe0[len(recipe0) - 1][2] > recipe1[len(recipe1) - 1][2]):
#recipe0 glider is faster
output.append(recipe0)
if numPhaseTypes >= 2:
output.append(recipe1)
else:
#recipe1 glider is faster
output.append(recipe1)
if numPhaseTypes >= 2:
output.append(recipe0)
for i in range(2, numPhaseTypes):
output.append(copy.deepcopy(output[i - 2]))
for j in range(finalGliderSetSize):
output[i][len(output[i]) - 1 - j][2] -= 2
if output[i][len(output[i]) - 1 - j][2] < 0:
output[i][len(output[i]) - 1 - j][2] += 4
output[i][len(output[i]) - 1 - j][0] += 1
output[i][len(output[i]) - 1 - j][1] += 1
else:
#simpler case where the same recipe works for both parities
output.append(makeCompactRecipeWithBlacklist(recipeList))
for i in range(1, numPhaseTypes):
output.append(copy.deepcopy(output[i - 1]))
for j in range(finalGliderSetSize):
output[i][len(output[i]) - 1 - j][2] -= 1
if output[i][len(output[i]) - 1 - j][2] < 0:
output[i][len(output[i]) - 1 - j][2] += 4
output[i][len(output[i]) - 1 - j][0] += 1
output[i][len(output[i]) - 1 - j][1] += 1
#add extra activation time to each salvo if needed
if minExtraActivationTime > 0:
newOutput = []
for i in range(len(output)):
#draw from (i + minExtraActivationTime) % numPhaseTypes
newOutput.append(output[(i + minExtraActivationTime) % numPhaseTypes])
if i + minExtraActivationTime >= numPhaseTypes:
#move final gliders back the required amount
for j in range(finalGliderSetSize):
numStepsToMoveBack = numPhaseTypes * ((i + minExtraActivationTime) // numPhaseTypes)
newOutput[i][len(newOutput[i]) - 1 - j][2] -= numStepsToMoveBack
while newOutput[i][len(newOutput[i]) - 1 - j][2] < 0:
newOutput[i][len(newOutput[i]) - 1 - j][2] += 4
newOutput[i][len(newOutput[i]) - 1 - j][0] += 1
newOutput[i][len(newOutput[i]) - 1 - j][1] += 1
output = newOutput
#make other-color versions
for i in range(numPhaseTypes, numPhaseTypes * 2):
output.append(copy.deepcopy(output[i - numPhaseTypes]))
for j in range(len(output[i])):
output[i][j][0] += 1
return output
#makes a demonstration convoy for a single salvo
def makeConvoySupportingSalvo(recipeList):
smallConvoy = block[0](51 - 32, -32)
for i in range(len(recipeList)):
gliderData = recipeList[i]
blockX = i * 64 + gliderData[0] - gliderData[1]
blockY = i * 64 + 8
smallConvoy += block[0](blockX, blockY)
convoyX = blockX + 1 + (gliderData[0] - blockX) * 2 + 128 * len(recipeList)
convoyY = i * 64
convoyGen = gliderData[2]
smallConvoy += convoyUnitWest0[convoyGen](convoyX, convoyY)
return smallConvoy
salvos = []
allGliderList = []
salvoHasGliders = []
indicesInSalvos = []
salvoFirstGliders = []
convoyWest = pattern()
convoyEast = pattern()
inverseConvoyDataWest = []
inverseConvoyDataEast = []
frozenSalvos = []
#makes the data for a convoy capable of supporting a given set of salvos with 8 activation times and 2 colors for each salvo
def makeAllSalvosData(requiredRecipes):
global salvos
global allGliderList
global salvoHasGliders
global indicesInSalvos
global salvoFirstGliders
global convoyWest
global convoyEast
global frozenSalvos
global inverseConvoyDataWest
global inverseConvoyDataEast
salvos = []
for recipeList in requiredRecipes:
salvos += getRecipesWithActivationTimes(recipeList[0], recipeList[1])
#turn salvos into a suitable ordered set
allGliderList = []
salvoHasGliders = []
indicesInSalvos = []
salvoFirstGliders = []
for i in range(len(salvos)):
indicesInSalvos.append(0)
salvoHasGliders.append(set())
while True:
minSalvo = -1
minGliderData = []
for i in range(len(salvos)):
if indicesInSalvos[i] < len(salvos[i]):
#salvo still has unregistered gliders
testGliderData = salvos[i][indicesInSalvos[i]]
if minSalvo == -1 or (minGliderData[0] + minGliderData[1]) * 4 - minGliderData[2] > (testGliderData[0] + testGliderData[1]) * 4 - testGliderData[2]:
minSalvo = i
minGliderData = testGliderData
if minSalvo == -1:
break
indicesInSalvos[minSalvo] += 1
#add the new glider if it's not identical to a prior glider
gliderIndex = len(allGliderList)
for i in range(len(allGliderList)):
if allGliderList[i] == minGliderData:
gliderIndex = i
break
if gliderIndex == len(allGliderList):
allGliderList.append(minGliderData)
salvoHasGliders[minSalvo].add(gliderIndex)
for i in range(len(salvos)):
for j in range(len(allGliderList)):
if j in salvoHasGliders[i]:
salvoFirstGliders.append(j)
break
#make convoy and frozen salvoes
convoyWest = pattern()
convoyEast = pattern()
frozenSalvos = []
for i in range(len(salvos)):
frozenSalvos.append(pattern())
for i in range(len(allGliderList)):
gliderData = allGliderList[i]
blockX = i * 64 + gliderData[0] - gliderData[1]
blockY = i * 64 + 8
for j in range(len(salvos)):
if i in salvoHasGliders[j]:
frozenSalvos[j] += block[0](blockX, blockY)
convoyX = blockX - 1 + (gliderData[0] - blockX) * 2
convoyY = i * 64
convoyGen = gliderData[2]
convoyWest += (convoyUnitWest0 if blockX % 2 == 0 else convoyUnitWest1)[convoyGen](convoyX, convoyY)
convoyEast += (convoyUnitEast0 if blockX % 2 == 0 else convoyUnitEast1)[convoyGen](convoyX, convoyY)
#adjust inverse convoy data for later construction
thingsToAddToInverseConvoyDataWest = copy.deepcopy(convoyUnitDataWest0 if blockX % 2 == 0 else convoyUnitDataWest1)
thingsToAddToInverseConvoyDataEast = copy.deepcopy(convoyUnitDataEast0 if blockX % 2 == 0 else convoyUnitDataEast1)
for xWSSData in thingsToAddToInverseConvoyDataWest:
xWSSData[1] -= convoyX
xWSSData[2] -= convoyY
xWSSData[3] += convoyGen
if xWSSData[3] >= 4:
xWSSData[3] -= 4
xWSSData[1] += 2
for xWSSData in thingsToAddToInverseConvoyDataEast:
xWSSData[1] -= convoyX
xWSSData[2] -= convoyY
xWSSData[3] += convoyGen
if xWSSData[3] >= 4:
xWSSData[3] -= 4
xWSSData[1] += 2
inverseConvoyDataWest += thingsToAddToInverseConvoyDataWest
inverseConvoyDataEast += thingsToAddToInverseConvoyDataEast
#makes a demonstration of the convoy and each possible salvo
def demonstrationConvoy():
convoy = pattern()
for i in range(len(allGliderList)):
gliderData = allGliderList[i]
blockX = i * 64 + gliderData[0] - gliderData[1]
blockY = i * 64 + 8
for j in range(len(salvos)):
convoy += block[0](18 - 128 * len(allGliderList) * j + allGliderList[salvoFirstGliders[j]][0], -33 + allGliderList[salvoFirstGliders[j]][1])
if i in salvoHasGliders[j]:
convoy += block[0](blockX - 128 * len(allGliderList) * j, blockY)
convoyX = blockX - 1 + (gliderData[0] - blockX) * 2 + 128 * len(allGliderList)
convoyY = i * 64
convoyGen = gliderData[2]
convoy += convoyUnitWest0[convoyGen](convoyX, convoyY)
return convoy
#makes data which, when hit by convoys at a given period, produces a certain inverse convoy
#convoyData is in the form:
# xWSS type: 0 for LWSS, 1 for MWSS, 2 for HWSS
# xWSS position (x, y) when in base phase
# xWSS phase (0-3)
totalDataIncludingEmpty = 0
finalSalvoXPos = 0
def makeInverseConvoyData(convoyData, period):
global totalDataIncludingEmpty
global finalSalvoXPos
generation = 0
totalDataIncludingEmpty = 0
finalSalvoXPos = 0
for i in range(len(convoyData)):
g.show("Making inverse convoy: Adding xWSS data " + str(i+1) + "/" + str(len(convoyData)) + " at " + str(time() - startTime) + " seconds")
#Suppose we put salvo(color, phase) at a given targetX, targetY
#Note that targetX % 2 == targetY % 2
#The impact point is at targetX - targetY
#The reader ships will impact at gen (targetY - targetX) * 2 (a multiple of 4)
#The salvo will impact at gen (targetY - targetX) * 2 - targetY * 4 = (-targetY - targetX) * 2 (a multiple of 4)
#The ship will be created at (targetX + color, targetY, phase) and travel (period * i) - ((-targetY - targetX) * 2) gens
#Alternatively, it will be created at (targetX + color, targetY, 0) and travel (period * i) - ((-targetY - targetX) * 2) + phase gens
#At gen period * i, it will be at:
# extraGens = (period * i) - ((-targetY - targetX) * 2) + phase
# ((extraGens // 4) * 2 + targetX + color, targetY, extraGens % 4)
# ((extraGens // 4) * 2 + targetX + color, targetY, (period * i + phase) % 4)
# (((period * i + phase) // 4) * 2 + targetX * 2 + targetY + color, targetY, (period * i + phase) % 4)
# phase = phaseMod4 + 4 * phaseType, where phaseType is either 0 or 1
# (((period * i + phaseMod4) // 4) * 2 + phaseType * 2 + targetX * 2 + targetY + color, targetY, (period * i + phaseMod4) % 4)
#Extra spacing to avoid HWSS-LWSS salvo incompatibilities
#This exception is specific to the particular salvo we're creating
if period <= 1081 and i > 0 and convoyData[i][0] == 2 and convoyData[i - 1][0] == 0:
generation += period
totalDataIncludingEmpty += 1
#Increment until construction is actually possible
while True:
#I know all the logic behind this is explained above but these formulas are still cursed
targetY = convoyData[i][2]
color = (convoyData[i][1] - targetY) % 2
targetXMod2 = targetY % 2
phaseMod4 = (convoyData[i][3] - generation) % 4
phaseType = ((convoyData[i][1] - ((generation + phaseMod4) // 4) * 2 - targetXMod2 * 2 - targetY - color) % 4) // 2
phase = phaseMod4 + 4 * phaseType
if (7 - phase) >= numPhaseTypes:
generation += period
totalDataIncludingEmpty += 1
continue
targetX = (convoyData[i][1] - ((generation + phase) // 4) * 2 - targetY - color) // 2
salvoID = convoyData[i][0] * 2 * numPhaseTypes + color * numPhaseTypes + (7 - phase)
block.put(targetX + color + 51, targetY)
frozenSalvos[salvoID].put(targetX - targetY, 0)
if i == len(convoyData) - 1:
finalSalvoXPos = targetX - targetY
break
g.update()
generation += period
totalDataIncludingEmpty += 1
return
#fast stepping from goto.py (thanks to PM 2Ring)
def intbase(n, b):
# convert integer n >= 0 to a base b digit list (thanks to PM 2Ring)
digits = []
while n > 0:
digits += [n % b]
n //= b
return digits or [0]
# actual fast stepping for this case by Tom Rokicki -- the old fastStep() is very often much slower than just sticking with one step size.
def fastStep(steps):
b = g.getbase()
t = steps
sc = 0
st = 1
while t % b == 0:
t //= b
sc += 1
st *= b
g.setstep(sc);
t = 0
while t < steps:
g.step()
t += st
#fill in the convoys
def runConvoys(numConvoys, period, convoyIsWest):
#1024: (-89):423:935, -105
#1000: (-89):411:911, -105
convoyXPos = (128 * len(allGliderList)) + (- 128 * len(allGliderList)) % (2 * period)
#TODO: I should probably verify that this position actually always works
#The actual conditions: east edge of convoy < finalSalvoXPos and west edge < finalBlockXPos
convoyDeleteXPos = finalSalvoXPos + (-finalSalvoXPos) % (2 * period) - 4 * period
for i in range(numConvoys):
g.show("Adding convoy " + str(i+1) +"/" + str(numConvoys) + " at " + str(time() - startTime) + " seconds")
(convoyWest if convoyIsWest else convoyEast).put(convoyXPos, 0)
fastStep(period)
#clear out the excess spaceships
g.putcells((convoyEast if convoyIsWest else convoyWest), -18 + period * 2, -121, -1, 0, 0, -1, "not")
#also clear out used convoys
g.putcells((convoyWest if convoyIsWest else convoyEast), convoyDeleteXPos, 0, 1, 0, 0, 1, "not")
g.update()
(convoyWest if convoyIsWest else convoyEast).put(convoyXPos, 0)
def makeOscillator(period):
oldStep = g.getstep()
makeInverseConvoyData(inverseConvoyDataWest, period)
runConvoys(totalDataIncludingEmpty + 62, period, False)
eastHalf = g.getcells(g.getrect())
g.new("P" + str(goalPeriod) + " Strictly Volatile Loop")
makeInverseConvoyData(inverseConvoyDataEast, period)
runConvoys(totalDataIncludingEmpty + 62, period, True)
#Connect the two halves
convoyXPos = (128 * len(allGliderList)) + (- 128 * len(allGliderList)) % (2 * period)
g.putcells(eastHalf, -18 + period * 2 + convoyXPos, -121, -1, 0, 0, -1, "or")
del eastHalf
#Arrange four copies so that ships annihilate, and step a bit to make it an oscillator
boundingRect = g.getrect()
singleLoop = g.getcells(boundingRect)
g.new("P" + str(goalPeriod) + " Strictly Volatile Loop")
xOffset = -boundingRect[0] - (boundingRect[2] // 2)
yOffset = -boundingRect[1] - (boundingRect[2] // 2) - boundingRect[3]
xOffset -= xOffset % 2
yOffset -= yOffset % 2
g.putcells(singleLoop, xOffset, yOffset, 1, 0, 0, 1, "or")
g.putcells(singleLoop, -xOffset, -yOffset, -1, 0, 0, -1, "or")
g.putcells(singleLoop, yOffset, xOffset, 0, 1, 1, 0, "or")
g.putcells(singleLoop, -yOffset, -xOffset, 0, -1, -1, 0, "or")
del singleLoop
fastStep(convoyXPos * 3)
g.setstep(oldStep)
g.setgen("0")
g.select([])
g.fit()
g.show("Finished at " + str(time() - startTime) + " seconds")
gliderRelativePositionBlacklist = gliderLocationsTooClose(30)
makeAllSalvosData([[lwssRecipe, 0], [mwssRecipe, 10], [hwssRecipeHighClearance, 0]])
makeOscillator(goalPeriod)
Fixed a bug where the streams weren't fully filling out for lower periods, and another bug where the quadrants were sometimes placed too close to one another. Also now includes faster stepping from Tom Rokicki.