Icon representing a recipe

Recipe: Banded Worm Pairs Inf Filt 3.4.11a A090000 series

created by Elfi

Profile


Name
Banded Worm Pairs Inf Filt 3.4.11a A090000 series
ID
109356
Shared with
Public
Parent
Banded Worm Pairs Inf Filt 3.4.10
Children
Created on
February 08, 2026 at 12:04 PM UTC
Updated on
February 14, 2026 at 17:07 PM UTC
Description

Banded worm, with pairs of bands and more random action, including user bands - Filter optimization and infinite run. Share to other tracks. Sketchbook.

Best for


Code


--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 2.0 saves and loads intermediate solutions to/from PC (usefull for parallel recipes tracking) -- v 2.1 testing for sharing on each Savebest -- v 3.0 replaced Hardsave by Isaksson share -- a dialog is now always included -- v 3.1 added band to symmetrics, 31/10/2019 -- (v 3.2 is on another track) -- v 3.3 weighted scores and conditions (directly from 3.1) -- v 3.3.1 fixed conditions weighted score -- v 3.3.2 printing more info (jeff101's suggestions) -- v 3.4 sketchbook adapted: minGain -- v 3.4.1 trying to fix filter score bug -- v 3.4.2 Isaksson share 1.4 -- v 3.4.3 Isaksson share 1.5.1 -- v 3.4.4 Redesign of MAIN by TurtleByte -- v 3.4.5 Avoiding bug for old Foldit versions if structure.GetSymCount is nil -- better seedrandom by LociOiling -- added SET NOTES BY PD -- v 3.4.5 fixed sym -- v 3.4.7 fixed patternnb=table.concat(pattern, ",") for printState -- v 3.4.8b (number used by LociOiling) fixed Firstligand typo bug -- v 3.4.9 fixing quicksave bug by initializing quicksaves -- v 3.4.10 restored line recentbest.restore in function BandedWorm -- v 3.4.11a A090000 series (Length of longest contiguous block of 1's in binary expansion of n-th prime) 8/2/2026, addition by Elfi --TO DO = probability otions in advanced dialog recipename= "Banded Worm Pairs Inf Filt 3.4.11a A090000 series" 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 -- new symmetry - 20200409 by LociOiling -- OLDFOLDIT=false if filter.GetNames == nil or structure.GetSymCount == nil then OLDFOLDIT=true print("OLD FOLDIT VERSION DETECTED") end symChains = 0 -- set later symTypes = { -- indexed by symChains + 1 "monomer", -- just for completeness "dimer", "trimer", "tetramer", "pentamer", "hexamer", "heptamer", "octamer", } symTyp = "" -- actual symmetry type PROB_BAND_TO_SYMMETRIC=0 -- probablity of banding to symmetric chain -- init gives equal chance to band to any branche including first one, 0 chance if monomer -- 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( ) SStartTime=os.time() InitialClashImportance = behavior.GetClashImportance() NonLockedSegList = {} segCt = structure.GetCount() MinUnlockedSeg = 1 MaxUnlockedSeg = segCt minGain=0 -- for sketchbook (in SaveBest) 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 SKETCHBOOK=false gen=0 -- this is only to have something to write for Poldunn set notes --initializing the slots in order not to use quicksaves from before running the recipe save.Quicksave( QS_Start ) -- DEBUG, trying to debug the return to other recipe's solution save.Quicksave( QS_Best ) -- DEBUG, trying to debug the return to other recipe's solution save.Quicksave( Qs_Recent ) -- DEBUG, trying to debug the return to other recipe's solution save.Quicksave( Qs_Current ) -- DEBUG, trying to debug the return to other recipe's solution --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, 8/11/2019, 18/5/2020 local descrTxt=puzzle.GetDescription() local puzzletitle=puzzle.GetName() local sym=1 local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013, 18/5/2020 to find number of symmetrics local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCt -- 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 local function FindSymmetryFromDescription() -- for old Foldit versions 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() -- only if PROBABLESYM yet 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 end if #descrTxt>0 and (descrTxt:find("Move Limit") or descrTxt:find("SKETCHBOOK") or descrTxt:find("sketchbook")) then SKETCHBOOK=true FILTERMANAGE=false -- better no: one cut is one move GENERICFILTER=false -- better no: one cut is one move print("SKETCHBOOK") 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 filter.GetBonusTotal() ~= 0 then PROBABLEFILTER=true 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 -- new way to find symmetrics using new command if structure.GetSymCount ~= nil then -- temporary workaround until functions released to main, thanks to LociOiling symChains = structure.GetSymCount () -- set global symChains (0 f monomer, 1 if dimer etc) if symChains > 0 then PROBABLESYM=true sim = symChains + 1 -- used for printing local sw = "" sw = sim .. "-way" if sim <= #symTypes then symTyp = symTypes [ sim ] .. " (" .. sw .. ")" else symTyp = sw end print ( symTyp .. " symmetry puzzle" ) -- -- adjust default odds of banding to a symmetric chain PROB_BAND_TO_SYMMETRIC = symChains / (1 + symChains) -- if band between segs, probability to band to a symmetric. If, 1, never band to itself end else FindSymmetryFromDescription() 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 --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) --STOP Generic Filter Management ------------------------------------------------------------------------------ -- FUNCTIONS ------------------------------------------------------------------------------ function BandedWorm( pattern ) recentbest.Save( ) -- restored 1/12/2021 FakeRecentBestSave() SetCI( 1.0 ) SaveBest( ) local ss = getScore() local idx=1 for w = 1, #pattern do local ps=getScore() save.Quickload( QS_Best ) -- new for DEBUG, returning to Savebest (weighted) solution before_Solution(rnd,str_name) -- on start of each rnd or loop len = pattern[ w ] local sw = getScore() local cg=sw-ps -- correction gain when returning to savebest (weighted) solution if cg<0 then print("Returned to best weighted score, gain correction: ", TrimNum(cg)) end local swCurr = sw print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( swCurr ) ) for s=MinUnlockedSeg, MaxUnlockedSeg - len + 1 do 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( ) end local pg= getScore( ) - sw print( "Pattern gain: ".. pg ) SaveBest( ) gen = gen+1 -- added for the following Poldunn set notes --3a) Uncommend the 2 following lines and COPY from here --------------- loop_count=gen -- adapt "gen" to the name of loops used in the recipe SetNote(note_number, starting_score, recipename, loop_count) -- WARNING: and mach "recipename" and "loop_count" with the names of the recipe --3b) COPY to here after main generations or loops of the recipe -------------- end selection.DeselectAll() print( "Total BandedWormPairs gain: " .. TrimNum( getScore( ) - ss ) ) end function PutSingleRandomBandToSeg( idx, probBis ) -- to symmetric as well 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 -- START Full Score stuff by Bruno Kestemont 8/11/2019 -- Copy this at the beginning of the recipe -- Light version with total bonus only -- Use: Score= FullScore() (always full score) -- Use (weighted): WScore= FullScore(10) with weighted bonus x 10. -- if there are no bonusses, the weight has no effect, normal score is calculated. FilterWeight = 1 -- default energy= false function FullScore(weight)--return score, exploration too, weithed filter local weight = weight or 1 local result=0 local FiltersWereDisabled = not filter.AreAllEnabled() -- if true, all filters are default enabled if FiltersWereDisabled then -- I think it's necessary to be able to calculate the bonus filter.EnableAll() -- Enables all filters end local bonus = filter.GetBonusTotal() -- 0 if no filter weight=weight-1 -- reset default to 0 because all calculations bellow are with filter enables if energy==true then result= current.GetEnergyScore() else result= current.GetScore() end result= result + bonus * weight if FiltersWereDisabled then -- return to the previous state filter.DisableAll() end return result end -- END Full Score stuff -- Not for "external" use - call getScore. This could change if customers want -- something besides current or recentbest. energy = not USENORMALSCORE 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( weighted) -- NEW version for filter weighting (BY DEFAULT !!) local weighted = weighted or true local score = getScore( ) if weighted and FilterWeight > 1 then score = FullScore(FilterWeight) end if score > CurrentBestScore + minGain then -- minGain for sketchbook save.Quicksave( QS_Best ) CurrentBestScore = score after_Solution(rnd,str_name) -- after any gain else save.Quickload( QS_Best ) -- if score < CurrentBestScore + minGain then print("Mingain not met, return to saved solution") end end end --START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017) function Score(weight) -- for BWPIF only --return current.GetScore() local weight=weight or 1 return FullScore(weight) 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(weight) recentbest.Restore() -- filter disabled (bug) local se=Score(weight) -- 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(weight) -- 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 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 ----------------------- -- seedRandom -- original by KarenCH -- -- looks for a seed > 10,000,000 and < 2 ^ 32 -- -- v2 - LociOiling - 20191103 -- * added 2 ^ 32 overflow check -- function seedRandom() local seed = os.time () / math.abs ( current.GetEnergyScore () ) seed = seed % 0.001 seed = 1 / seed while seed < 10000000 do seed = seed * 1000 end while seed > 2 ^ 32 do seed = seed / 10 end seed = seed - seed % 1 print ( "Random number seed = " .. 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 randomSym( ) -- random symmetric chain to band, 0 if no symmetry if structure.GetSymCount ~= nil then return random( structure.GetSymCount ()) -- 1 or 2 if trimer else return 0 end 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 , symchain) 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=0 -- it's a band if OLDFOLDIT then bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 ) -- old Foldit Version else bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2, symchain ) end 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 ) -- adapted to sym 31/10/2019 B. Kestemont needNew = true local failedTries = 0 local doSYM = random( ) < PROB_BAND_TO_SYMMETRIC while ( needNew and failedTries < 30 ) do idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable) symchain= randomSym( ) -- 0 if no symmetry, 1 if dimer, 1 or 2 if trimer ... if not doSYM then symchain=0 end -- there are (1- PROB_BAND_TO_SYMMETRIC) chances to band to itself if symchain > 0 or idx2 > idx + minGap or idx2 < idx - minGap then needNew = false break else failedTries = failedTries + 1 end end if not needNew then --print("DEBUG idx1 - idx2 - sym ",idx, idx2, symchain) return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil , symchain) 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( ) print("#######") print("Status of main loop "..mainloop.." pattern "..patternnb) local gain = getScore() - UnweightedInitialScore local TotalGain=getScore() - UUnweightedInitialScore if gain < 0.001 then print( " No change" ) else print( " Pattern startscore: "..TrimNum( UnweightedInitialScore )) print( " Score: "..TrimNum( getScore() ) ) print( " Pattern gain: "..TrimNum( gain )) end print( " Pattern run time: "..os.time() - StartTime) print( " Total run time: "..os.time() - SStartTime) print( " Total bwpi gain: "..TrimNum( TotalGain ) .. " from "..TrimNum( UUnweightedInitialScore ).." to "..TrimNum( getScore() )) 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 --START Isaksson Share v1.5.1 --Copy this into your recipe and call before and after every period of time. --Warning: all saves onto you hard disk will have to be removed manually afterwards ! --reference: supplementary material for Koepnick et al (2019) De novo protein design by citizen scientists, Nature, volume 570, pages 390394 --https://static-content.springer.com/esm/art%3A10.1038%2Fs41586-019-1274-4/MediaObjects/41586_2019_1274_MOESM1_ESM.pdf --v1: script by Isaksson --v1.2 added the dialog (BK 1/10/2019) --v1.3 added time intervals when no gain --v1.4 PrintVerbose, check every 60 seconds --v1.5 option Temp name --v1.5.1 fixed timeleft timeLeft=os.difftime(puzzle.GetExpirationTime(),os.time())/3600 -- in hours print("Remaining time: "..timeLeft) startTime=os.time() -- in seconds userTimeInterval= 60 -- in seconds lastchecktime = startTime -- in seconds verbose = false -- print more or less info save_resolution=1000 function timeLeft() local result=os.difftime(puzzle.GetExpirationTime(),os.time())/3600 -- in hours return result end function PrintVerbose(text) if verbose then print(text) end end function ItsCheckTime() local currentTime= os.time() -- in seconds if currentTime-lastchecktime > userTimeInterval then PrintVerbose(timeLeft().." hours remaining") return true end return false end function rounding(x)--cut all afer 3-rd place return x-x%0.001 end function before_Solution(rnd,str_name) if save_resolution > 999 then return end -- New BK 1/10/2019 PrintVerbose("-Before-") hi_score = 0 hi_idx = 1 sol = save.GetSolutions() if (sol[0] == nil) then print("0 solutions found") --save.SaveSolution("auto_"..rnd) else PrintVerbose((#sol+1) .. " solutions found") for idx = 0, #sol do soli = sol[idx] if (string.find(soli.name,rnd) ~= nil) then PrintVerbose(soli.name .. " : " .. soli.score) if (soli.score > hi_score) then hi_score = soli.score hi_idx = idx end end end --print("Load the solution ...") if(current.GetEnergyScore()+save_resolution < hi_score) then save.LoadSolution(sol[hi_idx]) soli = sol[hi_idx] print("Load solution: "..soli.name.." : "..rounding(soli.score,value_score_resolution)) end end end function after_Solution(rnd,str_name) if save_resolution > 999 then return end -- New BK 1/10/2019 PrintVerbose("-After-") hi_score = 0 hi_idx = 1 sol = save.GetSolutions() if (sol[0] == nil) then print("0 solutions found") str = "auto".."-"..rnd.."-"..rounding(current.GetEnergyScore(),value_score_resolution) print("Save solution "..str) save.SaveSolution(str) else PrintVerbose((#sol+1) .. " solutions found") for idx = 0, #sol do soli = sol[idx] if (string.find(soli.name,rnd) ~= nil) then PrintVerbose(soli.name .. " : " .. soli.score) if (soli.score > hi_score) then hi_score = soli.score hi_idx = idx end end end PrintVerbose("Save the solution ...") band.DeleteAll() if(current.GetEnergyScore()-save_resolution > hi_score) then str = str_name.."-"..rnd.."-"..rounding(current.GetEnergyScore(),value_score_resolution) print("Save solution: "..str) save.SaveSolution(str) end end end str_name= ui.GetTrackName() -- copy-paste anywhere on top of the recipe rnd= "Temp" -- Warning: edit before in order to cach the right loop name --How to use: --Uncomment and insert the following 2 lines on the right places, before or after each loop: --if ItsCheckTime() then before_Solution(rnd,str_name) end -- as often as possible after score evaluations, e.g. in savebest(), or before any action --after_Solution(rnd,str_name) -- after any gain (will not save any loaded solution) --Uncommend and copy-paste the following 2x2 lines within the dialog on the right places: --ask.LabelRowBeforeSave = dialog.AddLabel("Keep save resolution to 1000 to avoid local saves") --(optional) copy-paste and adapt in the dialog --ask.save_resolution = dialog.AddSlider("Save resolution:", 1000, 0.3, 1000, 1) --copy-paste and adapt in the dialog (parameters)-- 1000 means no sharing --ask.save_Name = dialog.AddTextbox("Name2share", rnd) --rnd=ask.save_Name.value --save_resolution=ask.save_resolution.value -- copy-paste and adapt after the dialog.show --if save_resolution < 1000 then print("Checking for solutioun every "..userTimeInterval.." seconds") end --End of Isaksson Share --START SET NOTES BY PD --[[ Set note with author, recipe and big steps -- By Pauldunn This subroutine is used in much group recipe versions in various groups originally in Go Science It traces the various recipes and players that improved the current solution Module to quick copy-paste it in existing recipes, by Bruno Kestemont ]]-- --HOW TO include in a recipe? --1a) COPY from here ================ function InitNotes(recipename) local recipename=recipename or "" note_number=structure.GetCount() for seg=structure.GetCount(),1,-1 do if structure.GetNote(seg)~="" then break end note_number=seg end print(string.format("Recording "..recipename.." results in Note for segment %i",note_number)) starting_score=current.GetScore() --structure.SetNote(note_number,string.format("(%s) %.3f + %s(%i) %.3f",user.GetPlayerName(),starting_score,recipename,loop_count,current.GetScore())) end function SetNote(note_number, starting_score, recipename, loop_count) local recipename=recipename or "" local starting_score=starting_score or 0 local loop_count=loop_count or 1 --print(string.format("Recording "..recipename.." results in Note for segment %i",note_number)) structure.SetNote(note_number,string.format("(%s) %.3f + %s(%i) %.3f",user.GetPlayerName(),starting_score,recipename,loop_count,current.GetScore())) end InitNotes(recipename) -- WARNING: and match "recipename" with the recipename of the recipe --1b) COPY to here. somewhere after the options and inits of the recipe ================ --3a) Uncommend the 2 following lines and COPY from here --------------- --loop_count=gen -- adapt "gen" to the name of loops used in the recipe --SetNote(note_number, starting_score, recipename, loop_count) -- WARNING: and mach "recipename" and "loop_count" with the names of the recipe --3b) COPY to here after main generations or loops of the recipe -------------- --END SET NOTES BY PD function GetParams() dlg = dialog.CreateDialog(recipename) if PROBABLEFILTER then dlg.GENERICFILTER = dialog.AddCheckbox ( "Filters off unless for score" , GENERICFILTER ) dlg.FilterWeight= dialog.AddSlider("Filter weight= ", FilterWeight, 1, 1000, 0) end if PROBABLELIGAND then dlg.GENERICLIGAND = dialog.AddCheckbox ( "Move ligand (or all)" , GENERICLIGAND ) end if SKETCHBOOK then dlg.minGain= dialog.AddSlider("Min gain/it.", minGain, 0, 50, 1) end --dlg.LabelRowBeforeSave = dialog.AddLabel("Keep save resolution to 1000 to avoid local saves") --(optional) copy-paste and adapt in the dialog dlg.save_resolution = dialog.AddSlider("Save after gain >", 1000, 0.3, 1000, 1) --copy-paste and adapt in the dialog (parameters)-- 1000 means no sharing dlg.save_Name = dialog.AddTextbox("Name2share", rnd) dlg.ok = dialog.AddButton("OK", 1) dlg.cancel = dialog.AddButton("Cancel", 0) if dialog.Show(dlg) > 0 then save_resolution=dlg.save_resolution.value -- copy-paste and adapt after the dialog.show rnd=dlg.save_Name.value if PROBABLEFILTER then GENERICFILTER=dlg.GENERICFILTER.value FilterWeight=dlg.FilterWeight.value end if PROBABLELIGAND then GENERICLIGAND=dlg.GENERICLIGAND.value end if SKETCHBOOK then minGain= dlg.minGain.value end if GENERICLIGAND and not FirstLigand == 0 then --there should be a ligand but who knows PROB_BAND_TO_LIGAND=1 end 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 return true else print("Cancelled") return false end end function InitializePuzzleState( ) seedRandom( ) UnweightedInitialScore = getScore( ) --CurrentBestScore = InitialScore InitialScore = FullScore(FilterWeight) -- new (used in saveBest) CurrentBestScore = InitialScore -- used in saveBest 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() -- by TrutleByte 04-01-2020 local function repeatPattern(pattern, last) if last == nil then last = false end local gain = 0.0 local round = 0 local score repeat score = getScore() round = round + 1 patternnb=table.concat(pattern, ",") -- fixed by BK for <printState print("Starting round " .. round .. " of pattern " .. patternnb) BandedWorm(pattern) gain = getScore() - score until gain < 0.01 PrintState() if not last then InitializePuzzleState() end end if GetParams() then UUnweightedInitialScore = getScore() for i = 1, 1000 do mainloop = i InitializePuzzleState() print("#####################") print("Starting main loop " .. mainloop) repeatPattern({1, 2, 1, 3, 2, 2, 1, 2, 3, 3, 5, 1, 1, 2, 4, 2, 3, 4, 2, 3, 1, 4, 2, 2, 2, 2, 3, 2, 2, 3, 7, 2, 1, 2, 1, 3, 3, 2, 3, 2, 2, 2, 6, 2, 2, 3, 2, 5, 3, 3, 3, 4, 4, 5, 1, 3, 2, 4, 1, 2, 2, 1, 2, 3, 3, 4, 2, 1, 2, 3, 2, 3, 4, 3, 4, 7, 2, 2, 2, 2, 2, 2, 4, 2, 3, 3, 3, 3, 3, 4, 3, 5, 4, 4, 5, 5, 7, 1, 2, 3, 2, 2, 2, 3, 3}) repeatPattern({14, 8, 6, 7, 13, 12, 2, 10, 11}) repeatPattern({5, 7, 1, 3, 9, 6, 2, 4, 8}) repeatPattern({3, 6, 12, 4, 14, 5, 8, 2, 7}) repeatPattern({3, 8, 4, 5, 12, 6, 10, 2, 7}, true) end End() end end function Oldmain( ) -- to be deleted if GetParams() then UUnweightedInitialScore = getScore( ) for i= 1, 1000 do mainloop=i InitializePuzzleState( ) print("#####################") print("Starting main loop "..mainloop) local gain = 0.0 local round = 0 repeat local score = getScore( ) round=round+1 pattern={ 2,5,11,3,13,4,7,1,6 } patternnb="2,5,11,3,13,4,7,1,6" print("Starting round "..round.." of pattern "..patternnb) BandedWorm( pattern ) gain = getScore( ) - score until gain < 0.01 PrintState( ) InitializePuzzleState( ) local gain = 0.0 local round = 0 repeat local score = getScore( ) round=round+1 pattern={ 14,8,6,7,13,12,2,10,11 } patternnb="14,8,6,7,13,12,2,10,11" print("Starting round "..round.." of pattern "..patternnb) BandedWorm( pattern ) gain = getScore( ) - score until gain < 0.01 PrintState( ) InitializePuzzleState( ) local gain = 0.0 local round = 0 repeat local score = getScore( ) round=round+1 pattern={ 5,7,1,3,9,6,2,4,8 } patternnb="5,7,1,3,9,6,2,4,8" print("Starting round "..round.." of pattern "..patternnb) BandedWorm( pattern ) gain = getScore( ) - score until gain < 0.01 PrintState( ) InitializePuzzleState( ) local gain = 0.0 local round = 0 repeat local score = getScore( ) round=round+1 pattern={ 3,6,12,4,14,5,8,2,7 } patternnb="3,6,12,4,14,5,8,2,7" print("Starting round "..round.." of pattern "..patternnb) BandedWorm( pattern ) gain = getScore( ) - score until gain < 0.01 PrintState( ) InitializePuzzleState( ) local gain = 0.0 local round = 0 repeat local score = getScore( ) round=round+1 pattern={ 3,8,4,5,12,6,10,2,7} patternnb="3,8,4,5,12,6,10,2,7" print("Starting round "..round.." of pattern "..patternnb) BandedWorm( pattern ) gain = getScore( ) - score until gain < 0.01 PrintState( ) end End( ) end end xpcall( main, End )

Comments


Elfi Lv 1

Isn't an improvement as intended. I wish I could edit or delete the recipe.