Profile
- Name
- Glycine Hinge v2.4.1
- ID
- 100513
- Shared with
- Public
- Parent
- Glycine Hinge v2.4
- Children
- Created on
- January 04, 2015 at 22:17 PM UTC
- Updated on
- January 04, 2015 at 22:17 PM UTC
- Description
Glycine Hinge with better choices -- variable width hinges and band-strength
Best for
Code
--[[------------------------------------------------------------------------------------------------
Glycine hinge v1.3.0.0 -- Because Glycine is the most flexible Amino acid, it will
respond readily and rather predictably to manipulation. The "glycine hinge" is a
tactic used to shape a section of a Loop containing at least one glycine. It is most
useful when a player has some idea of where other secondary structures should
go, and wants to bend the loop in a way that will put them in the appropriate
places while not dragging down his local score too much.[1] The recipe
attempts to freeze backbone, and necessary Secondary Structures (SS),
("The rods"), around the surrounding Glycine Amino Acid (AA),
("The hinge"), and proceeds to wiggle with bands.
Copyright (C) 2011 tlaloc <http://fold.it/portal/user/57765>
Copyright (C) 2011 rav3n_pl <http://fold.it/portal/user/174969>
Copyright (C) 2011 Seagat2011 <http://fold.it/port/user/1992490>
Copyright (C) 2011 thom001 <http://fold.it/port/user/172510>
Heavily rewritten by KarenCH (August 2014)
-------------------------------------- REFERENCES --------------------------------------
1. - "Glycine Hinge" - http://foldit.wikia.com/wiki/Glycine_Hinge
------------------------------------------------------------------------------------------------]]--
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = true -- mostly goes with DebugPrint, but could get more use later
GH_BAND_DEFAULT_STRENGTH = 0.25
QS_Start = 1
QS_Best = 3
EndCalled = false
segCt = structure.GetCount()
CurrentBestScore = 0.0
InitialScore = 0.0
InitialTime = 0
local UserOptions = {
hingeWidth = 1, -- number of unfrozen segments to do hinging around
glycineIdxList = {},
hingeStartIndices = {},
hingeEndIndices = {}
}
local function FindAllGlycines( options )
for idx = 1, segCt do
if structure.GetAminoAcid( idx ) == 'g' then
options.glycineIdxList[ #options.glycineIdxList + 1 ] = idx
end
end
end
local function GetEndOfRod( startIdx, doForward )
local tmp = structure.GetSecondaryStructure(startIdx )
endIdx = startIdx
if endIdx <= 1 or startIdx >= segCt then return startIdx end
while ( 1 ) do
if tmp == structure.GetSecondaryStructure( endIdx ) then
if doForward then
if endIdx < segCt then
endIdx = endIdx + 1
else
endIdx = segCt
break
end
else
if endIdx > 1 then
endIdx = endIdx - 1
else
endIdx = 1
break
end
end
else
if doForward then
endIdx = endIdx - 1
else
endIdx = endIdx + 1
end
break
end
end -- while ( 1 )
return endIdx
end
function PerformHinge( startHinge, endHinge )
if startHinge > endHinge then startHinge, endHinge = endHinge, startHinge end
DebugPrint( "Hinge is from "..startHinge.." to "..endHinge )
if startHinge <= 2 or endHinge >= segCt-1 then
print( "Cannot build hinge - too close to end" )
return false
end
-- find bounds of rods
startLeftRod = GetEndOfRod( startHinge - 1, false )
endRightRod = GetEndOfRod( endHinge + 1, true )
DebugPrint( "LeftRod: "..startLeftRod.." to ".. startHinge-1 ..
" RightRod: ".. (endHinge+1) .." to "..endRightRod )
SaveBest( )
-- freeze the "rods"
selection.DeselectAll ()
selection.SelectRange( endHinge + 1, endRightRod )
selection.SelectRange( startLeftRod, startHinge - 1 )
freeze.FreezeSelected( true, false )
selection.DeselectAll ()
-- put bands between the rods
for i = 1,4 do
local n1 = startHinge - i
local n2 = endHinge + i
if n1 >= 1 and n2 <= segCt then
local bIdx = band.AddBetweenSegments( n1, n2 )
band.SetStrength( bIdx, GH_BAND_DEFAULT_STRENGTH )
end
end
-- perform glycine hinge's version of a banded wiggle
recentbest.Save( )
ExactSetCI( 0.30 )
structure.WiggleAll( 1 )
DelBands( )
ExactSetCI( 1.00 )
structure.WiggleAll( 30 )
recentbest.Restore( )
DelBands( ) -- because recentbest might restore them
freeze.UnfreezeAll( )
SaveBest( )
end
function PerformHingeSequence( options )
for i=1, #options.hingeStartIndices do
local gain = 0.0
local currScore = getScore( )
repeat
LoadBest( )
PerformHinge( options.hingeStartIndices[ i ], options.hingeEndIndices[ i ] )
PrintState( )
local newScore = getScore( )
gain = newScore - currScore
currScore = newScore
until TrimNum( gain ) == 0.0
end -- FOR (i)
end
local function GetUserParams( options )
print( "Give comma separated list" )
print( "Indicate ranges to hinge around by '#-#' " )
local glystr = ""
for i=1, #options.glycineIdxList do
if i > 1 then glystr = glystr .. ", " end
glystr = glystr .. options.glycineIdxList[ i ]
end
dlg = dialog.CreateDialog( "------------- Glycine Hinge -------------" )
dlg.ghcmt1 = dialog.AddLabel( "Glycines: "..glystr )
dlg.ghcmt2 = dialog.AddLabel( "Comma sep list; ranges use -" )
dlg.idxset = dialog.AddTextbox( "Hinges", tostring( options.glycineIdxList[ 1 ] ) )
dlg.bandstrength = dialog.AddSlider( "Band strength", GH_BAND_DEFAULT_STRENGTH, 0.01, 1.50, 2 )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) > 0 then
GH_BAND_DEFAULT_STRENGTH = dlg.bandstrength.value
-- fancy parsing here: Input format is: idx1,idx2-idx3,idx4,idx5,idx6-idx7
options.hingeStartIndices = {}
options.hingeEndIndices = {}
for w in string.gmatch( dlg.idxset.value, "[^,]+" ) do
if string.find( w, "-" ) ~= nil then
local f, s = string.match( w, "(%d+)%-(%d+)" )
options.hingeStartIndices[ #options.hingeStartIndices + 1] = tonumber( f )
options.hingeEndIndices[ #options.hingeEndIndices + 1] = tonumber( s )
else
options.hingeStartIndices[ #options.hingeStartIndices + 1] = tonumber( w )
options.hingeEndIndices[ #options.hingeEndIndices + 1] = tonumber( w )
end
end
return true
end
return false
end
----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end
function BoolStr( bval )
if bval then return "true" end
return "false"
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 LoadBest( )
save.Quickload( QS_Best )
end
function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end
function ExactSetCI( ci )
behavior.SetClashImportance( ci )
end
function DelBands()
band.DeleteAll()
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
--------------------------------------------------------------------------
-- SETUP and CLEANUP
--------------------------------------------------------------------------
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
ExactSetCI( InitialClashImportance )
LoadBest( )
PrintState( )
print("")
end
function InitializePuzzleState( )
seedRandom()
InitialScore = getScore( )
CurrentBestScore = InitialScore
StartTime = os.time()
save.Quicksave( QS_Start )
save.Quicksave( QS_Best )
InitialClashImportance = behavior.GetClashImportance()
end
----------------------------------------------------------------
-- MAIN
----------------------------------------------------------------
function main()
InitializePuzzleState( )
local options = UserOptions
FindAllGlycines( options )
print ( "Glycine hinge - recipe" )
print( "Initial state stored in slot "..QS_Start.."; best in slot "..QS_Best )
print( "Initial score: " .. InitialScore )
if #options.glycineIdxList == 1 then
print( "There is "..#options.glycineIdxList.." glycine" )
elseif #options.glycineIdxList == 0 then
print( "There are no glycines" )
else
print( "There are "..#options.glycineIdxList.." glycines" )
end
while GetUserParams( options ) do
PerformHingeSequence( options )
end -- WHILE GetUserParams
End( )
end
xpcall( main, End )