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 16 hours 49 min 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