Profile
- Name
- GAB for Ligand puzzles
- ID
- 108991
- Shared with
- Public
- Parent
- None
- Children
- None
- Created on
- March 14, 2025 at 07:45 AM UTC
- Updated on
- January 09, 2026 at 13:20 PM UTC
- Description
Adapted for ligand puzzles with or without free segments
Best for
Code
--[[
GAB - Genetic Algorythm on Bands, with cuts (Bruno Kestemont)
based on Rav3n_pl
based on CartoonVillan and Crashguard303 scripts
Lua V2
Definitions:
band: randomised: start segment, end segment, length, strength
critter: set of bands
herd: set of critters
1.
- randomize needed bands
- randomly assignig them to criters
2.
- score each critter
3.
- keep critters scoring above score
- breed best critters
- breed bastards (best one + random one)
- forget about rest of critter
- randomize new critters to fill herd
VERSIONS:
2.0.5
2.0.5 Filter => Filter management added Bruno Kestemont 10/10/2013
16/04/2014: only added more score info in the log
3.0 : fuze using cuts 28/05/2014 Bruno Kestemont
3.1: cut bug fixed on deletecut 02/06/2014
3.2: using creditebest
3.3: creditbest bug fixed 02/06/2014 (do not use creditebest)
3.3.1: added Notes management and quicksaves for big gains (for archives when undo's) 4/11/2014
3.3.2: replaced shake by QuickShake 8/11/2014
3.3.3: debugged uncut
3.4.5: generic filter management and debugged segment to itself
3.4.6: detect ligand and always use ligand (optional)
3.4.7: 13/5/2015 changed small possible bugs
18/1/2016 log with archive of gains following jeff101 suggestions
reduced thresholds on Big Steps
Keeps user bands
Mutate once 5 days before the deadline or the 3 latest days for HBond puzzles
GENERICFILTER only latest days of non HBonds puzzles
FILTERMANAGE only on mid game
3.4.8: hide only hydrophobics
3.4.9 undo.SetUndo
3.4.10 fixed unideal loop bug
3.4.11 use ligand on ligand puzzle (not always)
3.4.12 don't cut on locked puzzles
3.4.13 don't cut on ligand puzzles
adapt lines 431, 648 and 476 and 501 for specific puzzles with unlocked loops
To Do: Effective filter management (was bugged), unless end Fuze and end Qstab // to verify
]]--
recipename="GAB v3.4 for specific LIGAND puzzles"
undo.SetUndo(false)
OriginalFilterSetting = behavior.GetSlowFiltersDisabled() -- new BK 8/4/2013
PROBABLEFILTER=false
HASMUTABLE=false -- NEW BK 10/10/2013
timeLeft=os.difftime(puzzle.GetExpirationTime(),os.time())/86400 -- in days
segCnt=structure.GetCount()
segCnt2=segCnt
--detect ligand
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end
HASLIGAND=segCnt2<segCnt
startLigand=segCnt2+1
stopLigand=segCnt
startUnlocked = 1 --init
stopUnlocked = segCnt-1 --init
--detect uncutable puzzle to save time
CUTTABLE = false
function Detectcuttable()
for i = 1, segCnt2-1 do
if not (structure.IsLocked(i) and structure.IsLocked(i+1))
then CUTTABLE = true
break
end
end
end
function DetectstartUnlocked()
for i = 1, segCnt2 do
if not (structure.IsLocked(i))
then startUnlocked = i
break
end
end
end
function DetectstopUnlocked()
for i = startUnlocked, segCnt2 do
if not (structure.IsLocked(i))
then stopUnlocked = i
end
end
end
if HASLIGAND then else
Detectcuttable() --disabled for ligand puzzles
if CUTTABLE then
DetectstartUnlocked()
DetectstopUnlocked()
end
end
if CUTTABLE then print("Using cuts") else print("Locked. Not using cuts") end
math.randomseed(os.clock()) -- 28/05/2014
math.random() -- 28/05/2014
p=print
userbands=band.GetCount()
p("Starting "..recipename.. " on\n puzzle "..(puzzle.GetName()))
p(' with puzzle ID '..(puzzle.GetPuzzleID())..' and '..userbands..' user-supplied bands.\n ')
--START Generic Filter Management by BitSpawn 21/12/2014
--Source: http://fold.it/portal/node/1998917
--identifying filtered puzzles and all properties
OriginalFilterSetting = behavior.GetSlowFiltersDisabled()
PROBABLEFILTER= false
GENERICFILTER=false
FILTERMANAGE= false
HASMUTABLE= false
IDEALCHECK= false
PROBABLESYM= false
SEPSIS= false
ELECTRON= false
CENTROID= false
HOTSPOT= false
HBOND=false
sym=1
function detectfilterandmut() -- new BK 10/10/2013; 13/2/2015
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters")) then
PROBABLEFILTER=true
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
if timeLeft<5 then GENERICFILTER = true end -- the only case, unless if HBonds
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs")) then
HASMUTABLE=true
IDEALCHECK=true
end
if #descrTxt>0 and (descrTxt:find("HBond Networks") or descrTxt:find("HBond Network Filter")) then
HBOND=true print("HBonds puzzle")
FILTERMANAGE=false
GENERICFILTER=false
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
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("Tetramer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else sym=6
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"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") then sym=5
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("HBond Networks") then -- new BK 17/6/2013
HBOND=true
print("HBond Networks")
end
if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=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
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()
-- 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 behavior.GetSlowFiltersDisabled() then
behavior.SetSlowFiltersDisabled(false)
end
end
function FiltersOff()
if behavior.GetSlowFiltersDisabled()==false and GENERICFILTER then -- 18/1/2016
behavior.SetSlowFiltersDisabled(true)
end
end
-- function to overload a funtion
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)
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
-- options:
energy = false --set true to seek energy in exploration puzzles; false works on all puzzles
pullCI = 0.9 --Clash Importance during pull
maxCI = 1 --maximum CI used by script
fastQstab = true -- only 1s1w after pull if true
fuzeThresh = 1 -- run fuze if we are close to best score (negative=new best score)
qstabThresh = 1 -- run qstab if score drops more than... wiggle only in other case
useLigand = false --use ligand if true
AlwaysUseLigand=true -- Warning: don't set true by default because there are some erroneous ligands
onlyMutable = false --if true use ONLY all mutable aas, no matter of always use
mutateOnce = false --if true use mutate(1) instead of shake in qstab
mutateOnceCI = 0.21 --mutate on what clashing importance
mutateAlwas = false --if true use mutate(1) instead of all shakes
if HASMUTABLE and not HBOND and timeLeft>4 and timeLeft<6 then mutateOnce = true print ("Option: mutate(1) instead of shake in qstab") end -- It's an option !
--if HASMUTABLE and not HBOND and timeLeft>4 then mutateAlwas = true print ("Option: mutate(1) instead of all shakes") end -- It's an option !
--Don't mutate just after hand fold (because of filters on AAs, better to mutate by purpose using recipes like MNW or Maaa)
--Don't mutate the last 4 days, it's slow and it will not gain.
if HASMUTABLE and HBOND and timeLeft<4 then mutateOnce = true print ("Option: mutate(1) instead of shake in qstab") end
--with HBonds, mutate a little bit later
if HASMUTABLE and HBOND and timeLeft<3 then FILTERMANAGE = true print ("Option: wiggle with filter disabled") end
--Cut functions
function cut(seg)
if CUTTABLE then
structure.InsertCut(seg)
end
end
--uncut=structure.DeleteCut -- 28/05/2014
--SLOTS:
--[[
3 is best
4 used in recipe
5 used in recipe
50-100 used to record each big steps for movie at the end
]]--
SlotStepStart=50 -- New 4/11/2014
SlotStep=SlotStepStart
herd= --herd options
{
breedBest = 5, --breed best 4 critters - all combinations => 6 kids form 4 critters, 3 form 3, 1 form 2, 9 form 5 ;]
keepBest = 3, --save up to 3 best scoring critters, rest are forgotten
breedBastards = 8, --number of best will have one random bastard
newRandom = 10, --adding new random ones each generation
maxGen= 1000, --maximum generations
shuffle = true, --set true to run critters in random order
renew=4, --create totally fresh herd after that many gens w/o improvement
}
critter= --critter options
{
minBands=2, --minimum bands
maxBands=7, --maximum bands
keepScore = 0 , --survive to next gen only if score higher than
breedScore= -20, --will breed kids only if score higher. Basttards always breed
maxLoss=30, --maximum loss by critter. set 0 to disable
}
bands= --bands options
{
minStr=0.3, --minimum band str
maxStr=1.5, --maximum band str DEFAULT was 1.1, set a little big more for unlocked loop in ligand puzzle
minChng = 3, -- minimum length change of band (negative length pull or positive length push)
maxUp = 6.1, -- maximum length change up (push)
maxDn = 6.9, -- maximum length change down (pull)(will be used negative)
minSkip = 5, --minimum segment distance (not banding segments that are too proximate)
minDist = 4, --minimum spatial distance
minLen = 2, --minimum length of created band
}
DoNotUse={--just comment lines below or add more areas to avoid
--{segCnt,segCnt}, --ligand cant be used
--{120,134},
--{1,10},
}
AlwaysUse={ --areas should be always used
--{236,236}--ligand need to be at one end
--{308,311}, --loopy
--{272,319}, --loopy
}
UseSegments= --use ONLY this segments
{
--2,3,4,5
}
-- bands by secondary structure
use=
{
Sheet=true, --set false to not band sheets
Helix=true, --set false to not band helices
Loop=true, --set false to not band loops
}
checkBoth=false --check both ends to above, if false only one end need to be true
--end of options
if HASLIGAND then useLigand=true end
if useLigand==false then
segCnt=segCnt2
end
if HASLIGAND and AlwaysUseLigand then
HBOND=true
AlwaysUse={ --areas should be always used
{startLigand,stopLigand},--ligand need to be at one end
--{194,197} --loopy
--{272,319}, --loopy
}
end
if CUTTABLE then
AlwaysUse[#AlwaysUse+1]= {startUnlocked,stopUnlocked}
end
function uncut(seg)
if CUTTABLE then
for i=1, structure.GetCount() do
structure.DeleteCut(i)
end
end
end
function CI(c)
if c>maxCI then c=maxCI end
behavior.SetClashImportance(c)
end
function round(x)--cut all afer 3-rd place
return x-x%0.001
end
function down(x)
return x-x%1
end
function Score()--return score, exploration too
if energy==true then
return current.GetEnergyScore()
else
return current.GetScore()
end
end
--local seed=recipe.GetRandomSeed() --NOT WORKING on windowz!!!
--calculate REALLY good seed from current score
seed=os.time()
seed=1/seed
while seed<10000000 do seed=seed*10 end
seed=seed-seed%1
p("Seed is: "..seed)
math.randomseed(seed)
math.random(100) -- Because the first is not random
format=string.format
--START Archive in Notes, -- New 4/11/2014
function SelectNote(recipename)
store={}
store.label=recipename or "" -- edit here the recipe name
store.note_number=structure.GetCount()
for seg=structure.GetCount(),1,-1 do
if structure.GetNote(seg)~="" then break end
store.note_number=seg
end
print(string.format("Recording results in Note for segment %i",store.note_number))
store.starting_score=current.GetScore()
--structure.SetNote(store.note_number,string.format("(%s) %.3f + FSP",user.GetPlayerName(),store.starting_score))
end
SelectNote(recipename)
function WhriteNote(loop_nb) -- all inits are in SelectNote function
local loop_count= loop_nb or 1
structure.SetNote(store.note_number,string.format("(%s) %.3f + %s(%i) %.3f",user.GetPlayerName(),store.starting_score,store.label,loop_count,current.GetScore()))
end
--END Archive in Notes
--START quick save algorithm
function QuickShake()
--[[------------------------------------------------------------------------------------------------
-- Algorithm for faster shake
REFERENCES
1. N/A
Copyright (C) 2014 Seagat2011 <http://fold.it/port/user/1992490>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
$Id$
------------------------------------------------------------------------------------------------]]--
--[[
v1
- Inception
1.2
- Loops until nogain
]]--
local idx = 1
local idx2 = structure.GetCount ()
local hash = {}
local cs = current.GetScore ()
--print ( 'Quick Shake Algorithm v1.2 SG2011 - Current Score: '..cs )
local function New ( obj )
return obj -- pass-new-obj-by-value
end
local _gses = New ( current.GetSegmentEnergySubscore )
local function SmartShake ( iters )
local gain = true
while (gain) do
gain = false
for i=idx,idx2 do
local ci = _gses ( i,'clashing' )
if ci < _gses ( i,'hiding' ) and
ci < _gses ( i,'packing' ) and
ci < _gses ( i,'ideality' ) and
ci < _gses ( i,'sidechain') and
ci < _gses ( i,'bonding' ) and
ci < _gses ( i,'other' ) and
ci < _gses ( i,'reference') then
selection.Select ( i )
end -- test ci --
end -- loop --
if selection.GetCount () then
local css = current.GetScore ()
structure.ShakeSidechainsSelected ( iters )
selection.DeselectAll ()
if current.GetScore () > css then
gain = true
end -- test --
end -- test --
end -- loopf --
end
structure.SmartShake = SmartShake
structure.SmartShake ( 1 )
--print ( 'Done.' )
end
--END quick save algorithm
function Wiggle(how, iters, minppi)
if FILTERMANAGE then behavior.SetSlowFiltersDisabled(true) end-- new BK 8/4/2013, always disable filter here
if how==nil then how="wa" end
if iters==nil then iters=6 end
if minppi==nil then minppi=0.1 end
if iters>0 then
iters=iters-1
sp=Score()
if how == "s" then
if FILTERMANAGE then behavior.SetSlowFiltersDisabled(OriginalFilterSetting) end -- new BK 8/4/2013, always back to user settings
if mutateAlwas==true then structure.MutateSidechainsSelected(1)
--else structure.ShakeSidechainsSelected(1) end
else QuickShake() end
elseif how == "wb" then structure.WiggleAll(2, true,false)
elseif how == "ws" then structure.WiggleAll(2, false,true)
elseif how == "wa" then structure.WiggleAll(2)
end
ep = Score()
ig=ep-sp
if how~="s" then
if ig > minppi then return Wiggle(how, iters, minppi) end --tail call
end
end
if FILTERMANAGE then behavior.SetSlowFiltersDisabled(OriginalFilterSetting) end -- new BK 10/10/2013, always back to user settings
end
wiggle=structure.WiggleAll -- 28/05/2014 No filter management
function SaveBest()
local g=Score()-bestScore
if g>0 then
if g>0.01 then p("Gained another "..round(g).." pts. Score: "..round(Score()).. " from "..round(ss)) end
bestScore=Score()
save.Quicksave(3)
end
end
function SaveRB()
if Score()>bestScore then
save.Quicksave(4)
recentbest.Restore() -- WARNING: it took the best including cuts
SaveBest()
save.Quickload(4)
end
end
function SaveCB() -- cut version 02/06/2014 , recent save no increment
if Score()>bestScore then
save.Quicksave(4)
recentbest.Restore() -- OK only is recentbest saved after deleting cuts
SaveBest()
save.Quickload(4)
end
end
function Qstab()
selection.SelectAll()
CI(0.1)
if mutateOnce==true then
CI(mutateOnceCI)
structure.MutateSidechainsSelected(1)
else
Wiggle("s",1)
end
if fastQstab==false then
CI(0.4)
Wiggle("wa",1)
CI(1)
Wiggle("s",1)
end
CI(1)
Wiggle()
end
function FuzeCuts(ci1) -- 28/05/2014 by pauldunn NB: no filter management here . 02/06/2014 recentbest bug fixed
local seg=math.random(structure.GetCount())
cut(seg)
CI(1)
wiggle(15,true,true)
CI(ci1) --ci(0.01)
wiggle(1,true,true)
CI(1)
wiggle(25,true,true)
uncut(seg)
recentbest.Save() -- NEW 02/06/2014 important to avoid recentbest in cut bug
wiggle(25,true,true)
CI(ci1) --ci(0.01)
wiggle(2,true,true)
CI(1)
wiggle(25,true,true)
end
function FuzeEnd()
CI(1)
Wiggle("wa",1)
Wiggle("s",1)
Wiggle()
srb()
end
function Fuze1(ci1,ci2)
CI(ci1)
Wiggle("s",1)
CI(ci2)
Wiggle("wa",1)
end
function Fuze2(ci1,ci2)
CI(ci1)
Wiggle("wa",1)
CI(1)
Wiggle("wa",1)
CI(ci2)
Wiggle("wa",1)
end
function srb()
recentbest.Restore() -- IMPORTANT: recentbest must be saved after deleting cut
SaveBest()
end
function scb()
recentbest.Restore() -- IMPORTANT: recentbest must be saved after deleting cut
SaveBest()
end
function FuzeWithCuts()
p("Fuzing with cuts...")
local scr=Score()
selection.SelectAll()
recentbest.Save()
Fuze1(0.3,0.6) FuzeEnd()
FuzeCuts(0.01) scb() -- NEW 28/05/2014, 02/06/2014
Fuze2(0.3,1) srb()
Fuze1(0.05,1) srb()
Fuze2(0.7,0.5) FuzeEnd()
FuzeCuts(0.1) scb() -- NEW 28/05/2014, 02/06/2014
Fuze1(0.07,1)
srb()
end
function random(n1,n2) --random function returns int or float depends on input vars
if n2==nil and n1==nil then
return math.random() --float
else
if n2==nil then
if n1%1==0 then
return math.random(n1) --integer
else
return math.random()*n1 --float
end
else
if n1%1==0 and n2%1==0 then
return math.random(n1,n2) --integer between
else
return math.random()*(n2-n1)+n1 --float between
end
end
end
end
function FillHerd() --fill up herd
local n=#critters
if n>0 then --fill up
n=herd.newRandom
else --fresh herd
n=herd.breedBest + herd.keepBest + herd.breedBastards
end
p("Randomizing "..n.." new critters...")
for i=1,n do
AddCritter()
end
end
function AddCritter() --create new random critter
local c={}
critterID=critterID+1
c.no=critterID
c.name=c.no..'-rnd'
c.bands={}
local r=random(critter.minBands, critter.maxBands)
for i=1,r do
c.bands[#c.bands+1]=AddBand() --{s1,s2,str,len}
end
critters[#critters+1]=c
p(c.name.." bands: "..#c.bands)
end
function AddBand() --create one random band (random push or pull)
local cnt=0
local b={}
while true do --try till die
cnt=cnt+1
local s1=random(segCnt)
if onlyMutable==true or #UseSegments>0 then
s1=UseSegments[random(#UseSegments)]
end
local s2=random(segCnt)
if s1>s2 then s1,s2=s2,s1 end --swap
if #UseSegments>0 or CanBeUsed(s1,s2) then
local str=random(bands.minStr,bands.maxStr)
local len=0
while true do --randomize correct distance
len=random(-bands.maxDn,bands.maxUp) -- the goal change from pull to push
if len<-bands.minChng or len>bands.minChng then break end
end
b={s1,s2,str,len}
break
end
if cnt>100 then
p("Sorry! Cant create band! Breaking script!")
--BreakScript() --there is no such function, so it crashes script
break
end
end
return b
end
function CanBeUsed(sg1,sg2) --checking end of bands
function ssCheck(ss)
local good=false
if use.Sheet and ss=="E" then good=true end
if use.Helix and ss=="H" then good=true end
if use.Loop and ss=="L" then good=true end
return good
end
function AreGood(s1,s2) --check that s1 and s2 can be used
local ok=true
if s2-s1<bands.minSkip then ok=false end
if ok==true and structure.GetDistance(s1,s2)<bands.minDist then ok=false end
return ok
end
local ok=AreGood(sg1,sg2)
if ok==true and #DoNotUse>0 then --none of 2 can be in that area
for i=1, #DoNotUse do
local r=DoNotUse[i]
for x=r[1],r[2] do
if x==sg1 or x==sg2 then
ok=false
break
end
end
if ok==false then break end
end
end
if ok==false then
return false --if false can`t be used
else
ok=false
if #AlwaysUse>0 then --at least one have to be there
for i=1, #AlwaysUse do
local r=AlwaysUse[i]
for x=r[1],r[2] do
if x==sg1 or x==sg2 then
ok=true
break
end
end
if ok==true then break end
end
else
ok=true
end
end
if ok==true then --check structure
ok=false
local ss1=structure.GetSecondaryStructure(sg1)
local ss2=structure.GetSecondaryStructure(sg2)
if checkBoth then
if ssCheck(ss1) and ssCheck(ss2) then ok=true end
else
if ssCheck(ss1) or ssCheck(ss2) then ok=true end
end
end
return ok
end
function deletebands() --leave user bands intact
nbands=band.GetCount()
if nbands>userbands then
for i=nbands,userbands+1,-1 do -- count down from nbands to userbands+1
band.Delete(i)
end -- for i
end -- if nbands
end -- function
function ScoreHerd() --makes the bands and score all critters from herd
save.Quickload(3)
p("Scoring "..#critters.." critters...")
save.Quicksave(5)
local herdScore=Score()
for i=1,#critters do
deletebands()
local crt=critters[i] --critter
local s=Score() --start score
local bnds=crt.bands -- b={s1,s2,str,len} (len is the random push or pull difference of length)
for b=1,#bnds do
local bnd=bnds[b]
if bnd[1] ~= bnd[2] then -- new debugging, no band to itself
local a1=5
local a2=5
local strength = 1
if bnd[1]== structure.GetCount() then a1=6 end
if bnd[2]== structure.GetCount() then a2=6 end -- changed a1 to a2 13/5/2015
if structure.GetAminoAcid(bnd[1])=='g' then a1=0 end
if structure.GetAminoAcid(bnd[2])=='g' then a2=0 end
if not structure.IsHydrophobic(bnd[1]) and bnd[4]<0 then a1=0 end -- only hiding hydrophobics
if not structure.IsHydrophobic(bnd[2]) and bnd[4]<0 then a2=0 end
if structure.IsHydrophobic(bnd[1]) and bnd[4]>0 then a1=0 end -- don't push hydropobics to water
if structure.IsHydrophobic(bnd[2]) and bnd[4]>0 then a2=0 end
band.AddBetweenSegments(bnd[1],bnd[2],a1,a2)
local bc=band.GetCount() -- latest added band
if bc==0 or bc==nil then break end -- DEBUG
if bnd[3] <10 and bnd[3] > 0 then
strength = bnd[3]
end
band.SetStrength(bc,strength) -- set strength to the created band
local len=structure.GetDistance(bnd[1],bnd[2])+bnd[4] -- distance between segments + band length change (+ or -)
if len<bands.minLen then len=bands.minLen end
band.SetGoalLength(bc,len)
--print("Band: "..bnd[1].."-"..bnd[2].." a1: "..a1.." a2: "..a2.." str: "..round(strength).." length= "..round(length))
end
end
selection.SelectAll()
CI(pullCI)
recentbest.Save()
local seg=math.random(structure.GetCount()) -- new 28/05/2014
cut(seg) -- new 28/05/2014
if HASLIGAND then
Wiggle("wa",1)
else
Wiggle("wb",1)
end
uncut(seg)-- new 28/05/2014 PROBLEM here, recentbest includes cuts
recentbest.Save() -- NEW 02/06/2014 to avoid recent best in cuts
deletebands()
CI(1)
if math.abs(s-Score()) > qstabThresh then
Qstab()
else
Wiggle()
end
if Score()-bestScore>fuzeThresh then
SaveRB()
FuzeWithCuts() -- new 02/06/2014
else
SaveRB()
end
crt.score=Score()-s
p("Critter "..crt.name.." : "..round(crt.score))
if critter.maxLoss>0 then
if Score()>herdScore-critter.maxLoss then
save.Quicksave(5)
herdScore=Score()
else
save.Quickload(5)
end
else
save.Quickload(3)
end
end
save.Quickload(3)
if band.GetCount()>0 then --clean bands from best solution (if any)
deletebands()
save.Quicksave(3)
end
end
function BreedCritters(mom,dad,t) --breed 2 critters. bands are taken randomly
local kid={}
critterID=critterID+1
kid.no=critterID
kid.name=kid.no.."-"..t..mom.no..'/'..dad.no
kid.bands={}
local mb=#mom.bands
local db=#dad.bands
if mb>db then mb,db=db,mb end --kid have bands count between mom and dad bands
local bn=random(mb,db)
for i=1,bn,2 do
kid.bands[#kid.bands+1]=mom.bands[random(#mom.bands)]
kid.bands[#kid.bands+1]=dad.bands[random(#dad.bands)]
end
p(kid.name.." bands: "..#kid.bands)
return kid
end
function KeepGood() --copy best scoring critters form last gen if score above
local newHerd={}
for i=1,herd.keepBest do
if critters[i].score>critter.keepScore and (math.abs(critters[i].score)>0.1 or critters[i].score>0) then
newHerd[#newHerd+1]=critters[i]
end
end
return newHerd
end
function SortCritters() --bubble sort
for i=1,#critters do
for j=i+1,#critters do
if critters[i].score<critters[j].score then
critters[i],critters[j]=critters[j],critters[i] --love lua :)
end
end
end
end
function BreedHerd()
p("Breeding...")
SortCritters()
newHerd=KeepGood()
for i=1, herd.breedBest do
local mom=critters[i]
if mom.score>critter.breedScore or i<2 then --breed only good ones, 1st is always breed anyway
for j=i+1, herd.breedBest do
local dad=critters[j]
newHerd[#newHerd+1]=BreedCritters(mom,dad,"kid-")
newHerd[#newHerd+1]=BreedCritters(dad,mom,"kid-")
end
end
end
for i=1, herd.breedBastards do --they will always appear ;]
local mom=critters[i]
local j=random(herd.breedBastards+1,#critters)
local dad=critters[j]
newHerd[#newHerd+1]=BreedCritters(mom,dad,"bas-")
newHerd[#newHerd+1]=BreedCritters(dad,mom,"bas-")
end
critters=newHerd
FillHerd()
end
function ShuffleHerd()
if herd.shuffle==true then
for i=1,#critters do
local r=random(#critters)
if r~=i then
critters[i],critters[r]=critters[r],critters[i]
end
end
end
end
function GAB()
if onlyMutable==true then
for i=1,segCnt do
if structure.IsMutable(i) then
UseSegments[#UseSegments+1]=i
end
end
end
if #AlwaysUse>0 then
for i=1,#AlwaysUse do
local ss=AlwaysUse[i][1]
local se=AlwaysUse[i][2]
if ss>se then ss,se=se,ss end
for j=ss,se do
UseSegments[#UseSegments+1]=j
end
end
AlwaysUse={} --added to list, no need to check later
end
bestScore=Score()
critterID=0
gen=0
ss=Score()
ssstr=(round(ss))
save.Quicksave(3)
recentbest.Save()
p("Starting "..recipename.." score: "..round(ss))
critters={}
FillHerd()
badGen=0
while true do --this is (almost) endless script ;]
undo.SetUndo(false) -- reset if gain before end of loop
genScore=Score()
gen=gen+1
p()
p("Generation: "..gen..", score: "..round(Score())..", gain: "..round(Score()-ss).." ("..(os.date())..")")
ShuffleHerd()
ScoreHerd()
save.Quickload(3)
if gen==herd.maxGen then break end --end of script
if genScore<=Score() then badGen=badGen+1 else badGen=0 end
if badGen>=herd.renew then
p("Creating fresh random herd...")
critters={}
FillHerd()
badGen=0
else
BreedHerd()
end
genEndScore=Score()
genGain=genEndScore-genScore
if gen>1 and (gen-1)==5*down((gen-1)/5) then -- New 18/1/2016 jeff101 log display
ssstr=(ssstr..'\n ') -- start new line every 5 generations
end
if genGain>=0 then
ssstr=(ssstr..' + '..round(genGain))
undo.SetUndo(true)
else
ssstr=(ssstr..' - '..(-round(genGain)))
end
p(ssstr..' = '..round(genEndScore))
WhriteNote(gen) -- New 4/11/2014
SaveBigSteps() -- New 4/11/2014 for movie at the end
end
p("Final score: "..round(Score()).." Total gain: "..round(Score()-ss))
end
--START Movie store big steps
StepScore= Score() -- New 4/11/2014
function StoreBigStep()
StepScore= Score()
save.Quicksave(SlotStep)
SlotStep=SlotStep+1
end
function SaveBigSteps() -- in order to be able to display big steps afterwards in undos
local g=Score()-StepScore
local s= Score()
if s >0 and ( (s <7000 and g>500) or (s <9000 and g>200) or (s <10000 and g>100) or (s >10000 and g>50)) then
StoreBigStep()
end
end
function MovieSteps()
undo.SetUndo(true)
for i= SlotStepStart, SlotStep do
save.Quickload(SlotStep)
end
--save.Quickload(3)
print("See remembering of big steps in undos")
end
--END Movie store big steps
function Cleanup(err) -- NEW BK 10/10/2013
print("Restoring CI, best result, filter and structures")
CI(1)
undo.SetUndo(true)
if FILTERMANAGE then behavior.SetSlowFiltersDisabled(OriginalFilterSetting) end -- new BK 8/4/2013, always back to user settings
MovieSteps() -- New 4/11/2014
save.Quickload(3)
MovieSteps()
WhriteNote(gen) -- New 4/11/2014
deletebands()
print(err)
end
-- main call
--GAB()
xpcall(GAB,Cleanup) -- NEW BK 10/10/2013
--end of script