Cmd-Z in script editor can load text from different previously edited script

Case number:845813-2010885
Topic:Game: Tools
Opened by:zo3xiaJonWeinberg
Status:Open
Type:Bug
Opened on:Saturday, November 28, 2020 - 12:21
Last modified:Monday, November 30, 2020 - 15:59

The new Cmd-Z and Cmd-Shift-Z (redo) and selection and copy tools in the script editor are nice but I overwrote my recipe because I hit Cmd-Z too many times and then typed something in the previous recipe's text, so the redo was overwritten. Fortunately I uploaded it.
Tail of log:
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.LoadSaveDialog: No object matching key 0.
game.application.boinc.Boinc: Sending SOPs:
game.application.LoadSaveDialog: loading solution: puzzle_2010882_time_1606565228.ir_solution
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
interactive.application.actions.cart.PoseLoopThreadActionCart: ***** STARTING THREAD ActionRepack
interactive.application.shared.tool_util: RT: 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
interactive.application.actions.cart.PoseLoopThreadActionCart: Packer cancelled
interactive.application.actions.cart.PoseLoopThreadActionCart: ***** ENDING THREAD ActionRepack
interactive.application.shared.Tool: Time: Sat Nov 28 12:09:36 2020 UTC - Modo Tirar completed
interactive.application.shared.Tool: Time: Sat Nov 28 12:09:36 2020 UTC - Modo Tirar completed
interactive.application.shared.Tool: Time: Sat Nov 28 12:09:36 2020 UTC - Modo Tirar completed
core.scoring.sc.ShapeComplementarityCalculator: [ ERROR ] Failed: No molecular dots generated!
protocols.simple_filters.ShapeComplementarityFilter: [ ERROR ] Issue running shape complementarity calculator - returning -1 instead.
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.BoincThread: SRVR_THRD getting notifications...
game.application.boinc.Boinc: Sending SOPs:
interactive.application.shared.Tool: Time: Sat Nov 28 12:12:59 2020 UTC - Modo Tirar completed
interactive.application.shared.Tool: Time: Sat Nov 28 12:12:59 2020 UTC - Modo Tirar completed
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
game.application.boinc.Boinc: Sending SOPs:
interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
print
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
pri
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
pri
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
p
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
p
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok

side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok

side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok

side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok

