Code
recipeTitle='Rebuild Worst Spherical Stabilize'
recipeVersion='1.1' -- 11/23/2014
--[[
Rebuild Worst Spherical Stabilize is a test bed for spherical stabilize after rebuild.
revisions: if you release a new version use a new library name for the new stuff
1.0 -- 11/23/2014 by GaryForbis
initial version.
1.1 -- 11/25/2014 by GaryForbis
finished OptionValidation
fixed min and max rebuild attempts
implemented allLoops, keep bands, keep frozen, use selected, and use frozen
as selection options
1.2 -- 11/27/2014 by GaryForbis
fixed rebuild of rwss.scores.
fixed when to display keep frozen check box.
added alternate scoring functions.
added ability to disable slow filters.
]]
orig={} -- the puzzle settings when the recipe started.
rwss={} -- the stuff in RWSS.
function orig.Save()
save.SaveSecondaryStructure()
orig.settings={} -- stuff gotten from the puzzle at the start of the script.
orig.settings.BandStrengthFactor = behavior.GetBandStrengthFactor()
orig.settings.BuriedSidechainShakeAccuracy = behavior.GetBuriedSidechainShakeAccuracy()
orig.settings.ClashImportance = behavior.GetClashImportance()
orig.settings.ExposedSidechainShakeAccuracy = behavior.GetExposedSidechainShakeAccuracy()
orig.settings.ShakeAccuracy = behavior.GetShakeAccuracy()
orig.settings.SlowFiltersDisabled = behavior.GetSlowFiltersDisabled()
orig.settings.WiggleAccuracy = behavior.GetWiggleAccuracy()
orig.settings.band={}
orig.settings.allBandEnabled=true
orig.settings.hasBands=(band.GetCount()>0)
orig.settings.hasEnabledBands=false
for b = 1,band.GetCount() do
orig.settings.band[b]={IsEnabled = band.IsEnabled(b)}
if band.IsEnabled(b) then
orig.settings.hasEnabledBands=true
else
orig.settings.allBandEnabled=false
end
end
orig.settings.isFrozen={}
orig.settings.isSelected={}
orig.settings.note={}
orig.settings.allLoops=true
for seg = 1,structure.GetCount() do
local backbone, sidechain=freeze.IsFrozen(seg)
if backbone or sidechain then
orig.settings.isFrozen[seg] = {backbone=backbone;sidechain=sidechain}
orig.settings.hasFrozen=true
end
if selection.IsSelected(seg) then
orig.settings.isSelected[seg]=true
orig.settings.hasSelected=true
end
orig.settings.note[seg]=structure.GetNote(seg)
local ss=structure.GetSecondaryStructure(seg)
if (ss=='H') or (ss=='S') then
orig.settings.allLoops=false
end
end
end
function orig.Restore()
save.LoadSecondaryStructure()
-- unimplemented commented out
--behavior.SetBandStrengthFactor(orig.settings.BandStrengthFactor)
--behavior.SetBuriedSidechainShakeAccuracy(orig.settings.BuriedSidechainShakeAccuracy)
behavior.SetClashImportance(orig.settings.ClashImportance)
--behavior.SetExposedSidechainShakeAccuracy(orig.settings.ExposedSidechainShakeAccuracy)
--behavior.SetShakeAccuracy(orig.settings.ShakeAccuracy)
behavior.SetSlowFiltersDisabled(orig.settings.SlowFiltersDisabled)
--behavior.SetWiggleAccuracy(orig.settings.WiggleAccuracy)
if orig.settings.allBandEnabled then
band.EnableAll()
else
for x,b in pairs(orig.settings.band) do
if b.IsEnabled then
band.Enable(x)
else
band.Disable(x)
end
end
end
freeze.UnfreezeAll()
for seg,isFrozen in pairs(orig.settings.isFrozen) do
freeze.Freeze(seg,isFrozen.backbone,isFrozen.sidechain)
end
selection.DeselectAll() -- normal case
for seg,IsSelected in pairs(orig.settings.isSelected) do
selection.Select(seg)
end
for seg=1,structure.GetCount() do
structure.SetNote(seg,orig.settings.note[seg])
end
end
function rwss.GetScore()
s=current.GetEnergyScore()+.0005
return s-s%.001
end
function rwss.OptionValidation(action,value)
local aType,label,minValue,maxValue=action[2],action[3],action[5],action[6]
if (aType=='Check') or (aType =='Label') then
return true,value
elseif (aType=='Integer') or (aType=='Number') then
local pat
if aType=='Integer' then
pat="^([+-]?%d+)$"
else
pat="^([+-]?%d*%.?%d+)$" --no scientific notation.
end
while true do
local n
_,_,n=value:find(pat)
if n then
value=tonumber(value)
if not (minValue and (value<minValue)) and
not (maxValue and (value>maxValue)) then
return true,tonumber(value)
end
end
local dlg=dialog.CreateDialog(aType.." Validation Error")
dlg.l1=dialog.AddLabel(label)
if minValue and maxValue then
dlg.l2=dialog.AddLabel('Must be '..aType..' between '..tostring(minValue)..
' and '..tostring(maxValue)..'.')
elseif minValue then
dlg.l2=dialog.AddLabel('Must be '..aType..' no less than '..tostring(minValue)..'.')
elseif maxValue then
dlg.l2=dialog.AddLabel('Must be '..aType..' no more than '..tostring(maxValue)..'.')
end
dlg.n=dialog.AddTextbox('','')
dlg.b1=dialog.AddButton('OK',1)
dlg.b0=dialog.AddButton('Cancel',0)
if dialog.Show(dlg) == 0 then
return false,nil
end
value=dlg.n.value
end
else
print(aType.." isn't a valid option type.")
return false,nil
end
end
function rwss.ChangeScoring()
local valid=false
local dlg
local subscoreNames = puzzle.GetPuzzleSubscoreNames()
repeat
valid=false
dlg=dialog.CreateDialog('Change Scoring Function')
dlg.l1=dialog.AddLabel('One Scoring method only')
dlg.scoreSeg=dialog.AddCheckbox('Score segment',true)
dlg.scoreDelta=dialog.AddCheckbox('Score difference from neighbor',false)
dlg.scoreSphere=dialog.AddCheckbox('Score sphere average',false)
dlg.l2=dialog.AddLabel('No Checks below for Total Segment Energy Score')
for i =1,#subscoreNames do
dlg['score'..subscoreNames[i]]=dialog.AddCheckbox('Include '..subscoreNames[i],false)
end
dlg.b1=dialog.AddButton('OK',1)
dlg.b0=dialog.AddButton('Cancel',0)
if dialog.Show(dlg) == 0 then
print ('Score using: Segment Energy Score')
return
end
local methods=0
if dlg.scoreSeg.value then
methods=methods+1
end
if dlg.scoreDelta.value then
methods=methods+1
end
if dlg.scoreSphere.value then
methods=methods+1
end
if methods==1 then
valid = true
end
until valid
if dlg.scoreDelta.value then
rwss.options.scoreDelta=true
print('Score using: Greatest score difference from neighbor.')
elseif dlg.scoreSphere.value then
rwss.options.scoreSphere=true
print('Score using: Average of segment scores in the sphere.')
else
print ('Score using: Segment Energy Score.')
end
rwss.options.scoreSubComponents={}
for i =1,#subscoreNames do
if dlg['score'..subscoreNames[i]].value then
table.insert(rwss.options.scoreSubComponents,subscoreNames[i])
print('Include: '..subscoreNames[i]..'.')
end
end
if #rwss.options.scoreSubComponents == 0 then
rwss.options.scoreSubComponents=nil -- don't score subcomponents
end
end
function rwss.GetOptions()
local optionsDialog = {
-- {condition,
-- {index,type,label,default,(optional)minimum,(optional)maximum}}
{not orig.settings.allLoops,
{'allLoops','Check','Run as all loops',false}},
{orig.settings.hasSelected,
{'rebuildSelected','Check','Only rebuild selected segments',true}},
{orig.settings.hasFrozen and not orig.settings.hasSelected,
{'selectFrozen','Check','Use frozen as if selected',true}},
{orig.settings.hasFrozen,
{'leaveFrozen','Check','Leave frozen during the run',orig.settings.hasSelected}},
{orig.settings.hasEnabledBands,
{'leaveBands','Check','Leave bands enabled during the run',false}},
{true,
{'l1','Label','Score using Segment Energy Score'}},
{true,
{'changeScoring','Check','Changing Scoring Method',false}},
{true,
{'disableSlowFilters','Check','Disable slow filters (bad idea)',false}},
{true,
{'maxRebuildAttempts','Integer','Segment Rebuild Attempts',6,1,10}},
{true,
{'rebuildsBeforeRetrying','Integer',
'Successful Rebuilds before clearing list',5,0,structure.GetCount()}},
{true,
{'minRebuildPoints','Number','Minimum Points for a Successful Rebuild',2.0}},
{true,
{'minScorePoints','Number','Score improvement clears rebuild list',40,0}},
{true,
{'radius','Number','Radius of sphere to local wiggle',9,1.9}},
{true,
{'wiggles','Integer','Cycles to wiggle',10,1}},
{true,
{'lowCI','Number','Low CI for local wiggle',0.03,0,1}}
}
local dlg=dialog.CreateDialog(recipeTitle)
for i=1,#optionsDialog do
if optionsDialog[i][1] then
local action=optionsDialog[i][2]
local actiontype=action[2]
if actiontype == 'Label' then
dlg[action[1]]=dialog.AddLabel(action[3])
elseif actiontype == 'Check' then
dlg[action[1]]=dialog.AddCheckbox(action[3],action[4])
else
dlg['l'..action[1]]=dialog.AddLabel(action[3])
dlg[action[1]]=dialog.AddTextbox('',tostring(action[4]))
end
end
end
dlg.b1=dialog.AddButton('OK',1)
dlg.b0=dialog.AddButton('Cancel',0)
rwss.options={}
if dialog.Show(dlg) == 0 then
return false
end
local valid
for i=1,#optionsDialog do
if optionsDialog[i][1] then
local action=optionsDialog[i][2]
valid,rwss.options[action[1]]=rwss.OptionValidation(action,dlg[action[1]].value)
if not valid then
return false
end
end
end
while rwss.options.selectFrozen and rwss.options.leaveFrozen do
local fdlg=dialog.CreateDialog("Fix Incompatibly Frozen")
fdlg.l1=dialog.AddLabel('Uncheck one of the following')
fdlg.s=dialog.AddCheckbox('Use frozen as if selected',true)
fdlg.l=dialog.AddCheckbox('Leave frozen during the run',true)
fdlg.b1=dialog.AddButton('OK',1)
fdlg.b0=dialog.AddButton('Cancel',0)
if dialog.Show(fdlg) == 0 then
return false
end
rwss.options.selectFrozen=fdlg.s.value
rwss.options.leaveFrozen=fdlg.l.value
end
if rwss.options.changeScoring then
rwss.ChangeScoring()
else
print ('Score using: Segment Energy Score')
end
rwss.options.changeScoring=nil -- don't display
for i=1,#optionsDialog do
local action=optionsDialog[i][2]
if rwss.options[action[1]] ~= nil then
print(action[3]..': '..tostring(rwss.options[action[1]]))
end
end
return true
end
function rwss.GetSegScore(seg)
if (seg < 1) or (seg > structure.GetCount()) then
return nil
end
if rwss.options.scoreSubComponents then
local x = 0
local subComponents=rwss.options.scoreSubComponents
for i=1,#subComponents do
x=x+ current.GetSegmentEnergySubscore(seg,subComponents[i])
end
return x
else
return current.GetSegmentEnergyScore(seg)
end
end
function rwss.GetWorkingSegScore(seg) -- handle the various scoring function.
-- delta and sphere scoring are currently mutually exclusive.
if rwss.options.scoreDelta then
local x,y=rwss.GetSegScore(seg-1),rwss.GetSegScore(seg+1)
if x and y then
return rwss.GetSegScore(seg) - math.max(x,y)
else
return rwss.GetSegScore(seg) - (x or y)
end
elseif rwss.options.scoreSphere then
local j = 0
local x = 0
local radius = rwss.options.radius
for i = 1,structure.GetCount() do
if structure.GetDistance(seg,i)<=radius then
j = j + 1
x = x + rwss.GetSegScore(seg)
end
end
return x/j
else
return rwss.GetSegScore(seg)
end
end
function rwss.GetScores() -- lowest score to highest score
local scores={}
local i=0
if rwss.options.rebuildSelected then
for seg,IsSelected in pairs(rwss.IsSelected) do
if not (structure.IsLocked(seg) or
freeze.IsFrozen(seg) or
(structure.GetSecondaryStructure(seg) == 'M')) then
i=i+1
scores[i]={seg,rwss.GetWorkingSegScore(seg)}
end
end
else
for seg=1,structure.GetCount() do
if not (structure.IsLocked(seg) or
freeze.IsFrozen(seg) or
(structure.GetSecondaryStructure(seg) == 'M')) then
i=i+1
scores[i]={seg,rwss.GetWorkingSegScore(seg)}
end
end
end
table.sort(scores,function(a,b) return a[2]<b[2] end)
return scores
end
function rwss.Setup()
orig.Save()
recentbest.Save()
rwss.startingScore=current.GetEnergyScore()
rwss.bestScore=rwss.startingScore
local startingScore = rwss.GetScore()
print(recipeTitle.." Starting Score: "..tostring(startingScore))
if not rwss.GetOptions() then
print('You cancelled out of the option dialogue.')
return false -- user cancelled
end
if rwss.options.allLoops then
selection.SelectAll()
structure.SetSecondaryStructureSelected('L')
end
if rwss.options.rebuildSelected then
rwss.IsSelected=orig.settings.isSelected
elseif rwss.options.selectFrozen then
rwss.options.rebuildSelected=true -- fake out the scoring function
rwss.IsSelected={}
for seg,isFrozen in pairs(orig.settings.isFrozen) do
rwss.IsSelected[seg]=true
print(seg)
end
end
if not rwss.options.leaveFrozen then
freeze.UnfreezeAll()
end
if not rwss.options.leaveBands then
band.DisableAll()
end
rwss.scores = rwss.GetScores()
rwss.rebuildsRemaining = rwss.options.rebuildsBeforeRetrying
rwss.scoreBeforeRetrying=rwss.bestScore+rwss.options.minScorePoints
rwss.triedSeg={} -- initialize the list of segments tried.
return true
end
function rwss.GetSeg()
for i = 1,#rwss.scores do
seg=rwss.scores[i][1]
if not rwss.triedSeg[seg] then
print("Trying Segment "..tostring(seg).." scored "..tostring(rwss.scores[i][2])..".")
return seg
end
end
return nil
end
function rwss.RebuildSegment(seg)
selection.DeselectAll()
selection.Select(seg)
for i =1,rwss.options.maxRebuildAttempts do
structure.RebuildSelected(i)
if current.GetEnergyScore()~=rwss.bestScore then
rwss.rebuildScore=current.GetEnergyScore()
if current.GetEnergyScore() > rwss.bestScore then
print("Rebuild of segment "..tostring(seg).." improved score to "..rwss.GetScore()..".")
end
return true
end
end
return false
end
function rwss.SphericalStabilize(seg)
local radius = rwss.options.radius
selection.DeselectAll()
for i = 1,structure.GetCount() do
if structure.GetDistance(seg,i)<=radius then selection.Select(i) end
end
-- this is a very simple Spherical Stabilize routine
local wiggles=rwss.options.wiggles
behavior.SetSlowFiltersDisabled(rwss.options.disableSlowFilters)
behavior.SetClashImportance(1)
structure.ShakeSidechainsSelected(1)
structure.WiggleSelected((wiggles+1)/2)
structure.WiggleAll((wiggles+1)/2)
behavior.SetClashImportance(rwss.options.lowCI)
structure.ShakeSidechainsSelected(1)
structure.WiggleSelected(wiggles*2)
behavior.SetClashImportance(1)
structure.ShakeSidechainsAll(1)
structure.WiggleAll(wiggles)
behavior.SetSlowFiltersDisabled(false)
-- the rest is administrative stuff about score keeping.
recentbest.Restore()
local currentScore=current.GetEnergyScore()
if currentScore > rwss.bestScore then
rwss.scores = rwss.GetScores()
if currentScore > rwss.rebuildScore then
print("Stabilize of segment "..tostring(seg).." improved score to "..rwss.GetScore()..".")
end
if currentScore > (rwss.bestScore + rwss.options.minRebuildPoints) then
rwss.rebuildsRemaining=rwss.rebuildsRemaining - 1
print("Rebuild counted as a success. Need "..tostring(rwss.rebuildsRemaining)..
" more.")
end
if currentScore > rwss.scoreBeforeRetrying then
print("Reached goal score.")
end
if (rwss.rebuildsRemaining <= 0) or
(currentScore > rwss.scoreBeforeRetrying) then
rwss.rebuildsRemaining = rwss.options.rebuildsBeforeRetrying
rwss.scoreBeforeRetrying=currentScore+rwss.options.minScorePoints
print("Resetting list to try. New goal score is "..
tostring(rwss.scoreBeforeRetrying)..".")
rwss.triedSeg={}
end
rwss.bestScore = currentScore
return true
else
return false
end
end
function rwss.Cleanup(message)
print("Total gain: "..tostring(current.GetEnergyScore()-rwss.startingScore))
print(recipeTitle.." "..message..". Final Score: "..tostring(rwss.GetScore()))
orig.Restore()
end
function rwss.Main ()
if not rwss.Setup() then
orig.Restore()
return true
end
local seg=rwss.GetSeg()
while seg do
rwss.triedSeg[seg]=true
if rwss.RebuildSegment(seg) then
if not rwss.SphericalStabilize(seg) then
print("Rebuild of segment "..tostring(seg).." didn't improve things.")
end
else
print("Couldn't rebuild segment "..tostring(seg))
end
seg=rwss.GetSeg()
end
rwss.Cleanup('completed')
return true
end
function rwss.EarlyExit(err)
if rwss.earlyExited then
print(err) -- handle error if already earlyExited
else
rwss.earlyExited=true -- prevent runaway in EarlyExit
local message
if err:find('Cancelled')~=nil then
message = 'cancelled'
else
message = 'aborted'
print(err)
end
behavior.SetSlowFiltersDisabled(false)
recentbest.Restore()
if current.GetEnergyScore() > rwss.bestScore then
print("Score improved to "..rwss.GetScore().." as recipe was "..message..".")
end
rwss.Cleanup(message)
end
end
xpcall(rwss.Main,rwss.EarlyExit)