Code
--[[
Bands! Bands in space!
GAB - Genetic Algorythm on Bands
by Rav3n_pl
based on CartoonVillan and Crashguard303 scripts
Lua V2
Definitions:
band: random band to space from random segment
critter: set of bands
herd: set of critters
1.
- randomize needed bands
- randomly assignig them to criters
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:
energy = false -- set true to seek energy in exploration puzzles; false works on all puzzles
pullCI = 0.9 -- Clash Impotrance during pull
shakeCI = 0.21 -- CI when shake on qstab or mutate once
maxCI = 1 -- maximum CI used by script
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
onlyMutable = true -- if true use ONLY all mutable aas, no matter of always use
mutateOnce = true -- if true use mutate(1) instead of shake in qstab
mutateAlwas = false -- if true use muatate(1) instead of all shakes
herd = {
-- herd options
breedBest = 5, -- breed best critters, all combinations => 6 kids form 4 critters, 3 form 3, 1 form 2, 9 form 5 etc
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 gens w/o improvement
}
critter = {
-- critter options
minBands = 3, -- 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. Basttards 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
maxUp = 12.1 -- maximum band length
}
DoNotUse = {}
AlwaysUse = {}
UseSegments = {} -- use ONLY this segments
-- 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
}
-- end of options
-- constants
segCnt = structure.GetCount()
p = print
function CI(c)
if c > maxCI then
c = maxCI
end
behavior.SetClashImportance(c)
end
function round(x) -- cut all afer 3-rd place
return x - x % 0.001
end
function down(x)
return x - x % 1
end
function Score() -- return score, exploration too
if energy == true then
return current.GetEnergyScore()
else
return current.GetScore()
end
end
-- local seed=recipe.GetRandomSeed() --NOT WORKING!!!
-- calculate REALLY good seed
seed = os.time() / math.abs(Score())
seed = seed % 0.001
seed = 1 / seed
while seed < 10000000 do
seed = seed * 1000
end
seed = seed - seed % 1
p('Seed is: ' .. seed)
math.randomseed(seed)
-- REALLY good seed made by rav3n_pl :P
function Wiggle(how, iters, minppi)
if how == nil then
how = 'wa'
end
if iters == nil then
iters = 3
end
if minppi == nil then
minppi = 0.3
end
if iters > 0 then
iters = iters - 1
local 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, true, true)
end
local ep = Score()
local 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 s
if energy then
s = recentbest.GetEnergyScore()
else
s = recentbest.GetScore()
end
if (s > bestScore) then
save.Quicksave(4)
recentbest.Restore()
SaveBest()
save.Quickload(4)
end
end
function Qstab()
selection.SelectAll()
CI(shakeCI)
if mutateOnce == true then
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...')
selection.SelectAll()
recentbest.Save()
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 n1 == nil then
return math.random()
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 _ = 1, n do
AddCritter()
end
end
function AddCritter() -- vreate 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 _ = 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
if #UseSegments > 0 or CanBeUsed(s1) then
local str = random(bands.minStr, bands.maxStr)
local len = random(1, bands.maxUp)
local theta = math.acos(math.random())
local phi = 2 * math.pi * math.random()
local segmentXAxis
local segmentYAxis
while true do -- all 3 must be different
segmentXAxis = random(segCnt)
segmentYAxis = random(segCnt)
if segmentXAxis ~= s1 and segmentYAxis ~= s1 and segmentXAxis ~= segmentYAxis then
break
end
end
-- {segmentOrigin, segmentXAxis, segmentYAxis, rho, theta, phi}
b = {s1, segmentXAxis, segmentYAxis, len, theta, phi, str}
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 ssCheck(ss)
if (use.Sheet and ss == 'E') or (use.Helix and ss == 'H') or (use.Loop and ss == 'L') then
return true
else
return false
end
end
function CanBeUsed(sg1) -- checking segment at end of band
local ok = true
if #DoNotUse > 0 then -- can not hook in that area
for i = 1, #DoNotUse do
local r = DoNotUse[i]
for x = r[1], r[2] do
if x == sg1 then
ok = false
break
end
end
if (not ok) then
return false
end
end
end
if #AlwaysUse > 0 then -- end have to be there
ok = false
for i = 1, #AlwaysUse do
local r = AlwaysUse[i]
for x = r[1], r[2] do
if x == sg1 then
ok = true
break
end
end
if ok then
break
end
end
end
if ok then -- check structure
ok = ssCheck(structure.GetSecondaryStructure(sg1))
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 atom = 5
local sn = bnd[1]
if sn == segCnt then
atom = 6
end -- bug in last segment
if structure.GetAminoAcid(sn) == 'g' then
atom = 0
end -- glycyne
band.Add(sn, bnd[2], bnd[3], bnd[4], bnd[5], bnd[6], atom)
local bc = band.GetCount()
band.SetStrength(bc, bnd[7])
end
selection.SelectAll()
CI(pullCI)
recentbest.Save()
Wiggle('wb', 1)
band.DeleteAll()
CI(1)
if s - Score() > qstabThresh then
Qstab()
else
Wiggle()
end
SaveRB()
if Score() - bestScore > fuzeThresh then
Fuze()
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 _ = 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 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()
local 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
--define global variables
critterID = 0
critters = {}
gen = 0
bestScore = Score()
function GAB()
if onlyMutable == true then
for i = 1, segCnt do
if structure.IsMutable(i) then
UseSegments[#UseSegments + 1] = i
end
end
end
bestScore = Score()
critterID = 0
gen = 0
local ss = Score()
save.Quicksave(3)
recentbest.Save()
p('Rav3n_pl Bands! Bands in space! GAB BiS')
p('Start score: ' .. round(ss))
critters = {}
FillHerd()
local badGen = 0
while true do -- this is (almost) endless script ;]
local 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
p('Final score: ' .. round(Score()) .. ' Total gain: ' .. round(Score() - ss))
end
function dialogOptions()
local mut = false
for i = 1, segCnt do
if structure.IsMutable(i) then
mut = true
break
end
end
if not mut then
onlyMutable = false
mutateOnce = false
end
opt = dialog.CreateDialog('Main Options')
opt.lbl1 = dialog.AddLabel('GAB BiS main options:')
opt.gen = dialog.AddTextbox('Generations:', herd.maxGen)
opt.pull = dialog.AddSlider('Pulling CI', pullCI, 0.05, 1, 2) -- desctip, default, min, max, precision
opt.shak = dialog.AddSlider('Shake CI', shakeCI, 0.01, 1, 2)
opt.maxci = dialog.AddSlider('Maximum CI', maxCI, 0.05, 1, 2)
opt.qstab = dialog.AddCheckbox('FastQstab', fastQstab)
opt.energy = dialog.AddCheckbox('Seek energy', energy)
opt.mutonly = dialog.AddCheckbox('Only mutable', onlyMutable)
opt.mutonce = dialog.AddCheckbox('Mutate once', mutateOnce)
opt.mutalways = dialog.AddCheckbox('Mutate alwyas', mutateAlwas)
opt.fuzeth = dialog.AddTextbox('Fuze threshold: ', fuzeThresh)
opt.qsth = dialog.AddTextbox('Qstab threshold: ', qstabThresh)
opt.run = dialog.AddButton('Start', 1)
opt.more = dialog.AddButton('More', 2)
opt.cancel = dialog.AddButton('Cancel (???)', 0)
res = dialog.Show(opt)
if res > 0 then
energy = opt.energy.value
pullCI = opt.pull.value
shakeCI = opt.shak.value
maxCI = opt.maxci.value
fastQstab = opt.qstab.value
fuzeThresh = tonumber(opt.fuzeth.value)
qstabThresh = tonumber(opt.qsth.value)
onlyMutable = opt.mutonly.value
mutateOnce = opt.mutonce.value
mutateAlwas = opt.mutalways.value
herd.maxgen = tonumber(opt.gen.value)
if res == 2 then
opt = dialog.CreateDialog('More Options')
opt.lbl1 = dialog.AddLabel('Herd:')
opt.brbest = dialog.AddSlider('Breed best', herd.breedBest, 1, 20, 0)
opt.kbest = dialog.AddSlider('Keep best', herd.keepBest, 1, 20, 0)
opt.bas = dialog.AddSlider('Breed bastards', herd.breedBastards, 1, 20, 0)
opt.rnd = dialog.AddSlider('New random', herd.newRandom, 1, 30, 0)
opt.renew = dialog.AddSlider('Renew herd', herd.renew, 1, 10, 0)
opt.shuff = dialog.AddCheckbox('Shuffle critters', herd.shuffle)
opt.lbl2 = dialog.AddLabel('Critter:')
opt.minb = dialog.AddTextbox('Minimum bands:', critter.minBands)
opt.maxb = dialog.AddTextbox('Maximum bands:', critter.maxBands)
opt.keeps = dialog.AddTextbox('Keep score:', critter.keepScore)
opt.breeds = dialog.AddTextbox('Breed score:', critter.breedScore)
opt.maxloss = dialog.AddTextbox('Max loss:', critter.maxLoss)
opt.lbl3 = dialog.AddLabel('Bands:')
opt.minst = dialog.AddSlider('Minimum str', bands.minStr, 0.1, 2, 1)
opt.maxst = dialog.AddSlider('Maximum str', bands.maxStr, 0.1, 2, 1)
opt.maxup = dialog.AddSlider('Max length', bands.maxUp, 3, 20, 0)
opt.run = dialog.AddButton('Start', 1)
dialog.Show(opt)
herd.breedBest = opt.brbest.value
herd.keepBest = opt.kbest.value
herd.breedBastards = opt.bas.value
herd.newRandom = opt.rnd.value
herd.shuffle = opt.shuff.value
herd.renew = opt.renew.value
critter.minBands = tonumber(opt.minb.value)
critter.maxBands = tonumber(opt.maxb.value)
critter.keepScore = tonumber(opt.keeps.value)
critter.breedScore = tonumber(opt.breeds.value)
critter.maxLoss = tonumber(opt.maxloss.value)
bands.minStr = opt.minst.value
bands.maxStr = opt.maxst.value
bands.maxUp = opt.maxup.value
end
else
p('Exiting...')
BreakScript()
end
end
-- main call
dialogOptions()
GAB()
-- end of script