side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to ok = structure.DeleteResidue(108)
print(o
interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(108)
print(ok) to ok = structure.DeleteResidue(108)
print(ok
interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(108)
print(ok to ok = structure.DeleteResidue(108)
pri
interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(108)
pri to ok = structure.DeleteResidue(108)

interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(108)
to ok = structure.DeleteResidue(1)
interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(1) to ok = structure.DeleteResidue(side)
interactive.gui.TextBox: Undone from ok = structure.DeleteResidue(side) to
interactive.gui.TextBox: Undone from to --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )

interactive.gui.TextBox: Undone from --DeleteMax
-- AddMax
--
-- adds as many segments as possible
--
-- 1.0 - LociOiling - 2018/03/02
--
Recipe = "DeleteMax"
Version = "1.0"
ReVersion = Recipe .. " " .. Version

print ( ReVersion )

undo.SetUndo ( false )
behavior.SetFiltersDisabled ( true )
local segcnti = structure.GetCount ()
local segcnt = segcnti

print ( "initial segment count = " .. segcnti )

--firstUnlocked=1
for i=1,segcnt do
if not structure.IsLocked(i) then firstUnlocked=i break end
end
print(firstUnlocked.."first unlocked")

local ok = false
repeat
--local ss1 = structure.GetSecondaryStructure ( segcnt - 1 )
--structure.SetSecondaryStructure ( segcnt - 1, "E" )
--local ss2 = structure.GetSecondaryStructure ( segcnt )
--structure.SetSecondaryStructure ( segcnt, "E" )
--local aa2 = structure.GetAminoAcid ( segcnt )
--
-- add segment before the last segment
--
i=1
repeat
ok = structure.DeleteResidue (segcnt- i ) or ok
side=firstUnlocked+i
ok = structure.DeleteResidue(side) or ok
print("side "..side)
i=i+1
until segcnt-i<=side
--structure.SetSecondaryStructure ( segcnt - 1, ss1 )
--structure.SetSecondaryStructure ( segcnt, ss2 )
segcnt = structure.GetCount ()

if ok then
--
-- set secondary structure of last segment
--
-- structure.SetSecondaryStructure ( segcnt, ss2 )
--
-- set amino acid of added segment
--
--structure.SetAminoAcid ( segcnt - 1, aa2 )
end
until not ok

local segcnt = structure.GetCount ()
if segcnti == segcnt then
print ( "no segments deleted" )
else
print ( segcnt - segcnti .. " segments added" )
end
print ( "final segment count = " .. segcnt )
print ( ReVersion .. " complete" )
undo.SetUndo ( true )
behavior.SetFiltersDisabled ( false )
to behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=1 --math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 -- or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

interactive.gui.TextBox: Undone from behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=1 --math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 -- or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

to behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=1math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 -- or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

interactive.gui.TextBox: Undone from behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=1math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 -- or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

to behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

interactive.gui.TextBox: Undone from behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 2,5,11,3,13,4,7,1,6 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 14,8,6,7,13,12,2,10,11 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 5,7,1,3,9,6,2,4,8 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,6,12,4,14,5,8,2,7 }
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01
InitializePuzzleState( )

local gain = 0.0
repeat
local score = getScore( )
pattern={ 3,8,4,5,12,6,10,2,7}
BandedWorm( pattern )
gain = getScore( ) - score
until gain < 0.01 or puzzle.GetExpirationTime()

--jon
if puzzle.GetExpirationTime() print("expired")
break
end

end

End( )
end

xpcall( main, End )

to behavior.SetClashImportance(1) --jon for BWP

---------------
-- Variables --
---------------
AFK = {}

AFK.SaveSlot = 98
AFK.StartScore = current.GetEnergyScore()
AFK.StartCI = behavior.GetClashImportance()
AFK.IsMutable = false

AFK.BounceWiggle = {}
AFK.Init = {}
AFK.Helper = {}

AFK.BounceWiggle.DoShake = false
AFK.BounceWiggle.DoMutate = false
AFK.BounceWiggle.Iterations = 5 --jon testing
AFK.BounceWiggle.IterationCount = 0 --jon
AFK.BounceWiggle.DogDays = 0 --jon
AFK.BounceWiggle.MinGain = 0.1
AFK.BounceWiggle.SkipCIMaximization = true
AFK.BounceWiggle.PrintFailures = true

----------------------
-- Helper Functions --
----------------------
AFK.Helper.IsMutable = function()
for n=1, structure.GetCount() do
if (structure.IsMutable(n)) then
AFK.IsMutable = true
end
end
end

AFK.Helper.PrintStart = function(choice)
if (choice > 2) then
mode = "Mutate"
AFK.BounceWiggle.DoMutate = true
elseif (choice > 1) then
mode = "Shake"
AFK.BounceWiggle.DoShake = true
else
mode = "NoShake"
end
print("AFK3(BounceWiggle"..mode..") started. "..
AFK.BounceWiggle.Iterations..
" consecutive Failed iterations before ending.") --jon
end

AFK.Helper.PrintEnd = function(gain)
if (gain > 0) then
print ("script complete: + "..
(current.GetEnergyScore() - AFK.StartScore)..
" -- "..current.GetEnergyScore())
else
print("script complete:"..current.GetEnergyScore())
end
end

AFK.Helper.SetCI = function(ci)
ci = ci or math.random(50, 900) / 1000
behavior.SetClashImportance(ci)
end

AFK.Helper.SelectRandom = function()
local shakeSelectionCount = math.random(1, structure.GetCount())
for n=1, shakeSelectionCount do
local selectSegment = math.random(1, structure.GetCount())
selection.Select(selectSegment)
end
end

AFK.Helper.PerformFunction = function(func, iters)
local currentScore = current.GetEnergyScore()
local newScore = currentScore
behavior.SetFiltersDisabled(true)
func(iters)
behavior.SetFiltersDisabled(false)
recentbest.Restore()
newScore = current.GetEnergyScore()
if (newScore > (currentScore + AFK.BounceWiggle.MinGain)) then
currentScore = newScore
save.Quicksave(AFK.SaveSlot)
else
save.Quickload(AFK.SaveSlot)
end
end

AFK.Helper.UpdateIterations = function(gain)
-- TODO: option to print, even on failure. (so we know Iterations)
AFK.BounceWiggle.IterationCount=AFK.BounceWiggle.IterationCount+1

if gain > 0 then
print (AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": + "..
gain.." -- "..current.GetEnergyScore())
AFK.BounceWiggle.DogDays = 0
elseif (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) then
if (AFK.BounceWiggle.PrintFailures == true) then
print(AFK.BounceWiggle.IterationCount
.." iters. dogdays "..AFK.BounceWiggle.DogDays..": "..gain.." -- "..current.GetEnergyScore())
end
AFK.BounceWiggle.DogDays = AFK.BounceWiggle.DogDays+1

end
end

-----------------------
-- Create Dialog Box --
-----------------------
AFK.CreateBounceWiggleDialog = function()
local currentDialog = dialog.CreateDialog("BounceWiggle")
currentDialog.IterationsLabel = dialog.AddLabel(
"Failed Iterations before ending")
currentDialog.IterationsSlider = dialog.AddSlider(
"Failure Iterations", AFK.BounceWiggle.Iterations, -1, 1000, 0)

currentDialog.BlankLabel1 = dialog.AddLabel("")
currentDialog.DiscardLabel = dialog.AddLabel(
"(Sketchbook) Discard gains less than")
currentDialog.DiscardSlider = dialog.AddSlider(
"Discard <", AFK.BounceWiggle.MinGain, 0, 10, 1)

currentDialog.BlankLabel2 = dialog.AddLabel("")
currentDialog.SkipMaximization = dialog.AddCheckbox(
"Skip CI=1 Maximization", AFK.BounceWiggle.SkipCIMaximization)

currentDialog.PrintFailuresOption = dialog.AddCheckbox(
"Print info for Failed Attempts", AFK.BounceWiggle.PrintFailures
)

currentDialog.NoShakeButton = dialog.AddButton("Wiggle Only", 1)
currentDialog.ShakeButton = dialog.AddButton("Shake", 2)
if (AFK.IsMutable) then
currentDialog.MutateButton = dialog.AddButton("Mutate", 3)
end
currentDialog.CancelButton = dialog.AddButton("Skip to endgame", 0) --jon

local choice = dialog.Show(currentDialog)

AFK.BounceWiggle.Iterations = currentDialog.IterationsSlider.value
AFK.BounceWiggle.MinGain = currentDialog.DiscardSlider.value
AFK.BounceWiggle.SkipCIMaximization = currentDialog.SkipMaximization.value
AFK.BounceWiggle.PrintFailures = currentDialog.PrintFailuresOption.value

if (AFK.BounceWiggle.Iterations < 1) then
AFK.BounceWiggle.Iterations = -1
end

if (choice > 0) then
AFK.Helper.PrintStart(choice)
end
return choice
end

-------------------
-- The main dish --
-------------------
AFK.BounceWiggle.Main = function()
AFK.Helper.IsMutable()

local startScore = AFK.StartScore
local choice = AFK.CreateBounceWiggleDialog()
if (choice < 1) then
print("Dialog cancelled.")
return
end

save.Quicksave(AFK.SaveSlot)
recentbest.Save()

if (AFK.BounceWiggle.SkipCIMaximization == false) then
AFK.Helper.PerformFunction(AFK.BounceWiggle.CIMaximization)
startScore = current.GetEnergyScore()
end

print("Script started: "..startScore)
while (AFK.BounceWiggle.DogDays < AFK.BounceWiggle.Iterations) do
startScore = current.GetEnergyScore()
AFK.Helper.PerformFunction(AFK.BounceWiggle.BounceWiggle)
AFK.Helper.UpdateIterations(current.GetEnergyScore() - startScore)
end

AFK.Helper.PrintEnd(current.GetEnergyScore() - startScore)
AFK.Cleanup()
end

-------------------------
-- The BounceWiggliest --
-------------------------
AFK.BounceWiggle.CIMaximization = function()
local currentScore = AFK.StartScore
local newScore = currentScore + AFK.BounceWiggle.MinGain + 0.01

print("Maximizing Wiggle Score at Clashing Importance = 1: "..currentScore)
AFK.Helper.SetCI(1)
while (currentScore + AFK.BounceWiggle.MinGain < newScore) do
structure.WiggleAll(25)
structure.LocalWiggleAll(25)
recentbest.Restore()
currentScore = newScore
newScore = current.GetEnergyScore()
end

if (newScore > AFK.StartScore) then
print ("CI Maximization: + "..(currentScore - AFK.StartScore)..
" -- "..currentScore)
end
end

AFK.BounceWiggle.WiggleAll = function(ci, wiggleIterations)
AFK.Helper.SetCI(ci)
wiggleIterations = wiggleIterations or math.random(1, 3)

local wiggleType = math.random(1, 2)
if (wiggleType > 1) then
structure.WiggleAll(wiggleIterations)
else
structure.LocalWiggleAll(wiggleIterations)
end
end

AFK.BounceWiggle.ShakeityShake = function()
local shakeType = math.random(1, 3)
local shakeIterations = math.random(1, 3)
AFK.Helper.SetCI()

-- 1/3 chance of whole protein
if (shakeType > 2) then
if (AFK.BounceWiggle.DoMutate == true) then
-- When mutating all, we only do one iteration.
-- This is to increase BounceWiggle speed, to explore more
-- configurations faster.
structure.MutateSidechainsAll(1)
else
structure.ShakeSidechainsAll(shakeIterations)
end
-- 2/3 chance of random selection
else
AFK.Helper.SelectRandom()
if (AFK.BounceWiggle.DoMutate == true) then
structure.MutateSidechainsSelected(shakeIterations)
else
structure.ShakeSidechainsSelected(shakeIterations)
end
selection.DeselectAll()
end
end

AFK.BounceWiggle.BounceWiggle = function()
AFK.BounceWiggle.WiggleAll()
if (AFK.BounceWiggle.DoShake == true
or AFK.BounceWiggle.DoMutate == true) then
AFK.BounceWiggle.ShakeityShake()
end
AFK.BounceWiggle.WiggleAll(1, 25)
end

-------------
-- The end --
-------------
function AFK.Cleanup(errorMessage)
behavior.SetClashImportance(AFK.StartCI)
recentbest.Restore()
selection.DeselectAll()
-- Re enable all filters
behavior.SetFiltersDisabled(false)

end

xpcall(AFK.BounceWiggle.Main, AFK.Cleanup)

----------------------------------------------------------------- ==jon copy
--Banded Worm Pairs Infinite (& Filter)
------------------------------------------------------------------------------
-- Banded Worm
------------------------------------------------------------------------------
-- Modifies Worm LWS v2 by rav3n_pl
--
-- by KarenCH
------------------------------------------------------------------------------
-- Made infinite and filters optimized by Bruno Kestemont 15/2/2015
-- v 1.1 corrected random contact map 20/9/2015
-- v 1.2 added random use of user bands (and random multiplier of their strength)
-- v 1.2.1 undo.SetUndo
-- v 1.3.0 BAND_TO_SIDECHAINS allowed
-- v 1.3.1 Fixed unideal loop bug (thanks to gitwut) (band= true)
-- v 1.3.2 Second attempt to fix it (added save.Quickload)
-- v 1.4 Dialog for filters
-- v 1.4.1 and tried again to fix GENERICFILTER on recentbest (see feedback discussion, Foldit Bug)
-- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017
-- v 1.4.2 added Ligand dialog
-- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug
-- v 1.4.4 fixed detect bonusses
-- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt

--TO DO = probability otions in advanced dialog

recipename= "Banded Worm Pairs Inf Filter 1.4.5"

undo.SetUndo(false)

-- interesting global variables that users might want to modify
PROB_CHOOSE_USERBANDS = 0.10 -- don't put it too high
PROB_CHOOSE_CONTACTMAP = 0.60
PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs
PROB_2ND_IS_BIS = 0.50
PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom
BAND_STRENGTH_DEFAULT = 1.0
PROB_BAND_TO_LIGAND= 0 -- prob band to ligand if ligand

-- less interesting global variables that users might consider modifying
BIS_LENGTH = 5.0 -- max length of a BIS
SCALE_MAXCI = 1.0
CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later

-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6 -- not used
CENTER_CARBON = 2 --this is the default atom for bands to attach to
BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well

-- variables users probably don't want to play with (SLOTS)
QS_Start = 1
QS_Best = 3
Qs_Recent = 5 -- for debugging recentbest bug
Qs_Current = 6 -- for debugging recentbest score

-- variables users really shouldn't play with
PuzzleHasContactMap = false
PuzzleHasLockedSegs = false
EndCalled = false
InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState( )
StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState( )
CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState( )
InitialClashImportance = behavior.GetClashImportance()
NonLockedSegList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt

segCnt2=segCt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
FirstLigand= segCnt2 -- 0 if no ligand
LastLigand= segCt

InitialBandCount = 0

ubcount = 0 -- user bands
ubandlist={} -- {band number, band strength, goal_length}
USERBANDS=false

PROBABLEFILTER=false
--FILTERMANAGE=false -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false
PROBABLELIGAND=false
GENERICLIGAND=false

--identifying explicitly (heavy) filtered puzzles (Contact with remain filter enabled, what is good)
--START extraction of information from puzzle metadata --Extrait des infos

function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt2 -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end

if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
end

if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end

if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()

--END extraction of information from puzzle metadata --Extrait des infos

function DialogForFilters()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Fast = filters off unless for score")
ask.l8 = dialog.AddLabel("Slow = filters always on")
ask.Fast = dialog.AddButton("Fast",1) ask.Slow = dialog.AddButton("Slow",2)

askresult=1 --dialog.Show(ask) --overridden by Jon for seamless transition

if askresult < 2 then GENERICFILTER=true
end
end

function DialogForLigand()
ask = dialog.CreateDialog(recipename)
ask.l6 = dialog.AddLabel("Ligand = move ligand")
ask.l8 = dialog.AddLabel("All = move all")
ask.Fast = dialog.AddButton("Ligand",1) ask.Slow = dialog.AddButton("All",2)

askresult=dialog.Show(ask)

if askresult> 1 then GENERICLIGAND=false
else GENERICLIGAND=true
end

end

if PROBABLEFILTER then DialogForFilters() end

if PROBABLELIGAND then DialogForLigand() end

if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows
PROB_BAND_TO_LIGAND=1
end

--START Generic Filter Management by BitSpawn 21/12/2014, updated by Bruno Kestemont 4/1/2019
--Source: http://fold.it/portal/node/1998917
--Note: GENERICFILTER must be defined elsewhere (otherwise, it will not do anything)

-- GENERICFILTER=true
-- function to copy class/table

function CopyTable(orig)

local copy = {}

for orig_key, orig_value in pairs(orig) do

copy[orig_key] = orig_value

end

return copy

end

-- functions for filters

function FiltersOn()

if filter.AreAllEnabled()==false then

filter.EnableAll()

end

end

function FiltersOff()

if filter.AreAllEnabled() then

filter.DisableAll()

end

end

-- function to overload a function

function mutFunction(func)

local currentfunc = func

local function mutate(func, newfunc)

local lastfunc = currentfunc

currentfunc = function(...) return newfunc(lastfunc, ...) end

end

local wrapper = function(...) return currentfunc(...) end

return wrapper, mutate

end

-- function to overload a class

-- to do: set the name of function

classes_copied = 0

myclcp = {}

function MutClass(cl, filters)

classes_copied = classes_copied+1

myclcp[classes_copied] = CopyTable(cl)

local mycl =myclcp[classes_copied]

for orig_key, orig_value in pairs(cl) do

myfunc, mutate = mutFunction(mycl[orig_key])

if filters==true then

mutate(myfunc, function(...)

FiltersOn()

if table.getn(arg)>1 then

-- first arg is self (function pointer), we pack from second argument

local arguments = {}

for i=2,table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

--print("No arguments")

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc

else

mutate(myfunc, function(...)

FiltersOff()

if table.getn(arg)>1 then

local arguments = {}

for i=2, table.getn(arg) do

arguments[i-1]=arg[i]

end

return mycl[orig_key](unpack(arguments))

else

return mycl[orig_key]()

end

end)

cl[orig_key] = myfunc
end
end
end

-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- if false, you have to enable filter just afterwards (otherwise unideal filter bug)
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end

--STOP Generic Filter Management
------------------------------------------------------------------------------
-- FUNCTIONS
------------------------------------------------------------------------------
function BandedWorm( pattern )
startseg=math.random(#NonLockedSegList-pattern[1]+1)
--recentbest.Save( )
FakeRecentBestSave()
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
local idx=1
for w = 1, #pattern do
save.Quickload( QS_Best ) -- new for DEBUG

--if puzzle.GetExpirationTime() --print("expired, local break")
-- break
--end

len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for iNon=startseg, #NonLockedSegList - len + 1 do
s=NonLockedSegList[iNon] -- unlocked unlocked locked locked unlocked unlocked --jon
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )

if random( ) < PROB_PUTBAND then
if random( ) < PROB_BAND_TO_LIGAND then
idx= random( Firstligand, LastLigand)
else
idx = random( s, s + len - 1 )
end
PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN )
if random( ) < 0.25 then
local idx2 = random( s, s + len - 1 )
PutSingleRandomBandToSeg( idx2, PROB_2ND_IS_BIS )
end
uBandEnabel() -- new v1.2 random adding one of the user bands
structure.LocalWiggleSelected( random(2,4) )
ManageBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 5 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then
print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ).." Score: "..TrimNum( swNew ))
end
SaveBest( )
swCurr = swNew
else
--recentbest.Restore( )
FakeRecentBestRestore()
structure.LocalWiggleSelected( 4 )
end
--recentbest.Restore( )
FakeRecentBestRestore()
ManageBands( )

