Icon representing a recipe

Recipe: Banded Worm v1.1

created by KarenCH

Profile


Name
Banded Worm v1.1
ID
49951
Shared with
Public
Parent
None
Children
None
Created on
September 02, 2014 at 18:27 PM UTC
Updated on
September 02, 2014 at 18:27 PM UTC
Description

Very late endgame script: worm wiggle with bands mixed in

Best for


Code


------------------------------------------------------------------------------ -- Banded Worm ------------------------------------------------------------------------------ -- Modifies Worm LWS v2 by rav3n_pl -- -- by KarenCH ------------------------------------------------------------------------------ -- interesting global variables that users might want to modify PROB_CHOOSE_CONTACTMAP = 0.50 PROB_PUTBAND = 1.0 -- how often do we put bands in our wiggle steps? PROB_BIS_NOT_BETWEEN = 0.50 -- do we prefer BIS or bands between segs PROB_BETWEEN_USES_TIP = 0.50 -- between: should wiggled seg have band from aa tip BAND_STRENGTH_DEFAULT = 1.0 -- 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.96 USENORMALSCORE = true -- exploration puzzles would set false DEBUGRUN = true -- mostly goes with DebugPrint, but could get more use later -- variables users probably don't want to play with QS_Start = 1 QS_Best = 3 -- variables users really shouldn't play with PuzzleHasContactMap = false EndCalled = false InitialScore = 0.0 StartTime = 0 CurrentBestScore = 0.0 InitialClashImportance = behavior.GetClashImportance() segCt = structure.GetCount() ------------------------------------------------------------------------------ -- FUNCTIONS ------------------------------------------------------------------------------ function BandedWorm( pattern ) recentbest.Save( ) SetCI( 1.0 ) SaveBest( ) local ss = getScore() for w = 1, #pattern do len = pattern[ w ] local sw = getScore() local swCurr = sw print( "Starting BandedWorm of len " .. len .. ", score: ".. TrimNum( getScore() ) ) for s=1, segCt - len + 1 do selection.DeselectAll() selection.SelectRange( s, s + len - 1 ) local idx = random( s, s + len - 1 ) if random( ) < PROB_PUTBAND then PutSingleRandomBandToSeg( idx ) structure.LocalWiggleSelected( 2 ) DelBands( ) structure.WiggleAll( 2 ) end structure.LocalWiggleSelected( 2 ) local swNew = getScore( ) local gain = swNew - swCurr if gain > 0 then structure.LocalWiggleSelected( 20 ) recentbest.Restore( ) DelBands( ) swNew = getScore( ) gain = swNew - swCurr if TrimNum( gain ) > 0 then print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ) ) end SaveBest( ) swCurr = swNew end recentbest.Restore( ) DelBands( ) end print( "Pattern gain: ".. getScore( ) - sw ) SaveBest( ) end selection.DeselectAll() print( "Total Worm gain: " .. TrimNum( getScore( ) - ss ) ) end function PutSingleRandomBandToSeg( idx ) changeSucceeded = false local strength = random( 0.5 * BAND_STRENGTH_DEFAULT, 1.5 * BAND_STRENGTH_DEFAULT, true ) local doBIS = random( ) < PROB_BIS_NOT_BETWEEN local doTip1 = random( ) < PROB_BETWEEN_USES_TIP if doBIS then changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength ) else if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and random( ) > PROB_CHOOSE_CONTACTMAP then changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doTip1, false ) else changeSucceeded = PutBandToRandomSeg( idx, strength, doTip1 ) end end return changeSucceeded end ---------------------------------------------------------------- -- ---------------- BOILERPLATE --------------------- -- ---------------------------------------------------------------- ---------------------------------------------------------------- -- BASIC FUNCTIONALITY ---------------------------------------------------------------- function DebugPrint( str ) if DEBUGRUN then print( str ) end end function TrimNum( val ) return val - val % 0.001 end -- Not for "external" use - call getScore. This could change if customers want -- something besides current or recentbest. function internalGetScore( wantRB ) if wantRB == nil then wantRB = false end local s=0.0 if not USENORMALSCORE then if wantRB then s = recentbest.GetEnergyScore( ) else s=current.GetEnergyScore( ) end else if wantRB then s = recentbest.GetScore( ) else s=current.GetScore( ) end end return s end function getScore( ) return internalGetScore( false ) end function getRBScore( ) return internalGetScore( true ) end function SaveBest( ) local score = getScore( ) if score > CurrentBestScore then save.Quicksave( QS_Best ) CurrentBestScore = score end end function SetCI( ci ) behavior.SetClashImportance( SCALE_MAXCI * ci ) end ------------- CONTACTMAP FUNCTIONS ------------ function CheckForContactMap( ) PuzzleHasContactMap = false local saveval = 0.0 for i=1, segCt-1 do for j=i+1, segCt do val = contactmap.GetHeat( i, j ) if saveval ~= 0.0 and val ~= saveval then PuzzleHasContactMap = true ContactMapScore = GetContactScore( ) return -- all we wanted to know was whether scores exist end if saveval == 0.0 then saveval = val end end end return end function SegHasContactData( segIn, heatThreshold ) for i = 1, segCt do if i < segIn - 1 or i > segIn + 1 then if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end end end return false end function InitializeContactMapSegList( heatThreshold ) HasContactMapSegList = {} for i = 1, segCt do if SegHasContactData( i, heatThreshold ) then HasContactMapSegList[ #HasContactMapSegList + 1 ] = i end end end function GetContactScore( ) if not PuzzleHasContactMap then return 0 end local sum = 0.0 local segCt = structure.GetCount( ) for i = 1,segCt-1 do for j = i + 1, segCt do if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end end end return sum end ----------------------- MATHY STUFF ----------------------- function seedRandom() seed=os.time( )/math.abs( getScore( ) ) seed=seed%0.001 seed=1/seed while seed<10000000 do seed=seed*1000 end seed=seed-seed%1 DebugPrint( "Seed is: "..seed ) math.randomseed( seed ) -- throw away a couple of randoms math.random( ) math.random( ) end function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars if forceFloat == nil then forceFloat = false end if n1==nil then return math.random() else if n2==nil then if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0 if n1%1==0 then -- can't test for "forceFloat", so caller must beware return math.random( n1) --integer else return math.random( ) * n1 --float end else if n1%1==0 and n2%1==0 and not forceFloat then return math.random( n1, n2 ) --integer between else return math.random( ) * (n2 - n1) + n1 --float between end end end end function 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 IsUnlockedSeg( seg1 ) return not structure.IsLocked( seg1 ) end function DelBands() band.DeleteAll() end function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, fromTip1, fromTip2 ) if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end if fromTip1 == nil then fromTip1 = false end if fromTip2 == nil then fromTip2 = false end if fromTip1 then tip1 = GetTipAtomOfSeg(seg1) else tip1 = 0 end if fromTip2 then tip2 = GetTipAtomOfSeg(seg2) else tip2 = 0 end local bIdx = band.AddBetweenSegments( seg1, seg2, tip1, tip2 ) if bIdx ~= band.GetCount( ) then DebugPrint( "failed to add band from "..seg1.." to "..seg2) return false end 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 ) if not ( IsUnlockedSeg( seg) ) then return false end local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi ) if bIdx ~= band.GetCount( ) then return false end 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 ) -- trying to do tips is too weird, so we omit the capability 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 return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength ) end function PutBandToRandomSeg( idx, strength, doTip ) needNew = true local failedTries = 0 while ( needNew and failedTries < 20 ) do idx2 = random( segCt ) -- ok if this one isn't movable (we assume idx is movable) if idx2 > idx + 1 or idx2 < idx - 1 then needNew = false break else failedTries = failedTries + 1 end end if not needNew then return BandBetweenSegsWithParameters( idx, idx2, strength, nil, doTip, false ) end return false end function PutSomeContactMapBands( heatThreshold, strength, ctBands, doTip1, doTip2 ) 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 which = random( #hotList ) 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() DelBands() end function PrintState( ) local gain = getScore() - InitialScore if gain < 0.001 then print( " No change" ) else print( " Startscore: "..TrimNum( InitialScore )) print( " Score: "..TrimNum( getScore() ) ) print( " Total gain: "..TrimNum( gain )) end print( " Run time: "..os.time() - StartTime) end function End( errstr ) if EndCalled then return end -- no infinite recursion please!!! EndCalled = true print( "" ) if errstr ~= nil then if string.find( errstr, "Cancelled" ) then print( "User cancel" ) else print( errstr ) end end save.Quickload( QS_Best ) PrintState( ) CleanPuzzleState( ) end function main( ) seedRandom( ) InitialScore = getScore( ) CurrentBestScore = InitialScore StartTime = os.time() save.Quicksave( QS_Start ) save.Quicksave( QS_Best ) InitialClashImportance = behavior.GetClashImportance() SCALE_MAXCI = InitialClashImportance CheckForContactMap() if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end local gain = 0.0 repeat local score = getScore( ) pattern={ 2,5,11,3,13,4,7,1,6 } BandedWorm( pattern ) gain = getScore( ) - score until gain < 3.0 End( ) end xpcall( main, End )

Comments