Code
-------------------------------------------------------------------------------
-- Cut And Wiggle v 3
--
-- Performs a fuse-wiggle with cuts inserted every "step size" segment
-- Randomly chooses shifts to attempt but eventually tries them all.
-- Decrements step size if all shifts have failed to produce gain.
-- Restarts at original step size after all have been tried.
--
-- Originally by Susume
-- Gui and features added by KarenCH
-- Enhanced by zeroblue
-- Enhancements:
-- Recent best saves and restores changed to improve scoring,
-- especially when running at a high CI less than 1 or when using bands.
-- Order of offsets is now less random: All possible offsets are tried,
-- and no offset for a given step size is tried twice in a row.
-- Random seed function fixed to produce different results with each run.
-- Added to step size to allow limited ranges of step sizes
-- After all step sizes and offsets have been tried, the lowCI is randomly changed.
-- Added an option to disable filters for puzzles that have them.
-- Reorganized functionality.
-- Really will run forever now.
-- SemperRabbit 20220608@2150 JST: added cleanup button to dialog to remove all cuts from a cancel.
-- V3.1
-- Adds option to only work with selected AAs.
-------------------------------------------------------------------------------
------------------------------------------------------------------------------------
-- HELPER FUNCTIONS AND INTERNAL-ONLY CONSTANTS
------------------------------------------------------------------------------------
OriginalCI = 1.0
startingStepSize=3
toStepSize=10
lowCI = 0.3
originalLowCI = 0.3
highCI=1.0
shortwiggle = 1
longwiggle = 25
StartSegment = 0
EndSegment = structure.GetCount()
originalScore = 0
bestScore = 0
hasFilters = false
toggleFilters = false
selectedOnly = false -- v3.1
function setStartEnd()
low = structure.GetCount()
high = 1
if selectedOnly then
for i=1 , structure.GetCount(), 1 do
if selection.IsSelected(i) then
if low >= i then
low = i
end
if high <= i then
high = i
end
end
end
else
low = 1
high = structure.getCount()
end
StartSegment = low
EndSegment = high
end
function SeedRandom()
local seed = os.time()%1000
print("Seed is: "..seed)
math.randomseed(seed)
end
function GetOffsetList(stepsize)
local offsetOrderedList = {}
for i=1, stepsize do
table.insert( offsetOrderedList, i )
end
return offsetOrderedList
end
function RandomizeList(offsetOrderedList)
local offsetRandomList = {}
for i=1, #offsetOrderedList do
local offsetPos = math.random( #offsetOrderedList )
local thisOffset = table.remove ( offsetOrderedList, offsetPos )
table.insert( offsetRandomList, thisOffset )
end
return offsetRandomList
end
function TableList(myTable)
local myString=""
for i=1,#myTable do
myString = myString..myTable[i]..', '
end
return(string.sub(myString,1,#myString-2))
end
function SetupOffsetList( stepsize )
local offsetList = GetOffsetList( stepsize )
offsetList = RandomizeList( offsetList )
print("Offset list is: "..TableList(offsetList))
return offsetList
end
function Round(num, idp)
local mult = 10^(idp or 0)
if num >= 0 then return math.floor(num * mult + 0.5) / mult
else return math.ceil(num * mult - 0.5) / mult end
end
function RandomizeLowCI( )
local newLowCI = lowCI
while newLowCI == lowCI do
newLowCI = Round(newLowCI + (math.random( 21 ) - 11) * 0.01, 2)
if newLowCI < 0.04 or newLowCI > math.max(highCI * 0.5, originalLowCI * 1.1) then
newLowCI = lowCI
end
end
lowCI = newLowCI
print( "New lowCI = "..lowCI )
end
function PrintSettings( )
print("Starting step size = "..startingStepSize)
print("To step size = "..toStepSize)
print("lowCI = "..lowCI)
print("highCI = "..highCI)
print("shortwiggle = "..shortwiggle)
print("longwiggle = "..longwiggle)
end
------------------------------------------------------------------------------------
-- THE WIGGLER
------------------------------------------------------------------------------------
function DoWiggle( )
PrintSettings( )
originalLowCI = lowCI
local stepsize = startingStepSize
-- create initial offsetList
local offsetList = SetupOffsetList( stepsize )
-- set the step size change value
local stepSizeChange = 0
if toStepSize > startingStepSize then
stepSizeChange = 1
elseif toStepSize < startingStepSize then
stepSizeChange = -1
end
--print( "Step size change is: "..stepSizeChange )
-- loop until user cancels
while 1==1 do
local offset = table.remove( offsetList, 1 )
print( "Starting at seg "..offset.." with stepsize="..stepsize )
Wiggler(offset, stepsize)
-- scoring and shake
local newScore = current.GetEnergyScore( )
if newScore > bestScore then
recentbest.Save( )
structure.ShakeSidechainsAll( 1 )
recentbest.Restore( )
save.Quicksave( 3 )
newScore = current.GetEnergyScore( )
print( "Score improved by "..newScore-bestScore.." to "..newScore )
bestScore = newScore
-- create new offsetList
offsetList = GetOffsetList(stepsize)
local currentOffset = table.remove ( offsetList, offset ) -- remove the current offset
offsetList = RandomizeList( offsetList )
table.insert( offsetList, currentOffset ) -- add last offset to end
print( "Offset list is: "..TableList( offsetList ) )
else
print( "Failed with loss of "..newScore-bestScore )
if toggleFilters then
behavior.SetSlowFiltersDisabled(true)
end
save.Quickload( 3 )
end
-- If offsetList is empty, create a new one
if #offsetList == 0 then
if stepsize == toStepSize then
print( "Finished end step size. Restarting with step size "..startingStepSize )
stepsize = startingStepSize
RandomizeLowCI( )
else
stepsize = stepsize + stepSizeChange
print( "Step size changed to.. "..stepsize )
print( "Score increase so far of: "..bestScore-originalScore.." from "..originalScore)
end
-- create new offsetList
offsetList = SetupOffsetList( stepsize )
end
end
end
function Wiggler( offset, stepsize )
if toggleFilters then
behavior.SetSlowFiltersDisabled(true)
end
if selectedOnly then -- v3.1
for i = StartSegment+offset, EndSegment-1, stepsize do
structure.InsertCut( i )
end
behavior.SetClashImportance( lowCI )
structure.WiggleSelected( shortwiggle )
recentbest.Save( )
behavior.SetClashImportance( highCI )
structure.WiggleSelected( longwiggle )
recentbest.Restore( )
for i = StartSegment+offset, EndSegment-1, stepsize do
structure.DeleteCut( i )
end
recentbest.Save( )
behavior.SetClashImportance( lowCI )
structure.WiggleSelected( shortwiggle )
behavior.SetClashImportance( highCI )
structure.WiggleSelected( longwiggle )
recentbest.Restore( )
else
for i = StartSegment+offset, EndSegment-1, stepsize do
structure.InsertCut( i )
end
behavior.SetClashImportance( lowCI )
structure.WiggleAll( shortwiggle )
recentbest.Save( )
behavior.SetClashImportance( highCI )
structure.WiggleAll( longwiggle )
recentbest.Restore( )
for i = StartSegment+offset, EndSegment-1, stepsize do
structure.DeleteCut( i )
end
recentbest.Save( )
behavior.SetClashImportance( lowCI )
structure.WiggleAll( shortwiggle )
behavior.SetClashImportance( highCI )
structure.WiggleAll( longwiggle )
recentbest.Restore( )
end
if toggleFilters then
behavior.SetSlowFiltersDisabled(false)
end
end
------------------------------------------------------------------------------------
-- USER DIALOG BOX
------------------------------------------------------------------------------------
function GetParams( )
dlg = dialog.CreateDialog( "Cut And Wiggle" )
dlg.step = dialog.AddSlider( "Initial step size", startingStepSize, 1, 20, 0 )
dlg.toStep = dialog.AddSlider( "To step size", toStepSize, 1, 20, 0 )
dlg.lowCI = dialog.AddSlider( "LowCI", lowCI, 0, 1, 2 )
dlg.highCI = dialog.AddSlider( "HighCI", highCI, 0, 1, 2 )
dlg.shortwiggle = dialog.AddSlider( "ShortWiggleIters", shortwiggle, 1, 10, 0 )
dlg.longwiggle = dialog.AddSlider( "LongWiggleIters", longwiggle, 1, 50, 0 )
if hasFilters then
dlg.disable = dialog.AddCheckbox("Wiggle with disabled slow filters",filterActive)
end
dlg.bSelectedOnly = dialog.AddCheckbox("Work on selected (contiguous) AAs only",selectedOnly) -- v3.1
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
dlg.cleanup = dialog.AddButton ("Cleanup", 2)
local dlgret = dialog.Show( dlg )
if dlgret == 1 then
startingStepSize = dlg.step.value
toStepSize = dlg.toStep.value
lowCI = dlg.lowCI.value
highCI = dlg.highCI.value
shortwiggle = dlg.shortwiggle.value
longwiggle = dlg.longwiggle.value
if hasFilters then
toggleFilters = dlg.disable.value
end
selectedOnly = dlg.bSelectedOnly.value -- v3.1
if selectedOnly then
setStartEnd()
end
return true
elseif dlgret == 2 then
cleanup()
return false
else
print( "dialog cancelled" )
return false
end
end
------------------------------------------------------------------------------------
-- CONTROLLERS: SETUP AND CLEANUP
------------------------------------------------------------------------------------
function cleanCuts( )
for i,j in ipairs(structure.GetCuts()) do
structure.DeleteCut( j )
end
end
function cleanup( )
if toggleFilters then
behavior.SetSlowFiltersDisabled(false)
end
if bestScore > current.GetEnergyScore( ) then
save.Quickload( 3 )
else
bestScore = current.GetEnergyScore( )
end
behavior.SetClashImportance( OriginalCI )
PrintSettings( )
print( "Total score increase of: "..bestScore-originalScore.." from "..originalScore)
cleanCuts( )
end
function main( )
SeedRandom()
print( "Original in slot 2, High score in slot 3" )
save.Quicksave( 2 ) --starting position
save.Quicksave( 3 ) --high score
bestScore = current.GetEnergyScore( )
-- Test for filters
if not behavior.GetSlowFiltersDisabled() then
behavior.SetSlowFiltersDisabled(true)
if current.GetEnergyScore( ) ~= bestScore then
hasFilters = true
print( "Slow filters detected" )
end
behavior.SetSlowFiltersDisabled(false)
end
print( "Starting score "..bestScore )
originalScore = bestScore
OriginalCI = behavior.GetClashImportance( )
while GetParams( ) do
DoWiggle( )
end
cleanup( )
end
xpcall( main,cleanup )