--structure.WiggleAll(1)
--structure.DeleteCut(structure.GetCount())
--jon's intentionally useless functions to bide time to not crash foldit
end
startseg=1
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll()
print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) )
end

function PutSingleRandomBandToSeg( idx, probBis )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < probBis

if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) < PROB_CHOOSE_CONTACTMAP -- changed > to < BK
then
local doSidechain = random( ) < PROB_BETWEEN_USES_ATOM
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, 5, strength, atom )
end
end
return changeSucceeded
end

----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------

----------------------------------------------------------------
-- BASIC FUNCTIONALITY
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end

function TrimNum( val )
return val - val % 0.001
end

-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB )
if wantRB == nil then wantRB = false end
local s=0.0
if not USENORMALSCORE then
if wantRB then
--s = recentbest.GetEnergyScore( )
s= FakeRecentBestGetEnergyScore()
else s=current.GetEnergyScore( )
end
else
if wantRB then
--s = recentbest.GetScore( )
s = FakeRecentBestGetScore()
else s=current.GetScore( )
end
end
return s
end
function getScore( )
return internalGetScore( false )
end
function getRBScore( )
return internalGetScore( true )
end

function SaveBest( )
local score = getScore( )
if score > CurrentBestScore then
save.Quicksave( QS_Best )
CurrentBestScore = score
end
-- CheckFullScore() -- for DEBUG only
end

