Created
January 13, 2015 20:25
-
-
Save Rav3nPL/a5836333e0b4f3d3b5e4 to your computer and use it in GitHub Desktop.
Foldit GAB v2.2 filters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
GAB - Genetic Algorithm on Bands | |
by Rav3n_pl | |
based on CartoonVillan and Crashguard303 scripts | |
Lua V2 | |
Definitions: | |
band: randomised: start segment, end segment, length, strength | |
critter: set of bands | |
herd: set of critters | |
1. | |
- randomize needed bands | |
- randomly assigning them to critters | |
2. | |
- score each critter | |
3. | |
- keep critters scoring above score | |
- breed best critters | |
- breed bastards (best one + random one) | |
- forget about rest of critter | |
- randomize new critters to fill herd | |
]]-- | |
-- options: | |
filters=true --if true filters are enabled only for scoring | |
energy = false --set true to seek energy in exploration puzzles; false works on all puzzles | |
pullCI = 0.9 --Clash Importance during pull | |
maxCI = 1 --maximum CI used by script | |
CapCI=false --false=use maxci as multiplier, true=just cap it to maxci | |
fastQstab = true -- only 1s1w after pull if true | |
fuzeThresh = 1 -- run fuze if we are close to best score (negative=new best score) | |
qstabThresh = 1 -- run qstab if score drops more than... wiggle only in other case | |
useLigand = false --use ligand if true | |
onlyMutable = false --if true use ONLY all mutable aas, no matter of always use | |
mutateOnce = false --if true use mutate(1) instead of shake in qstab | |
mutateOnceCI = 0.21 --mutate on what clashing importance | |
mutateAlwas = false --if true use mutate(1) instead of all shakes | |
herd= --herd options | |
{ | |
breedBest = 5, --breed best 4 critters - all combinations => 6 kids form 4 critters, 3 form 3, 1 form 2, 9 form 5 ;] | |
keepBest = 3, --save up to 3 best scoring critters, rest are forgotten | |
breedBastards = 8, --number of best will have one random bastard | |
newRandom = 10, --adding new random ones each generation | |
maxGen= 100, --maximum generations | |
shuffle = true, --set true to run critters in random order | |
renew=4, --create totally fresh herd after that many generations w/o improvement | |
} | |
critter= --critter options | |
{ | |
minBands=2, --minimum bands | |
maxBands=7, --maximum bands | |
keepScore = 0 , --survive to next gen only if score higher than | |
breedScore= -20, --will breed kids only if score higher. Bastards always breed | |
maxLoss=30, --maximum loss by critter. set 0 to disable | |
} | |
bands= --bands options | |
{ | |
minStr=0.3, --minimum band str | |
maxStr=1.1, --maximum band str | |
minChng = 3, -- minimum change of band len according to current distance | |
maxUp = 6.1, -- maximum change up (push) | |
maxDn = 6.9, -- maximum change down (pull) | |
minSkip = 5, --minimum segment distance | |
minDist = 4, --minimum spatial distance | |
minLen = 2, --minimum length of created band | |
} | |
DoNotUse={--just comment lines below or add more areas to avoid | |
--{segCnt,segCnt}, --ligand cant be used | |
--{120,134}, | |
--{1,10}, | |
} | |
AlwaysUse={ --areas should be always used | |
--{segCnt,segCnt},--ligand need to be at one end | |
--{308,311}, --loopy | |
--{272,319}, --loopy | |
} | |
UseSegments= --use ONLY this segments | |
{ | |
--2,3,4,5 | |
} | |
-- bands by secondary structure | |
use= | |
{ | |
Sheet=true, --set false to not band sheets | |
Helix=true, --set false to not band helices | |
Loop=true, --set false to not band loops | |
} | |
checkBoth=false --check both ends to above, if false only one end need to be true | |
--end of options | |
function filtersOn() | |
if filters then | |
behavior.SetSlowFiltersDisabled(false) | |
end | |
end | |
function filtersOff() | |
if filters then | |
behavior.SetSlowFiltersDisabled(true) | |
end | |
end | |
segCnt=structure.GetCount() | |
if useLigand==false then | |
while structure.GetSecondaryStructure(segCnt)=="M" do segCnt=segCnt-1 end | |
end | |
p=print | |
function CI(c) | |
if c>maxCI then | |
if CapCI==true then | |
c=maxCI | |
else | |
c=c*maxCI | |
end | |
end | |
behavior.SetClashImportance(c) | |
end | |
function round(x)--cut all after 3-rd place | |
return x-x%0.001 | |
end | |
function down(x) | |
return x-x%1 | |
end | |
function Score()--return score, exploration too | |
filtersOn() | |
local s=0 | |
if energy then | |
s=current.GetEnergyScore() | |
else | |
s=current.GetScore() | |
end | |
filtersOff() | |
return s | |
end | |
--local seed=recipe.GetRandomSeed() --NOT WORKING on windowz!!! | |
--calculate REALLY good seed from current score | |
seed=os.time() | |
seed=1/seed | |
while seed<10000000 do seed=seed*10 end | |
seed=seed-seed%1 | |
p("Seed is: "..seed) | |
math.randomseed(seed) | |
function Wiggle(how, iters, minppi) | |
if how==nil then how="wa" end | |
if iters==nil then iters=6 end | |
if minppi==nil then minppi=0.1 end | |
if iters>0 then | |
iters=iters-1 | |
sp=Score() | |
if how == "s" then | |
if mutateAlwas==true then structure.MutateSidechainsSelected(1) | |
else structure.ShakeSidechainsSelected(1) end | |
elseif how == "wb" then structure.WiggleAll(2, true,false) | |
elseif how == "ws" then structure.WiggleAll(2, false,true) | |
elseif how == "wa" then structure.WiggleAll(2) | |
end | |
ep = Score() | |
ig=ep-sp | |
if how~="s" then | |
if ig > minppi then return Wiggle(how, iters, minppi) end --tail call | |
end | |
end | |
end | |
function SaveBest() | |
local g=Score()-bestScore | |
if g>0 then | |
if g>0.01 then p("Gained another "..round(g).." pts.") end | |
bestScore=Score() | |
save.Quicksave(3) | |
end | |
end | |
function SaveRB() | |
local rb=0 | |
filtersOn() | |
if energy==true then | |
rb= recentbest.GetEnergyScore() | |
else | |
rb= recentbest.GetScore() | |
end | |
if rb>bestScore then | |
save.Quicksave(4) | |
recentbest.Restore() | |
SaveBest() | |
save.Quickload(4) | |
end | |
filtersOff() | |
end | |
function Qstab() | |
selection.SelectAll() | |
CI(0.1) | |
if mutateOnce==true then | |
CI(mutateOnceCI) | |
structure.MutateSidechainsSelected(1) | |
else | |
Wiggle("s",1) | |
end | |
if fastQstab==false then | |
CI(0.4) | |
Wiggle("wa",1) | |
CI(1) | |
Wiggle("s",1) | |
end | |
CI(1) | |
Wiggle() | |
end | |
function FuzeEnd() | |
CI(1) | |
Wiggle("wa",1) | |
Wiggle("s",1) | |
Wiggle() | |
srb() | |
end | |
function Fuze1(ci1,ci2) | |
CI(ci1) | |
Wiggle("s",1) | |
CI(ci2) | |
Wiggle("wa",1) | |
end | |
function Fuze2(ci1,ci2) | |
CI(ci1) | |
Wiggle("wa",1) | |
CI(1) | |
Wiggle("wa",1) | |
CI(ci2) | |
Wiggle("wa",1) | |
end | |
function srb() | |
recentbest.Restore() | |
SaveBest() | |
end | |
function Fuze() | |
p("Fuzing...") | |
local scr=Score() | |
selection.SelectAll() | |
filtersOn() | |
recentbest.Save() | |
filtersOff() | |
Fuze1(0.3,0.6) FuzeEnd() | |
Fuze2(0.3,1) srb() | |
Fuze1(0.05,1) srb() | |
Fuze2(0.7,0.5) FuzeEnd() | |
Fuze1(0.07,1) | |
srb() | |
end | |
function random(n1,n2) --random function returns int or float depends on input vars | |
if n2==nil and n1==nil then | |
return math.random() --float | |
else | |
if n2==nil then | |
if n1%1==0 then | |
return math.random(n1) --integer | |
else | |
return math.random()*n1 --float | |
end | |
else | |
if n1%1==0 and n2%1==0 then | |
return math.random(n1,n2) --integer between | |
else | |
return math.random()*(n2-n1)+n1 --float between | |
end | |
end | |
end | |
end | |
function FillHerd() --fill up herd | |
local n=#critters | |
if n>0 then --fill up | |
n=herd.newRandom | |
else --fresh herd | |
n=herd.breedBest + herd.keepBest + herd.breedBastards | |
end | |
p("Randomizing "..n.." new critters...") | |
for i=1,n do | |
AddCritter() | |
end | |
end | |
function AddCritter() --create new random critter | |
local c={} | |
critterID=critterID+1 | |
c.no=critterID | |
c.name=c.no..'-rnd' | |
c.bands={} | |
local r=random(critter.minBands, critter.maxBands) | |
for i=1,r do | |
c.bands[#c.bands+1]=AddBand() | |
end | |
critters[#critters+1]=c | |
p(c.name.." bands: "..#c.bands) | |
end | |
function AddBand() --create one random band | |
local cnt=0 | |
local b={} | |
while true do --try till die | |
cnt=cnt+1 | |
local s1=random(segCnt) | |
if onlyMutable==true or #UseSegments>0 then | |
s1=UseSegments[random(#UseSegments)] | |
end | |
local s2=random(segCnt) | |
if s1>s2 then s1,s2=s2,s1 end --swap | |
if #UseSegments>0 or CanBeUsed(s1,s2) then | |
local str=random(bands.minStr,bands.maxStr) | |
local len=0 | |
while true do --randomize correct distance | |
len=random(-bands.maxDn,bands.maxUp) | |
if len<-bands.minChng or len>bands.minChng then break end | |
end | |
b={s1,s2,str,len} | |
break | |
end | |
if cnt>100 then | |
p("Sorry! Cant create band! Breaking script!") | |
BreakScript() --there is no such function, so it crashes script | |
end | |
end | |
return b | |
end | |
function CanBeUsed(sg1,sg2) --checking end of bands | |
function ssCheck(ss) | |
local good=false | |
if use.Sheet and ss=="E" then good=true end | |
if use.Helix and ss=="H" then good=true end | |
if use.Loop and ss=="L" then good=true end | |
return good | |
end | |
function AreGood(s1,s2) --check that s1 and s2 can be used | |
local ok=true | |
if s2-s1<bands.minSkip then ok=false end | |
if ok==true and structure.GetDistance(s1,s2)<bands.minDist then ok=false end | |
return ok | |
end | |
local ok=AreGood(sg1,sg2) | |
if ok==true and #DoNotUse>0 then --none of 2 can be in that area | |
for i=1, #DoNotUse do | |
local r=DoNotUse[i] | |
for x=r[1],r[2] do | |
if x==sg1 or x==sg2 then | |
ok=false | |
break | |
end | |
end | |
if ok==false then break end | |
end | |
end | |
if ok==false then | |
return false --if false can`t be used | |
else | |
ok=false | |
if #AlwaysUse>0 then --at least one have to be there | |
for i=1, #AlwaysUse do | |
local r=AlwaysUse[i] | |
for x=r[1],r[2] do | |
if x==sg1 or x==sg2 then | |
ok=true | |
break | |
end | |
end | |
if ok==true then break end | |
end | |
else | |
ok=true | |
end | |
end | |
if ok==true then --check structure | |
ok=false | |
local ss1=structure.GetSecondaryStructure(sg1) | |
local ss2=structure.GetSecondaryStructure(sg2) | |
if checkBoth then | |
if ssCheck(ss1) and ssCheck(ss2) then ok=true end | |
else | |
if ssCheck(ss1) or ssCheck(ss2) then ok=true end | |
end | |
end | |
return ok | |
end | |
function ScoreHerd() --score all critters from herd | |
save.Quickload(3) | |
p("Scoring "..#critters.." critters...") | |
save.Quicksave(5) | |
local herdScore=Score() | |
for i=1,#critters do | |
band.DeleteAll() | |
local crt=critters[i] --critter | |
local s=Score() --start score | |
local bnds=crt.bands | |
for b=1,#bnds do | |
local bnd=bnds[b] | |
local a1=5 | |
local a2=5 | |
if bnd[1]== structure.GetCount() then a1=6 end | |
if bnd[2]== structure.GetCount() then a1=6 end | |
if structure.GetAminoAcid(bnd[1])=='g' then a1=0 end | |
if structure.GetAminoAcid(bnd[2])=='g' then a2=0 end | |
band.AddBetweenSegments(bnd[1],bnd[2],a1,a2) | |
local bc=band.GetCount() | |
band.SetStrength(bc,bnd[3]) | |
local len=structure.GetDistance(bnd[1],bnd[2])+bnd[4] | |
if len<bands.minLen then len=bands.minLen end | |
band.SetGoalLength(bc,len) | |
end | |
selection.SelectAll() | |
CI(pullCI) | |
recentbest.Save() | |
Wiggle("wb",1) | |
band.DeleteAll() | |
CI(1) | |
if math.abs(s-Score()) > qstabThresh then | |
Qstab() | |
else | |
Wiggle() | |
end | |
if Score()-bestScore>fuzeThresh then | |
SaveRB() | |
Fuze() | |
else | |
SaveRB() | |
end | |
crt.score=Score()-s | |
p("Critter "..crt.name.." : "..round(crt.score)) | |
if critter.maxLoss>0 then | |
if Score()>herdScore-critter.maxLoss then | |
save.Quicksave(5) | |
herdScore=Score() | |
else | |
save.Quickload(5) | |
end | |
else | |
save.Quickload(3) | |
end | |
end | |
save.Quickload(3) | |
if band.GetCount()>0 then --clean bands from best solution (if any) | |
band.DeleteAll() | |
save.Quicksave(3) | |
end | |
end | |
function BreedCritters(mom,dad,t) --breed 2 critters. bands are taken randomly | |
local kid={} | |
critterID=critterID+1 | |
kid.no=critterID | |
kid.name=kid.no.."-"..t..mom.no..'/'..dad.no | |
kid.bands={} | |
local mb=#mom.bands | |
local db=#dad.bands | |
if mb>db then mb,db=db,mb end --kid have bands count between mom and dad bands | |
local bn=random(mb,db) | |
for i=1,bn,2 do | |
kid.bands[#kid.bands+1]=mom.bands[random(#mom.bands)] | |
kid.bands[#kid.bands+1]=dad.bands[random(#dad.bands)] | |
end | |
p(kid.name.." bands: "..#kid.bands) | |
return kid | |
end | |
function KeepGood() --copy best scoring critters form last gen if score above | |
local newHerd={} | |
for i=1,herd.keepBest do | |
if critters[i].score>critter.keepScore and (math.abs(critters[i].score)>0.1 or critters[i].score>0) then | |
newHerd[#newHerd+1]=critters[i] | |
end | |
end | |
return newHerd | |
end | |
function SortCritters() --bubble sort | |
for i=1,#critters do | |
for j=i+1,#critters do | |
if critters[i].score<critters[j].score then | |
critters[i],critters[j]=critters[j],critters[i] --love lua :) | |
end | |
end | |
end | |
end | |
function BreedHerd() | |
p("Breeding...") | |
SortCritters() | |
newHerd=KeepGood() | |
for i=1, herd.breedBest do | |
local mom=critters[i] | |
if mom.score>critter.breedScore or i<2 then --breed only good ones, 1st is always breed anyway | |
for j=i+1, herd.breedBest do | |
local dad=critters[j] | |
newHerd[#newHerd+1]=BreedCritters(mom,dad,"kid-") | |
newHerd[#newHerd+1]=BreedCritters(dad,mom,"kid-") | |
end | |
end | |
end | |
for i=1, herd.breedBastards do --they will always appear ;] | |
local mom=critters[i] | |
local j=random(herd.breedBastards+1,#critters) | |
local dad=critters[j] | |
newHerd[#newHerd+1]=BreedCritters(mom,dad,"bas-") | |
newHerd[#newHerd+1]=BreedCritters(dad,mom,"bas-") | |
end | |
critters=newHerd | |
FillHerd() | |
end | |
function ShuffleHerd() | |
if herd.shuffle==true then | |
for i=1,#critters do | |
local r=random(#critters) | |
if r~=i then | |
critters[i],critters[r]=critters[r],critters[i] | |
end | |
end | |
end | |
end | |
function GAB() | |
if onlyMutable==true then | |
for i=1,segCnt do | |
if structure.IsMutable(i) then | |
UseSegments[#UseSegments+1]=i | |
end | |
end | |
end | |
if #AlwaysUse>0 then | |
for i=1,#AlwaysUse do | |
local ss=AlwaysUse[i][1] | |
local se=AlwaysUse[i][2] | |
if ss>se then ss,se=se,ss end | |
for j=ss,se do | |
UseSegments[#UseSegments+1]=j | |
end | |
end | |
AlwaysUse={} --added to list, no need to check later | |
end | |
bestScore=Score() | |
critterID=0 | |
gen=0 | |
ss=Score() | |
save.Quicksave(3) | |
recentbest.Save() | |
p("Starting Rav3n_pl GAB Start score: "..round(ss)) | |
filtersOff() | |
critters={} | |
FillHerd() | |
badGen=0 | |
while true do --this is (almost) endless script ;] | |
genScore=Score() | |
gen=gen+1 | |
p() | |
p("Generation: "..gen..", score: "..round(Score())..", gain: "..round(Score()-ss)) | |
ShuffleHerd() | |
ScoreHerd() | |
save.Quickload(3) | |
if gen==herd.maxGen then break end --end of script | |
if genScore<=Score() then badGen=badGen+1 else badGen=0 end | |
if badGen>=herd.renew then | |
p("Creating fresh random herd...") | |
critters={} | |
FillHerd() | |
badGen=0 | |
else | |
BreedHerd() | |
end | |
end | |
filtersOn() | |
p("Final score: "..round(Score()).." Total gain: "..round(Score()-ss)) | |
end | |
-- main call | |
GAB() | |
--end of script |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment