Icon representing a recipe

Recipe: GAB for Ligand puzzles

created by Bruno Kestemont

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

Comments