--START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017)

function Score() -- for BWPIF only
return current.GetScore()
end

function FakeRecentBestSave()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Recent)
else
recentbest.Save()
end
end

function FakeRecentBestRestore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
local ss=Score()
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
if se > ss then
save.Quicksave(Qs_Recent)
end
save.Quickload(Qs_Recent)
else
recentbest.Restore()
end
end

function FakeRecentBestGetScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=Score() -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetScore( )
end
end

function FakeRecentBestGetEnergyScore()
if PROBABLEFILTER then -- trying to solve the Foldit bug
save.Quicksave(Qs_Current)
recentbest.Restore() -- filter disabled (bug)
local se=current.GetEnergyScore( ) -- now with the filter
save.Quickload(Qs_Current)
return se
else
recentbest.GetEnergyScore( )
end
end

--END Debugging Recentbest Foldit Bug

--[[
function CheckFullScore() -- check without filter (only for DEBUG)
if filter.AreAllEnabled()==false then
DebugPrint ("DEBUG: filter is off, turning on")
FiltersOn()
end
end
]]--

function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end

------------- CONTACTMAP FUNCTIONS ------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end

function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end

function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end

function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end

----------------------- MATHY STUFF -----------------------
function seedRandom()
seed=os.time( )/math.abs( getScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )

