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

Started by zo3xiaJonWeinberg

zo3xiaJonWeinberg Lv 1

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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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()<os.time() --jon

--jon
if puzzle.GetExpirationTime()<os.time() then
    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()<os.time() then --jon
        --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<minstrength then
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