Icon representing a recipe

Recipe: Banded Worm Pairs Inf Filt 1.4.9

created by LociOiling

Profile


Name
Banded Worm Pairs Inf Filt 1.4.9
ID
104960
Shared with
Public
Parent
Banded Worm Pairs Inf Filt 1.4.8
Children
Created on
June 29, 2021 at 05:46 AM UTC
Updated on
June 29, 2021 at 05:46 AM UTC
Description

Banded worm, with pairs of bands and more random action, including user bands - Filter optimization and infinite run. V1.4.9 fixes a bug when ligands are present.

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 DISABLEFILTERS on recentbest (see feedback discussion, Foldit Bug) -- replaced by FakeRecentBestSave() and FakeRecentBestRestore() 29/8/2017 -- v 1.4.2 added Ligand dialog -- v 1.4.3 systematic display of current score (Greg's suggestion), again fixing filter bug -- v 1.4.4 fixed detect bonusses -- v 1.4.5 fixed segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt -- v 1.4.6 LociOiling - 20200409 -- use new functions to positively detect filters and symmetry -- obsessive-compulsive reformatting, tweaking, and fiddling -- put all patterns in a table, steamline the mainline -- eliminate repeated calls to InitializePuzzleState -- move initial dialogs -- eliminate getScore and unused getRBScore, use current.GetEnergyScore -- upgrade cleanup function -- upgrade seedRandom function -- v 1.4.7 LociOiling - 20200427 -- eliminate crash on TrimNum at end of pattern in v 1.4.6 -- combine filter and ligand dialogs into single dialog -- add new settings to main dialog -- rework filter and ligand logic slightly -- don't keep checking for contact info in PutSingleRandomBandToSeg -- v 1.4.8 LociOiling - 20200429 -- detect and avoid multiple ranges of locked segments -- (added "SLT", Timo van der Laan's segment set and list module, for unlockeds) -- avoid repeated calls to GetAminoAcid -- v 1.4.9 LociOiling - 20210629 -- fix a ligand-related problem due to typo -- ReVersion = "Banded Worm Pairs Inf Filter 1.4.9" undo.SetUndo(false) -- interesting global variables that users might want to modify LILWIGGLE = 5 -- cycles for short Local Wiggle Selected BIGWIGGLE = 20 -- cycles for longer Local Wiggle Selected WF = 1 -- wiggle factor, for WiggleSelected 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_2NDBAND = 0.25 PROB_BIS_NOT_BETWEEN = 0.25 -- do we prefer BIS or bands between segs PROB_2ND_IS_BIS = 0.50 PROB_BETWEEN_USES_ATOM = 0.50 -- between: should wiggled seg have band from non-default atom BAND_STRENGTH_DEFAULT = 1.0 PROB_BAND_TO_LIGAND = 0 -- prob band to ligand if ligand -- less interesting global variables that users might consider modifying BIS_LENGTH = 5.0 -- max length of a BIS SCALE_MAXCI = 1.0 CONTACTMAP_THRESHOLD = 0.25 -- min heat of contacts USENORMALSCORE = true -- exploration puzzles would set false DEBUGRUN = false -- mostly goes with DebugPrint, but could get more use later -- atoms in an amino acid BETA_CARBON = 5 TERMINAL_BETA = 6 -- not used CENTER_CARBON = 2 --this is the default atom for bands to attach to BAND_TO_SIDECHAINS = true -- New BK, some bands to sidechains as well -- variables users probably don't want to play with (SLOTS) QS_Start = 1 QS_Best = 3 Qs_Recent = 5 -- for debugging recentbest bug Qs_Current = 6 -- for debugging recentbest score -- variables users really shouldn't play with PuzzleHasContactMap = false PuzzleHasLockedSegs = false EndCalled = false InitialScore = 0.0 -- not important: will be reinitialized in InitializePuzzleState () StartTime = 0 -- not important: will be reinitialized in InitializePuzzleState () CurrentBestScore = 0 -- not important: will be reinitialized in InitializePuzzleState () InitialClashImportance = behavior.GetClashImportance() AcidList = {} NonLockedSegSet = {} NonLockedBool = {} segCt = structure.GetCount() -- -- new symmetry - 20200409 -- symChains = 0 -- set later symTypes = { -- indexed by symChains + 1 "monomer", -- just for completeness "dimer", "trimer", "tetramer", "pentamer", "hexamer", "heptamer", "octamer", } symTyp = "" -- actual symmetry type symP = 1.0 -- probablity of banding to symmetric chain segCnt2 = segCt while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end HASLIGAND = false FirstLigand = segCnt2 LastLigand = segCt if segCnt2 ~= segCt then HASLIGAND = true end InitialBandCount = 0 ubcount = 0 -- user bands ubandlist={} -- {band number, band strength, goal_length} USERBANDS=false HASFILTERS = false FILTERNAMES = {} DISABLEFILTERS = false -- -- variables for BitSpawn overrides -- classes_copied = 0 myclcp = {} INCLUDELIGAND = false SLT = { -- SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT-- --[[ SLT - Segment set, list, and type module v0.5 Includes the segment set and list module and the segment type module developed by Timo van der Laan. The following Foldit recipes contain the original code for these modules: * Tvdl enhanced DRW 3.1.1 - https://fold.it/portal/recipe/102840 * TvdL DRemixW 3.1.2 - https://fold.it/portal/recipe/102398 The "set and list" module performs logical operations and transformations on tables containing ranges of segment. The segment type module find lists and sets of segments with various properties, such as selected or frozen. A "list" is one-dimensional table containing segment numbers. A "set" is a two-dimensional table containing segment number ranges. For example, given a list of segments: list = { 1, 2, 3, 7, 8, 11, 13, 14, 15 } the corresponding set is: set = { { 1, 3 }, { 7, 8 }, { 11, 11 }, {13, 15 } } Most functions assume that the sets are well-formed, meaning they are ordered and have no overlaps. As an example, the method FindUnlocked returns a set of all the unlocked segments in a puzzle. The method can be called as follows: funlocked = SLT:FindUnlocked () The return value funlocked is a two-dimensional table containing ranges of unlocked segments. In source format, the table might look like this: funlocked = { { 27, 35, }, { 47, 62, }, { 78, 89, }, } The code to use this table would look like: -- -- for each range of segments -- for ii = 1, #funlocked do -- -- for each segment in the range, so something -- for jj = funlocked [ ii ] [ 1 ], funlocked [ ii ] [ 2 ] do ... something ... end end This psuedo-module is a table containing a mix of data fields and methods. This wiki article explains the packaging technique: https://foldit.fandom.com/wiki/Lua_packaging_for_Foldit Authorship ---------- Original by Timo van der Laan: 02-05-2012 TvdL Free to use for non commercial purposes French comments by Bruno Kestemont and perhaps others. v0.1 - LociOiling + extract and reformat code v0.2 - LociOiling - 2017/11/03 + add primary FindUnlocked function v0.3 - LociOiling + add FindRotamers function v0.4 - LociOiling - 2019/10/29 + package as table + remove dependencies on segCnt and segCnt2 v0.5 - LociOiling - 2019/12/17 + convert functions to methods, update internal references ]]-- -- -- variables -- segCnt = nil, -- segment count, not adjusted for ligands segCnt2 = nil, -- segment count, not including terminal ligands -- -- initializer - can be called externally, but invoked inline if segCnt or segCnt2 are nil -- Init = function ( self ) self.segCnt = structure.GetCount () self.segCnt2 = self.segCnt while structure.GetSecondaryStructure ( self.segCnt2 ) == "M" do self.segCnt2 = self.segCnt2 - 1 end end, -- -- segment set and list functions -- SegmentListToSet = function ( self, list ) -- retirer doublons local result = {} local ff = 0 local ll = -1 table.sort ( list ) for ii = 1, #list do if list [ ii ] ~= ll + 1 and list [ ii ] ~= ll then -- note: duplicates are removed if ll > 0 then result [ #result + 1 ] = { ff, ll } end ff = list [ ii ] end ll = list [ ii ] end if ll > 0 then result [ #result + 1 ] = { ff, ll } end return result end, SegmentSetToList = function ( self, set ) -- faire une liste a partir d'une zone local result = {} for ii = 1, #set do for kk = set [ ii ] [ 1 ], set [ ii ] [ 2 ] do result [ #result + 1 ] = kk end end return result end, SegmentCleanSet = function ( self, set ) -- Makes it well formed return self:SegmentListToSet ( self:SegmentSetToList ( set ) ) end, SegmentInvertSet = function ( self, set, maxseg ) -- -- Gives back all segments not in the set -- maxseg is added for ligand -- local result={} if maxseg == nil then maxseg = structure.GetCount () end if #set == 0 then return { { 1, maxseg } } end if set [ 1 ] [ 1 ] ~= 1 then result [ 1 ] = { 1, set [ 1 ] [ 1 ] - 1 } end for ii = 2, #set do result [ #result + 1 ] = { set [ ii - 1 ] [ 2 ] + 1, set [ ii ] [ 1 ] - 1, } end if set [ #set ] [ 2 ] ~= maxseg then result [ #result + 1 ] = { set [ #set ] [ 2 ] + 1, maxseg } end return result end, SegmentInvertList = function ( self, list ) if self.segCnt2 == nil then self:Init () end table.sort ( list ) local result = {} for ii = 1, #list - 1 do for jj = list [ ii ] + 1, list [ ii + 1 ] - 1 do result [ #result + 1 ] = jj end end for jj = list [ #list ] + 1, self.segCnt2 do result [ #result + 1 ] = jj end return result end, SegmentInList = function ( self, seg, list ) -- verifier si segment est dans la liste table.sort ( list ) for ii = 1, #list do if list [ ii ] == seg then return true elseif list [ ii ] > seg then return false end end return false end, SegmentInSet = function ( self, set, seg ) --verifie si segment est dans la zone for ii = 1, #set do if seg >= set [ ii ] [ 1 ] and seg <= set [ ii ] [ 2 ] then return true elseif seg < set [ ii ] [ 1 ] then return false end end return false end, SegmentJoinList = function ( self, list1, list2 ) -- fusionner 2 listes de segments local result = list1 if result == nil then return list2 end for ii = 1, #list2 do result [ #result + 1 ] = list2 [ ii ] end table.sort ( result ) return result end, SegmentJoinSet = function ( self, set1, set2 ) --fusionner (ajouter) 2 zones return self:SegmentListToSet ( self:SegmentJoinList ( self:SegmentSetToList ( set1 ), self:SegmentSetToList ( set2 ) ) ) end, SegmentCommList = function ( self, list1, list2 ) -- chercher intersection de 2 listes local result = {} table.sort ( list1 ) table.sort ( list2 ) if #list2 == 0 then return result end local jj = 1 for ii = 1, #list1 do while list2 [ jj ] < list1 [ ii ] do jj = jj + 1 if jj > #list2 then return result end end if list1 [ ii ] == list2 [ jj ] then result [ #result + 1 ] = list1 [ ii ] end end return result end, SegmentCommSet = function ( self, set1, set2 ) -- intersection de 2 zones return self:SegmentListToSet ( self:SegmentCommList ( self:SegmentSetToList ( set1 ), self:SegmentSetToList ( set2 ) ) ) end, SegmentSetMinus = function ( self, set1, set2 ) return self:SegmentCommSet ( set1, self:SegmentInvertSet ( set2 ) ) end, SegmentPrintSet = function ( self, set ) print ( self:SegmentSetToString ( set ) ) end, SegmentSetToString = function ( self, set ) -- pour pouvoir imprimer local line = "" for ii = 1, #set do if ii ~= 1 then line = line .. ", " end line = line .. set [ ii ] [ 1 ] .. "-" .. set [ ii ] [ 2 ] end return line end, SegmentSetInSet = function ( self, set, sub ) if sub == nil then return true end -- -- Checks if sub is a proper subset of set -- for ii = 1, #sub do if not self:SegmentRangeInSet ( set, sub [ ii ] ) then return false end end return true end, SegmentRangeInSet = function ( self, set, range ) -- verifier si zone est dans suite if range == nil or #range == 0 then return true end local bb = range [ 1 ] local ee = range [ 2 ] for ii = 1, #set do if bb >= set [ ii ] [ 1 ] and bb <= set [ ii ] [ 2 ] then return ( ee <= set [ ii ] [ 2 ] ) elseif ee <= set [ ii ] [ 1 ] then return false end end return false end, SegmentSetToBool = function ( self, set ) --vrai ou faux pour chaque segment utilisable ou non local result = {} for ii = 1, structure.GetCount () do result [ ii ] = self:SegmentInSet ( set, ii ) end return result end, -- -- End of Segment Set module -- -- -- Module Find Segment Types -- FindMutablesList = function ( self ) if self.segCnt2 == nil then self:Init () end local result = {} for ii = 1, self.segCnt2 do if structure.IsMutable ( ii ) then result [ #result + 1 ] = ii end end return result end, FindMutables = function ( self ) return self:SegmentListToSet ( self:FindMutablesList () ) end, FindFrozenList = function ( self ) if self.segCnt2 == nil then self:Init () end local result = {} for ii = 1, self.segCnt2 do if freeze.IsFrozen ( ii ) then result [ #result + 1 ] = ii end end return result end, FindFrozen = function ( self ) return self:SegmentListToSet ( self:FindFrozenList () ) end, FindLockedList = function ( self ) if self.segCnt2 == nil then self:Init () end local result = {} for ii = 1, self.segCnt2 do if structure.IsLocked ( ii ) then result [ #result + 1 ] = ii end end return result end, FindLocked = function ( self ) return self:SegmentListToSet ( self:FindLockedList () ) end, FindUnlockedList = function ( self ) if self.segCnt2 == nil then self:Init () end local result = {} for ii = 1, self.segCnt2 do if not structure.IsLocked ( ii ) then result [ #result + 1 ] = ii end end return result end, FindUnlocked = function ( self ) return self:SegmentListToSet ( self:FindUnlockedList () ) end, FindZeroScoreList = function ( self ) if self.segCnt == nil then self:Init () end local result = {} for ii = 1, self.segCnt do local sub = 0 for jj = 1, #SubScores do sub = sub + current.GetSegmentEnergySubscore ( ii, SubScores [ jj ] [ 1 ] ) end if sub == 0 then result [ #result + 1 ] = ii end end return result end, FindZeroScore = function ( self ) return self:SegmentListToSet ( self:FindZeroScoreList () ) end, FindRotamersList = function ( self ) if self.segCnt == nil then self:Init () end local result = {} for ii = 1, self.segCnt do local rots = rotamer.GetCount ( ii ) if rots > 1 then result [ #result + 1 ] = ii end end return result end, FindRotamers = function ( self ) return self:SegmentListToSet ( self:FindRotamersList () ) end, FindSelectedList = function ( self ) if self.segCnt == nil then self:Init () end local result = {} for ii = 1, self.segCnt do if selection.IsSelected ( ii ) then result [ #result + 1 ] = ii end end return result end, FindSelected = function ( self ) return self:SegmentListToSet ( self:FindSelectedList () ) end, FindAAtypeList = function ( self, aa ) if self.segCnt2 == nil then self:Init () end local result = {} for ii = 1, self.segCnt2 do if structure.GetSecondaryStructure ( ii ) == aa then result [ #result + 1 ] = ii end end return result end, FindAAtype = function ( self, aa ) return self:SegmentListToSet ( self:FindAAtypeList ( aa ) ) end, FindAminotype = function ( self, at ) --NOTE: only this one gives a list not a set if self.segCnt2 == nil then self:Init () end local result={} for ii = 1, self.segCnt2 do if structure.GetAminoAcid ( ii ) == at then result [ #result + 1 ] = ii end end return result end, }-- SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT-- --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; LociOiling 20200405 -- -- check for symmetry puzzle -- if structure.GetSymCount ~= nil then -- temporary workaround until functions released to main symChains = structure.GetSymCount () -- set global symChains if symChains > 0 then local sc = symChains + 1 local sw = "" sw = sc .. "-way" if sc <= #symTypes then symTyp = symTypes [ sc ] .. " (" .. sw .. ")" else symTyp = sw end print ( symTyp .. " symmetry puzzle" ) -- -- adjust default odds of banding to a symmetric chain -- symP = symP - ( 1 / sc ) -- all we are saying is give Ps a chance end end -- -- determine if filters apply -- FILTERNAMES = filter.GetNames () if #FILTERNAMES > 0 then HASFILTERS = true DISABLEFILTERS = true end return end --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: DISABLEFILTERS must be defined elsewhere (otherwise, it will not do anything) -- DISABLEFILTERS=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 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 --STOP Generic Filter Management function BandedWorm ( pattern ) --recentbest.Save( ) FakeRecentBestSave () SetCI ( 1.0 ) SaveBest () local ss = current.GetEnergyScore () local idx = 1 for ww = 1, #pattern do save.Quickload( QS_Best ) -- new for DEBUG len = pattern [ ww ] local sw = current.GetEnergyScore () local swCurr = sw print( "Starting BandedWormPairs of len " .. len .. ", score: ".. TrimNum( sw ) ) for uu = 1, #NonLockedSegSet do for ss = NonLockedSegSet [ uu ] [ 1 ], NonLockedSegSet [ uu ] [ 2 ] - len + 1 do selection.DeselectAll () selection.SelectRange ( ss, ss + len - 1 ) if random () < PROB_PUTBAND then if HASLIGAND and random () < PROB_BAND_TO_LIGAND then idx = random ( FirstLigand, LastLigand ) else idx = random ( ss, ss + len - 1 ) end PutSingleRandomBandToSeg( idx, PROB_BIS_NOT_BETWEEN ) if random () < PROB_2NDBAND then local idx2 = random ( ss, ss + len - 1 ) PutSingleRandomBandToSeg ( idx2, PROB_2ND_IS_BIS ) end uBandEnable () -- new v1.2 random adding one of the user bands structure.LocalWiggleSelected ( random ( 2, 4 ) ) ManageBands () structure.WiggleAll ( 2 * WF ) end structure.LocalWiggleSelected ( LILWIGGLE ) local swNew = current.GetEnergyScore () local gain = swNew - swCurr if gain > 0 then structure.LocalWiggleSelected ( BIGWIGGLE ) --recentbest.Restore () FakeRecentBestRestore () ManageBands () swNew = current.GetEnergyScore () gain = swNew - swCurr if TrimNum ( gain ) > 0 then print( ">>>> At " .. ss .. ", gain: ".. TrimNum ( gain )..", score: ".. TrimNum ( swNew ) ) end SaveBest () swCurr = swNew else --recentbest.Restore () FakeRecentBestRestore () structure.LocalWiggleSelected ( 4 ) end --recentbest.Restore () FakeRecentBestRestore () ManageBands () end end print( "Pattern gain: ".. current.GetEnergyScore () - sw ) SaveBest () end selection.DeselectAll() print ( "Total BandedWormPairs gain: " .. TrimNum ( current.GetEnergyScore () ) - ss ) end function PutSingleRandomBandToSeg ( idx, probBis ) changeSucceeded = false local strength = random( 0.5 * BAND_STRENGTH_DEFAULT, 1.5 * BAND_STRENGTH_DEFAULT, true ) local doBIS = random( ) < probBis if doBIS then changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength ) else if PuzzleHasContactMap and 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 ---------------------------------------------------------------- -- BASIC FUNCTIONALITY ---------------------------------------------------------------- function DebugPrint( str ) if DEBUGRUN then print( str ) end end function TrimNum ( val ) return val - val % 0.001 end function SaveBest( ) local score = current.GetEnergyScore ( ) if score > CurrentBestScore then save.Quicksave( QS_Best ) CurrentBestScore = score end -- CheckFullScore() -- for DEBUG only end --START Debugging Recentbest Foldit Bug Temporary solution of Foldit bug (BK 29/8/2017) function Score() -- for BWPIF only return current.GetScore() end function FakeRecentBestSave() if HASFILTERS then -- trying to solve the Foldit bug save.Quicksave(Qs_Recent) else recentbest.Save() end end function FakeRecentBestRestore() if HASFILTERS then -- trying to solve the Foldit bug local ss=Score() recentbest.Restore() -- filter disabled (bug) local se=Score() -- now with the filter if se > ss then save.Quicksave(Qs_Recent) end save.Quickload(Qs_Recent) else recentbest.Restore() end end function FakeRecentBestGetScore() if HASFILTERS then -- trying to solve the Foldit bug save.Quicksave(Qs_Current) recentbest.Restore() -- filter disabled (bug) local se=Score() -- now with the filter save.Quickload(Qs_Current) return se else recentbest.GetScore( ) end end function FakeRecentBestGetEnergyScore() if HASFILTERS then -- trying to solve the Foldit bug save.Quicksave(Qs_Current) recentbest.Restore() -- filter disabled (bug) local se=current.GetEnergyScore( ) -- now with the filter save.Quickload(Qs_Current) return se else recentbest.GetEnergyScore( ) end end --END Debugging Recentbest Foldit Bug --[[ function CheckFullScore() -- check without filter (only for DEBUG) if filter.AreAllEnabled()==false then DebugPrint ("DEBUG: filter is off, turning on") FiltersOn() end end ]]-- function SetCI( ci ) behavior.SetClashImportance( SCALE_MAXCI * ci ) end ------------- CONTACTMAP FUNCTIONS ------------ function CheckForContactMap( ) PuzzleHasContactMap = false local saveval = 0.0 for i=1, segCt-1 do for j=i+1, segCt do val = contactmap.GetHeat( i, j ) if saveval ~= 0.0 and val ~= saveval then PuzzleHasContactMap = true ContactMapScore = GetContactScore( ) return -- all we wanted to know was whether scores exist end if saveval == 0.0 then saveval = val end end end return end function SegHasContactData( segIn, heatThreshold ) for i = 1, segCt do if i < segIn - 1 or i > segIn + 1 then if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end end end return false end function InitializeContactMapSegList( heatThreshold ) HasContactMapSegList = {} for i = 1, segCt do if SegHasContactData( i, heatThreshold ) then HasContactMapSegList[ #HasContactMapSegList + 1 ] = i end end end function GetContactScore( ) if not PuzzleHasContactMap then return 0 end local sum = 0.0 local segCt = structure.GetCount( ) for i = 1,segCt-1 do for j = i + 1, segCt do if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end end end return sum end ----------------------- MATHY STUFF ----------------------- -- -- 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 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( AcidList [ 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 ----------------------- 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 uBandEnable() -- random enable a user band if USERBANDS and PROB_CHOOSE_USERBANDS >= random ( 0.0, 1.0 ) then local maxstrength= 5 local minstrength=0.1 local bandIndex= random(1,ubcount) local multiplier = random( 0.5,2) local bandstrength=band.GetStrength(bandIndex) bandstrength=ubandlist[bandIndex][2]*multiplier if bandstrength>maxstrength then bandstrength=maxstrength elseif bandstrength<minstrength then bandstrength=minstrength end band.SetStrength(bandIndex, bandstrength) -- to do: random length? band.Enable(bandIndex) end end function ManageBands() --delete recipe bands, disable user bands if ubcount==0 or ubcount==nil then band.DeleteAll() else local bands=band.GetCount() if bands>ubcount then for i=bands, ubcount+1, -1 do band.Delete(i) end -- TO DO: il reste peut etre une bande ici end end band.DisableAll() end function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 ) if not ( NonLockedBool [ seg1 ] or NonLockedBool [ seg2 ] ) then return false end if atom1 == nil then atom1 = CENTER_CARBON end if atom2 == nil then atom2 = CENTER_CARBON end local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 ) if bIdx ~= band.GetCount( ) then DebugPrint( "failed to add band from "..seg1.." to "..seg2) return false end if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands if goalLength ~= nil then band.SetGoalLength( bIdx, goalLength ) end if strength ~= nil and strength > 0.0 then band.SetStrength( bIdx, strength ) end return true end function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis) if not ( NonLockedBool [ seg ] ) then return false end local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = atomIndexOrigin or CENTER_CARBON, atomIndexXAxis or 0, atomIndexYAxis or 0 local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis ) if bIdx ~= band.GetCount( ) then return false end if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands if goalLength ~= nil then band.SetGoalLength( bIdx, goalLength ) end if strength ~= nil and strength ~= 0.0 then band.SetStrength( bIdx, strength ) end return true end function PutBandInSpace ( idx, maxRho, strength ) local atomIndexOrigin, atomIndexXAxis, atomIndexYAxis = 0,0,0 -- x and y not used actually local idx2, idx3 if idx < segCt then idx2 = idx + 1 else idx2 = idx - 2 end if idx > 1 then idx3 = idx - 1 else idx3 = idx + 2 end -- random point in sphere of radius maxRho local theta, phi = randomThetaPhi( ) local rho = (maxRho * random()^(1/3)) + 0.001 -- random atom for the banded (BIS) segment local MaxatomIndexOrigin = PickAtomNumberForBand( idx ) -- it's the end of the sidechain atomIndexOrigin = random( 0 , MaxatomIndexOrigin ) return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength, atomIndexOrigin, atomIndexXAxis, atomIndexYAxis ) end function PutBandToRandomSeg( idx, minGap, strength, atom ) needNew = true local failedTries = 0 while ( needNew and failedTries < 30 ) do idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable) if idx2 > idx + minGap or idx2 < idx - minGap then needNew = false break else failedTries = failedTries + 1 end end if not needNew then return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil ) end return false end function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains ) changeSucceeded = false local hotList = {} for i = 1, segCt-2 do for j = i+2, segCt do local heat = contactmap.GetHeat(i, j) if heat >= heatThreshold and not contactmap.IsContact( i , j ) then hotList[ #hotList + 1] = { i, j, heat } end end end randomizeIndexList( hotList ) for i=1, math.min( ctBands, #hotList ) do local atom1 = nil local atom2 = nil if BAND_TO_SIDECHAINS and doSidechains then atom1 = PickAtomNumberForBand( hotList[i][1] ) atom2 = PickAtomNumberForBand( hotList[i][2] ) end local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, doTip1, doTip2 ) changeSucceeded = ch or changeSucceeded end return changeSucceeded end -------------------------------------------------------------------------- -- SETUP and MAIN -------------------------------------------------------------------------- function PrintState( ) local gain = current.GetEnergyScore () - InitialScore if gain < 0.001 then print( "No change" ) else print( "Start score: " .. TrimNum ( InitialScore )) print( "Final score: " .. TrimNum ( current.GetEnergyScore () ) ) print( "Total gain: " .. TrimNum ( gain ) ) end local rtime = os.time() - StartTime print( "Run time: ".. rtime .. " seconds, " .. TrimNum ( rtime / 3600 ) .. " hours" ) end function InitializePuzzleState () seedRandom () for ii = 1, segCnt2 do AcidList [ #AcidList + 1 ] = structure.GetAminoAcid ( ii ) end detectfilterandmut() if HASLIGAND then PROB_BAND_TO_LIGAND = 1 end InitialScore = current.GetEnergyScore () CurrentBestScore = InitialScore StartTime = os.time() save.Quicksave ( QS_Start ) save.Quicksave ( QS_Best ) InitialClashImportance = behavior.GetClashImportance () SCALE_MAXCI = InitialClashImportance uMakeBands () -- new v1.2 NonLockedSegSet = SLT:FindUnlocked () NonLockedBool = SLT:SegmentSetToBool ( NonLockedSegSet ) CheckForContactMap () if PuzzleHasContactMap then InitializeContactMapSegList ( CONTACTMAP_THRESHOLD ) end end function GetParam () local askresult = 0 local dlg = dialog.CreateDialog ( ReVersion ) dlg.LILWIGGLE = dialog.AddSlider ( "Short local wiggle", LILWIGGLE, 1, 10, 0 ) dlg.BIGWIGGLE = dialog.AddSlider ( "Long local wiggle", BIGWIGGLE, 15, 50, 0 ) dlg.WF = dialog.AddSlider ( "Wiggle factor", WF, 1, 5, 0 ) dlg.PROBL1 = dialog.AddLabel ( "----probability sliders----" ) dlg.PROBL2 = dialog.AddLabel ( "0 = no chance, 1 = sure thing" ) dlg.PROBL3 = dialog.AddLabel ( "\"BiS\" = band in space, one end not on a segment" ) dlg.PROB_PUTBAND = dialog.AddSlider ( "1st band", PROB_PUTBAND, 0, 1, 2 ) dlg.PROB_BIS_NOT_BETWEEN = dialog.AddSlider ( "1st BiS", PROB_BIS_NOT_BETWEEN, 0, 1, 2 ) dlg.PROB_2NDBAND = dialog.AddSlider ( "2nd band", PROB_2NDBAND, 0, 1, 2 ) dlg.PROB_2ND_IS_BIS = dialog.AddSlider ( "2nd BiS", PROB_2ND_IS_BIS, 0, 1, 2 ) dlg.PROB_BETWEEN_USES_ATOM = dialog.AddSlider ( "random atom", PROB_BETWEEN_USES_ATOM, 0, 1, 2 ) if PuzzleHasContactMap then dlg.PROB_CHOOSE_CONTACTMAP = dialog.AddSlider ( "use contact", PROB_CHOOSE_CONTACTMAP , 0, 1, 2 ) end dlg.PROBL9 = dialog.AddLabel ( "--end of probability sliders--" ) if HASFILTERS then dlg.l0 = dialog.AddLabel ( "" ) dlg.l2 = dialog.AddLabel ( #FILTERNAMES .. " Filters for this puzzle" ) dlg.DISABLEFILTERS = dialog.AddCheckbox ( "Disable filters?", DISABLEFILTERS ) end if HASLIGAND then dlg.INCLUDELIGAND = dialog.AddCheckbox ( "Include ligand(s)?", INCLUDELIGAND ) end dlg.ok = dialog.AddButton ( "OK", 1 ) dlg.cancel = dialog.AddButton ( "Cancel", 0 ) repeat askresult = dialog.Show ( dlg ) if askresult > 0 then LILWIGGLE = dlg.LILWIGGLE.value BIGWIGGLE = dlg.BIGWIGGLE.value WF = dlg.WF.value PROB_PUTBAND = dlg.PROB_PUTBAND.value PROB_BIS_NOT_BETWEEN = dlg.PROB_BIS_NOT_BETWEEN.value PROB_2NDBAND = dlg.PROB_2NDBAND.value PROB_2ND_IS_BIS = dlg.PROB_2ND_IS_BIS.value PROB_BETWEEN_USES_ATOM = dlg.PROB_BETWEEN_USES_ATOM.value if PuzzleHasContactMap then PROB_CHOOSE_CONTACTMAP = dlg.PROB_CHOOSE_CONTACTMAP.value end if HASFILTERS then DISABLEFILTERS = dlg.DISABLEFILTERS.value end if HASLIGAND then INCLUDELIGAND = dlg.INCLUDELIGAND.value end end until askresult < 2 return askresult > 0 end function main () local patternz = { { 2, 5, 11, 3, 13, 4, 7, 1, 6, }, -- 1 { 14, 8, 6, 7, 13, 12, 2, 10, 11, }, -- 2 { 5, 7, 1, 3, 9, 6, 2, 4, 8, }, -- 3 { 3, 6, 12, 4, 14, 5, 8, 2, 7, }, -- 4 { 3, 8, 4, 5, 12, 6, 10, 2, 7, }, -- 5 } print ( ReVersion ) print ( "Puzzle: " .. puzzle.GetName () ) print ( "Track: " .. ui.GetTrackName () ) InitializePuzzleState () if GetParam () then -- how to use: -- setting default options if filters BK 4/2/2015 -- MutClass(structure, false) -- MutClass(band, false) -- MutClass(current, true) if DISABLEFILTERS then -- WARNING: TO VERIFY !! (maybe it's irreversible for several functions below) 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 print ( "options:" ) print ( "wiggle factor = " .. WF ) print ( "\"big\" local wiggle = " .. BIGWIGGLE ) print ( "probability of 1st band = " .. PROB_PUTBAND ) print ( "probability 1st band is band in space (BiS) = " .. PROB_BIS_NOT_BETWEEN ) print ( "probability of 2nd band = " .. PROB_2NDBAND ) print ( "probability 2nd band is band in space (BiS) = " .. PROB_2ND_IS_BIS ) print ( "probability non-BiS band uses random non-default atom = " .. PROB_BETWEEN_USES_ATOM ) if PuzzleHasContactMap then print ( "probability of using contact map for non-BiS band = " .. PROB_CHOOSE_CONTACTMAP ) end if HASFILTERS then print ( "disable filters = " .. tostring ( DISABLEFILTERS ) ) end if HASLIGAND then print ( "include ligand(s) = " .. tostring ( INCLUDELIGAND ) ) end print ( "--" ) for ii = 1, 1000 do for jj = 1, #patternz do print ( "start pattern " .. jj .. "/" .. #patternz ) local gain = 0.0 repeat local score = current.GetEnergyScore () BandedWorm ( patternz [ jj ] ) gain = current.GetEnergyScore () - score until gain < 0.01 print ( "end pattern " .. jj .. "/" .. #patternz ) end end end cleanup () end function cleanup ( errmsg ) if CLEANUPENTRY ~= nil then return end CLEANUPENTRY = true print ( "---" ) local reason local start, stop, line, msg if errmsg == nil then reason = "complete" else -- -- civilized error reporting, -- thanks to Bruno K. and Jean-Bob -- start, stop, line, msg = errmsg:find ( ":(%d+):%s()" ) if msg ~= nil then errmsg = errmsg:sub ( msg, #errmsg ) end if errmsg:find ( "Cancelled" ) ~= nil then reason = "cancelled" else reason = "error" end end print ( ReVersion .. " " .. reason ) print ( "Puzzle: " .. puzzle.GetName () ) print ( "Track: " .. ui.GetTrackName () ) if reason == "error" then print ( "Unexpected error detected" ) print ( "Error line: " .. line ) print ( "Error: \"" .. errmsg .. "\"" ) end save.Quickload ( QS_Best ) PrintState () SetCI ( 1.0 ) selection.DeselectAll () ManageBands () end xpcall( main, cleanup )

Comments


LociOiling Lv 1

Version 1.4.8 crashed on ligand puzzles.

The problem was a simple typo: "Firstligand" should have been "FirstLigand".

With the undefined "Firstligand", the function random() returned a fractional value ( 0 <= n <= 1), which was used as segment number. The Foldit functions don't like fractional segment numbers.