-- throw away a couple of randoms
math.random( )
math.random( )
end

function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random()
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end

function randomSeg( )
return random( segCt )
end

function randomThetaPhi()
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end

function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end

-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end

function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end

function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end

local r = random( 1, 4 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return CENTER_CARBON
else
return GetTipAtomOfSeg( idx ) -- 50% of the cases
end
end

function IsUnlockedSeg( seg1 )
return not structure.IsLocked( seg1 )
end

function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end

if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end

return false
end

----------------------- BANDY STUFF -----------------------

function uMakeBands() -- list of existing user bands, strength, goal length
ubcount=band.GetCount()
for i=1, ubcount do
ubandlist[i]={i, band.GetStrength(i), band.GetGoalLength(i)} -- {band number, band strength, goal_length}
end
if #ubandlist==0 then
USERBANDS=false
else
USERBANDS=true
end
return ubandlist
end

function uBandEnabel() -- random enable a user band
if USERBANDS and PROB_CHOOSE_USERBANDS >= random(0.0,1.0) then
local maxstrength= 5
local minstrength=0.1
local bandIndex= random(1,ubcount)
local multiplier = random( 0.5,2)
local bandstrength=band.GetStrength(bandIndex)
bandstrength=ubandlist[bandIndex][2]*multiplier
if bandstrength>maxstrength then
bandstrength=maxstrength
elseif bandstrength bandstrength=minstrength
end
band.SetStrength(bandIndex, bandstrength) -- to do: random length?
band.Enable(bandIndex)
end
end

function ManageBands() --delete recipe bands, disable user bands
if ubcount==0 or ubcount==nil then band.DeleteAll()
else
local bands=band.GetCount()
if bands>ubcount then
for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici
end
end
band.DisableAll()
end

function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end

local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis)
if not ( IsUnlockedSeg( seg) ) then return false end
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands

