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 )