if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end

function PutBandInSpace( idx, maxRho, strength )
local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end

-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = (maxRho * random()^(1/3)) + 0.001

-- random atom for the banded (BIS) segment
local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain
atomIndexOrigin = random( 0 , MaxatomIndexOrigin )

return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis )
end

function PutBandToRandomSeg( idx, minGap, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 30 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + minGap or idx2 < idx - minGap then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end

function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end

randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end

--------------------------------------------------------------------------
-- SETUP, CLEANUP, and MAIN
--------------------------------------------------------------------------
function CleanPuzzleState( )
SetCI( 1.0 )
selection.DeselectAll()
ManageBands()
end

function PrintState( )
local gain = getScore() - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore() ) )
print( " Total gain: "..TrimNum( gain ))
end
print( " Run time: "..os.time() - StartTime)
end

function End( errstr )
undo.SetUndo(true)
if EndCalled then return end -- no infinite recursion please
EndCalled = true

print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end

save.Quickload( QS_Best )
PrintState( )
CleanPuzzleState( )
end

function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
SCALE_MAXCI = InitialClashImportance
uMakeBands() -- new v1.2
FindAllMovableSegs( )
CheckForContactMap()
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
end

function main( )

for i= 1, 1000 do
InitializePuzzleState( )

local gain = 0.0
repeat
local s

(Sat, 11/28/2020 - 12:21  |  2 comments)


Joined: 10/10/2015
Groups: Team China

If it happens and you saved it locally, you can close the script and click New Script, discard changes

joshmiller's picture
User offline. Last seen 1 day 17 hours ago. Offline
Joined: 09/08/2017
Groups: Foldit Staff

Thanks zo3xia,

A fix for this bug is pending release.

Sitemap

Developed by: UW Center for Game Science, UW Institute for Protein Design, Northeastern University, Vanderbilt University Meiler Lab, UC Davis
Supported by: DARPA, NSF, NIH, HHMI, Amazon, Microsoft, Adobe, Boehringer Ingelheim, RosettaCommons