Icon representing a recipe

Recipe: CG303 GA+EO Remix V7.870

created by Crashguard303

Profile


Name
CG303 GA+EO Remix V7.870
ID
45953
Shared with
Public
Parent
None
Children
None
Created on
April 23, 2013 at 22:19 PM UTC
Updated on
April 23, 2013 at 22:19 PM UTC
Description

A complete rework of my old script,
object-oriented parameter-/variable-structure allowing more features.
Manual is included.
See script code for details.
Note:
1.: This script detects "important"/"cardinal" segments, if there is a change in secondary structure,
but you can add more segments by selection interface (blue).
2.: As we can't get locked (grey) segments by script a.t.m., you will have to exclude them via script parameters.

Best for


Code


--[[ 0. general: GAB+EO Remix V7.870 by Crashguard303 Copyright (C) 2013 Crashguard303 <http://fold.it/portal/user/119022> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. 1. introduction: 1.1 inspired by: Cartoon Villain (GA idea & first random band script of this kind) Rav3n_pl (OO parameter/variable handling) jeff101 (EO & input-boxes idea) 1.2 idea: -- This is a further development of my last GAB+EO which i wrote from scratch to improve table handling and modularity (it was a work of more than one month!) -- this script doesn't simply band to random segs, it works with a list of "cardinal" segs. -- origin segs always stay the same (they are just turned off or on), -- target segs change -> this will avoid double banding -- new idea is to keep the number of random band parameters as small as they can be so it works with 2 fixed band strengths (as the length is already random) -- as i didn't want to overload the starting sequence with too many input boxes and selectable vals (more than 7 boxes at the moment!). -> you can set all vals (even the other) in the declaration section. 1.3 features: -- cardinal seg auto-detection: (see 3.3.2.3) -- secondary-structure changes, amino-acids, etc will create a uselist ++ selecting segs via game interface (blue) is possible, too -- 3 breeding modes: (see 3.2) GA EO1 EO2 -- 2 gene modes: (see 3.2.2.2) "dual" "poly" -- 6 banding modes (see 3.3) "space" "center" "user" "furthest" "random1" "random2" ++ further settings for each 1.4 known bugs/problems: 1.4.1 ligand puzzles: -- are supported by script, but i couldn't test if it works properly! -> to band ligand: use band to "user", then slide to last segment! 1.4.2 locked segs (grey in game): -- can't be detected at the moment :( -> work with a proper seg_use list. you can: -> pass seg indices as table in declaration section or -> make a selection via game's selection interface (blue) (although this wouldn't remove locked ones) 2. abbreviations/terms you might find: 2.1 structure: gen: gen clu: cluster seg: segment dst: distance len: length str: strength itr: iterations 2.2 addressing: list: table idx: index (numerical) key: key (alphanumerical) val: value 2.3 vals: prm: parameter (argument) cur: current (measured) max: maximum min: minimum cng: change (to add) res: result (calculated) rnd: random (genrated) 2.4 other: gui: frontend (dialog input box) 3. features: 3.1 terms and herd generating: This script (as GAB before) applies and tests combinations of random bands. one band combination set is named cluster (clu) here (GA-term: individuum). a testing row of different clus is a generation (gen) (by default, 1 generation consists of 8 clus = herd.size) when one generation has tested, it is evaluated. each clu is measured by it's score gain, gen will be sorted by clu's score gain, best on top. 3.2 analyzing and breeding: selection of of partners and breeding them (mutating, too) is the heart of a genetic algorithm !!! 3.2.1 analyzing: after testing each gen, clus are sorted by the score gain they made -> first clu is this with best gain, -> last clu is this with worst gain, 3.2.2 breeding: 3.2.2.1 selection via EO: EO mode decides what happens with mediocre clus: -- EO_mode.cur=0: mediocre clus are breeded (only this is GA) -- EO_mode.cur=1: mediocre clus are considered as best (soft EO) -- EO_mode.cur=2: mediocre clus are considered as worst (hard/real EO) best clus (always): -- are kept (default: cluster 1 and 2 = 1...herd.breed.first) and "breeded" = mixed to new ones. mediocre clus (EO_mode.cur=0): -- are overwritten by breeded ones (default: clu 3...5 = herd.breed.first to herd.breed.last) worst clus (always): -- are replaced by new random ones (default: clu 6...8 = herd.breed.last...herd.size) 3.2.2.2 sex (breed mode): -- imagine the clus as snails with both genders -- "poly": each single gene can be from any clu by roulette! -- "dual": 1 kid has only 2 parents (default): -> dad is 1st breeding partner -> mom is 2nd breeding partner -- breeding partners can be chosen by: -> their rank: 1st has best score gain -> roulette: 1st appears most time, but all possible -> if you breed 3 kids, roulette combinations are: -- by default: herd.breed.roulette.dad==false herd.breed.roulette.mom==true -> kid#1: dad=clu#1 & mom#1=roulette -> kid#2: dad=clu#2 & mom#2=roulette -> kid#3: dad=clu#3 & mom#3=roulette -- other setting: herd.breed.roulette.dad==false herd.breed.roulette.mom==false -> kid#1: dad=clu#1 & mom#1=dad#1+1=clu#2 -> kid#2: dad=clu#2 & mom#2=dad#2+1=clu#3 -> kid#3: dad=clu#3 & mom#3=dad#3+1=clu#4 -- other setting: herd.breed.roulette.dad==true herd.breed.roulette.mom==false -> kid#1: dad#1=roulette & mom#1=dad#1+1 -> kid#2: dad#2=roulette & mom#2=dad#2+1 -> kid#3: dad#3=roulette & mom#3=dad#3+1 -- other setting: herd.breed.roulette.dad==true herd.breed.roulette.mom==true -> kid#1: dad#1=roulette & mom#1=roulette -> kid#2: dad#2=roulette & mom#2=roulette -> kid#3: dad#3=roulette & mom#3=roulette 3.3 applying bands to segments: 3.3.1 types of connections: The clus which are tested will be not only from seg to seg anymore (like in old GAB) but also from seg to free space (if using clu.band_to.val="space"), allowing more freedom of direction. 3.3.2 origin segs and target segs: 3.3.2.1 origin segs: -- are always the same -- i did this to prevent double banding, simplifiy breeding and keeping the clu's gene-length homogenous -> you can't crossbreed a horse with a zebra because their DNAs have different lengths -> in real life, dna also doesn't change length or genes get other tasks, genes remain and are just active or inactive !! -- segs which can be banded and their banding order are stored in a uselist (selection) -> but you can turn some off and change their activity by mutation -> you can set them in the seg_use table declaration 3.3.2.2 target segs: -- can be different, depending on mode clu.band_to.val: -- origin segs can be banded to -> "space": no seg, but to "air" (more freedom of movement, but no reference to other segs anymore) -> "center": calculated seg (real center seg) -> "user": selected seg (pseudo "center" seg internally) -> "furthest": calculated seg furthest to origin seg -> "random1": another seg in seg_use list (given in table) -> "random2": rnd seg in puzzle (all available) 3.3.2.3 if no list is given (in declaration section): -- if you -- neither change the seg_use list to your needs or -- nor select any seg by game interface: -> by default, only these segs are banded, where changes in secondary structures do occur and which do have special residues (default: Glycine) -> key idea of this new script !!! 3.3.3 band strength: -- is constant for all bands now (unlike to old GAB), as the length is already set by random. -> but you can set the global band strength for primary and secondary bands (not always used) --]] function random_seed_set_val(random_seed) local random_seed= random_seed if (random_seed==nil) or (random_seed=="") then random_seed=os.time()+recipe.GetRandomSeed() else random_seed= tonumber(random_seed) end -- if random_seed print('Random Seed: '..random_seed) math.randomseed(random_seed) -- initialize random seed return random_seed end -- function randomseed function above0(x) if x>0 then return 1 else -- if x<=0 return -1 end -- if x end -- function above0 function crop_digits(x,n) local y=x y=y*10^n y=math.floor(y) y=y*10^(-n) return y end -- function crop_digits function range_wrap(x,min,max,offset) --[[ prevent x getting higher than max or lower than min if x gets out of range, it is set to the other boundary+it's exceeding amount if x>max, val will be >min, if offset is 0 and float vals are used or =min, if offset is 1 and integer vals are used if x<min, val will be <max, if offset is 0 and float vals are used or =min, if offset is 1 and integer vals are used example: range_wrap(1.1,0,1,0) will return 0.1 (x is by .1 >1, so it is set next to 0+.1) range_wrap(-.1,0,1,0) will return 0.9 (x is by .1 <0, so it is set next to 1-.1) range_wrap(11,0,10,0) will return 1 (x is by 1 >10, so it is set to 0+1) range_wrap(-1,0,10,0) will return 9 (x is by 1 <10, so it is set to 10-1) range_wrap(11,0,10,1) will return 0 (x is by 1 >10, so it is set to 0+1-1) range_wrap(-1,0,10,1) will return 10 (x is by 1 < 0, so it is set to 10-1+1) --]] local y=x+0 if y>max then y=min+(y-max)-offset elseif y<min then y=max+(y-min)+offset end -- if y return y end -- function range_wrap function range_clamp(x,min,max) --[[ prevent x getting higher than max by setting it to max or lower than min by setting it to min --]] local y=x+0 if y>max then y=max elseif y<min then y=min end -- if y return y end -- range_clamp() function random_flag1(prob) --[[ return true or false randomly, depending on prob(ability) probability should be a float val between 0 and 1 as vals <0 have the same effect as =0 and vals >1 have the same effect as =1 returns true, if prob=0: never prob=0.1: sometimes prob=0.5: quite as often as false prob=0.9: most times prob=1: always calculation example: -- if you want it to respond true -- in 1 of 10 cases -- use 1/10= 0.1 --]] return (math.random()<prob) end -- function random_flag1 function random_flag2(prob,invert,bool) -- return val like random_flag1(), but -- invert=true inverts val -- bool=false converts boolean vals -- true -> 1 -- false -> 0 local val=random_flag1(prob) if invert then val= not(val) end -- if invert if bool==false then if val then val= 1 else val= 0 end -- if val end -- if bool return val end -- function random_flag2 function list_show_deep(list,text) -- full key and val as string - advanced -- indexing: pairs if text==nil then text=tostring(list) end -- if text local k,v for k,v in pairs(list) do local ktext if type(k)=="number" then ktext= text..'['..tostring(k)..']' else ktext= text..'.'..tostring(k) end -- if type(k) if type(v)=="table" then list_show_deep(v,ktext) else local vtext if (type(v)=="number") or (type(v)=="boolean") then vtext= tostring(v) else vtext= '\"'..tostring(v)..'\"' end -- if type(v) print(ktext..'='..vtext) end -- if type(v) end -- for k end -- function list_show_deep function puzzle_ismutable() local ismutable= false for i=1,puzzle.seg_count do if structure.IsMutable(i) then ismutable= true break -- for i end -- if structure.IsMutable end -- for i return ismutable end -- function puzzle_ismutable function puzzle_isligand() local isligand if structure.GetSecondaryStructure(puzzle.seg_count)==not(ss=="L" or ss=="H" or ss=="E" ) then isligand= true else isligand= false end -- if structure.GetSecondaryStructure print('Ligand detected: '..tostring(isligand)) return isligand end -- function puzzle_isligand function seg_blocks(blocks) -- create a list of mutlitple segment sections -- example: -- blocks={{1;3};{7;10}} -- results list={1;2;3;7;8;9;10} local list={} local i for i=1,#blocks do local j for j=blocks[i][1],blocks[i][2] do print('Appending Idx:'..(#list+1)..'=Seg:'..j) table.insert(list,j) end -- for j end -- for i return list end -- function seg_blocks function seg_use_list_create(seg_use) -- table seg_use contains -- associative keys - their vals are information for creating the: -- numerical keys - their vals are a list, which puzzle segment indices can be used local seg_use=seg_use print('Appending segs with idx given in table...') seg_use=seg_use_list_append(seg_use,seg_use.include.idx) if seg_use.include.selected.act then print('Appending segs depending on selection: '..tostring(seg_use.include.selected.val)..'...') seg_use=seg_use_list_append(seg_use,seg_list_selected_get(seg_use.include.selected.val)) end -- seg_use.include.selected.act selection.DeselectAll() if seg_use.include.frozen.act then print('Appending segs depending on freeze: backbone: '..tostring(seg_use.include.frozen.backbone)..' sidechain:'..tostring(seg_use.include.frozen.sidechain)) seg_use=seg_use_list_append(seg_use,seg_list_frozen_get(seg_use.include.frozen.backbone,seg_use.include.frozen.sidechain)) end -- seg_use.include.frozen.act if seg_use.include.ss_cngs then print('Appending segs, where structure changes do occur...') seg_use=seg_use_list_append(seg_use,ss_cngs_get_seg_list()) end -- if seg_use.include.ss_cngs print('Appending segs with ss given in table...') seg_use=seg_use_list_append(seg_use,seg_list_ss_get(seg_use.include.ss)) print('Appending segs with aa given in table...') seg_use=seg_use_list_append(seg_use,seg_list_aa_get(seg_use.include.aa)) seg_use=seg_use_filter(seg_use) -- sort segment list and -- remove duplicate entries print('Removing segs with idx given in table...') seg_use=seg_use_list_remove(seg_use,seg_use.exclude.idx) if seg_use.exclude.selected.act then print('Removing segs depending on selection: '..tostring(seg_use.exclude.selected.val)..'...') seg_use=seg_use_list_remove(seg_use,seg_list_selected_get(seg_use.exclude.selected.val)) end -- seg_use.exclude.selected.act if seg_use.exclude.frozen.act then print('Remove segs depending on freeze: backbone: '..tostring(seg_use.exclude.frozen.backbone)..' sidechain:'..tostring(seg_use.exclude.frozen.sidechain)) seg_use=seg_use_list_remove(seg_use,seg_list_frozen_get(seg_use.exclude.frozen.backbone,seg_use.exclude.frozen.sidechain)) end -- seg_use.exclude.frozen print('Removing segs with ss given in table...') seg_use=seg_use_list_remove(seg_use,seg_list_ss_get(seg_use.exclude.ss)) print('Removing segs with aa given in table...') seg_use=seg_use_list_remove(seg_use,seg_list_aa_get(seg_use.exclude.aa)) print('\nScript will use these segs:') for i=1,#seg_use do print(i..': '..seg_use[i]) end -- for i return seg_use end -- function seg_use_list_create function seg_use_list_append(use_idx,append_idx) local use_idx=use_idx if seg_use.append_all~=false then seg_use.append_all=true end -- if seg_use.append_all -- if seg_use.append_all is not false -- force it to be true -- as no other vals are allowed if seg_use.append_all then use_idx=seg_use_list_append_all(use_idx,append_idx) else -- if seg_use.append_all~=true use_idx=seg_use_list_append_missing(use_idx,append_idx) end -- if seg_use.append_all return use_idx end -- function seg_use_list_append function seg_use_list_append_all(use_idx,append_idx) local use_idx=use_idx local i for i=1,#append_idx do -- print('Appending Idx:'..(#use_idx+1)..'=Seg:'..append_idx[i]) table.insert(use_idx,append_idx[i]) end -- for i return use_idx end -- function seg_use_list_append_all function seg_use_list_append_missing(use_idx,append_idx) local use_idx=use_idx local i for i=1,#append_idx do if val_isintable(use_idx,append_idx[i])==false then print('Appending Idx:'..(#use_idx+1)..'=Seg:'..append_idx[i]) table.insert(use_idx,append_idx[i]) end -- if val_isintable end -- for i return use_idx end -- function seg_use_list_append_missing function val_isintable(list,valsearch) -- return: -- true: if list does contain valsearch -- false: if list doesn't contain valsearch if #list==0 then return false else return key_getbyval(list,valsearch)~=nil end -- if #list end -- function val_isintable function key_getbyval(list,valsearch) -- check all keys in table list -- return first found key which has valsearch local keyres=nil local key,val for key,val in pairs(list) do -- with key loop, cycle through all keys -- for each key, get its val if val==valsearch then -- if val found keyres= key -- return it's key break -- key,val -- break key loop end -- if val end -- key loop return keyres end -- function key_getbyval function seg_use_list_remove(use_idx,remove_idx) local use_idx=use_idx local i=1 for i=1,#remove_idx do local j=1 while j<=#use_idx do if use_idx[j]==remove_idx[i] then print('Removing Idx:'..j..'=Seg:'..use_idx[j]) table.remove(use_idx,j) j=j-1 end -- if use_idx j=j+1 end -- while j end -- for i return use_idx end -- function seg_use_list_remove function seg_use_filter(seg_use) -- sort list by segment idx numbers and -- remove duplicate entries local seg_use=seg_use -- leave seg_use.sort_ascending as it is, as it is allowed to be nil print('Sorting segs ascending: '..tostring(seg_use.sort_ascending)) if seg_use.remove_double~=false then seg_use.remove_double=true end -- if seg_use.remove_double -- if seg_use.remove_double is not false -- force it to be true -- as no other vals are allowed print('Checking and removing double segs:'..tostring(seg_use.remove_double)) local i=1 while i<=(#seg_use-1) do local j=i+1 while j<=#seg_use do if seg_use[i]==seg_use[j] then -- if two elements in list are equal if seg_use.remove_double then -- print('Removing idx:'..j..' = Seg:'..seg_use[j]) table.remove(seg_use,j) j=j-1 -- as element at position j was removed, -- another one might have moved up to position j end -- if seg_use.remove_double elseif ((seg_use[i]>seg_use[j])==seg_use.sort_ascending) then -- if they are not equal -- and they don't have the order as given in seg_use.sort_ascending -- print('Swapping idx:'..i..'=Seg:'..seg_use[i]..' with idx:'..j..'=Seg:'..seg_use[j]) seg_use[i],seg_use[j]=seg_use[j],seg_use[i] end -- if seg_use j=j+1 end -- for j i=i+1 end -- while i<=(#seg_use-1) return seg_use end -- function seg_use_filter function ss_cngs_get_seg_list() local seg_found={} local seg_count2= puzzle.seg_count-1 local i=1 while i<seg_count2 do local ss1=structure.GetSecondaryStructure(i) local ss2=structure.GetSecondaryStructure(i+1) -- check ss of seg with idx i and i+1 if ss1~=ss2 then -- are ss different? -- primary, we want to find segs with loop-ss at the end of helix or sheet sections -- but sometimes helices are directly near sheets if ss1=="L" then -- loop found at i? (then other ss is behind i) then table.insert(seg_found,i) elseif ss2=="L" then -- loop found near i? (then other ss is at i) table.insert(seg_found,(i+1)) i=i+1 elseif ss1=="H" then -- helix found at i? (then other ss (but no loop) is behind i) table.insert(seg_found,i) elseif ss2=="H" then -- helix found near i? (then other ss (but no loop) is at i) table.insert(seg_found,(i+1)) i=i+1 end -- if ssx==ss end -- if ss1~=ss2 i=i+1 end -- while i<seg_count2 return seg_found end -- function ss_cngs_get_seg_list function seg_list_selected_get(selected_flag) local seg_found={} local i for i=1,puzzle.seg_count do if selection.IsSelected(i)==selected_flag then table.insert(seg_found,i) end -- if selection.IsSelected end -- for i return seg_found end -- seg_list_selected_get function seg_list_frozen_get(backbone_flag,sidechain_flag) local seg_found={} local i for i=1,puzzle.seg_count do local backbone,sidechain=freeze.IsFrozen(i) if (backbone==backbone_flag) and (sidechain==sidechain_flag) then table.insert(seg_found,i) end -- if backbone,sidechain end -- for i return seg_found end -- function seg_list_frozen_get function ss_cngs_show(structure_cngs_get,freeze_flag1,freeze_flag2) selection.DeselectAll() freeze.UnfreezeAll() print("Found changes:") local i for i=1,#seg_use do -- by variable i, cycle through all segs in list which have been found print(i..': '..seg_use[i]) -- show seg number selection.Select(seg_use[i]) -- select seg number end -- for i freeze.FreezeSelected(freeze_flag1,freeze_flag2) -- if flags are set, freeze selected segs, backbone and/or sidechains selection.DeselectAll() end -- function ss_cngs_show function seg_list_ss_get(use_ss) local seg_found={} local i for i=1,#use_ss do local j for j=1,puzzle.seg_count do if structure.GetSecondaryStructure(j)==use_ss[i] then -- print(j..': '..structure.GetSecondaryStructure(j)) table.insert(seg_found,j) end -- if structure.GetSecondaryStructure end -- for j end -- for i return seg_found end -- function seg_list_ss_get function seg_list_aa_get(use_aa) local seg_found={} local i for i=1,#use_aa do local j for j=1,puzzle.seg_count do if structure.GetAminoAcid(j)==use_aa[i] then -- print(j..': '..structure.GetAminoAcid(j)) table.insert(seg_found,j) end -- if structure.GetAminoAcid end -- for j end -- for i return seg_found end -- function seg_list_aa_get function seg_center_get() if (puzzle.seg_center.get_mode=="start") and (puzzle.seg_center.val~=nil) then -- start center seg is to get and center seg is set print('Stored center seg: '..puzzle.seg_center.val) return puzzle.seg_center.val -- use stored center seg else -- start center seg is not to get or center seg is not set -- current center seg is to get puzzle.seg_center.val= seg_center_get2() -- get current center seg return puzzle.seg_center.val end -- if puzzle.seg_center.get_mode,puzzle.seg_center.val end -- function seg_center_get function seg_center_get2() --[[ find center seg of puzzle for each seg, measure it's dst to all other segs and sum these dsts up center seg has lowest sum of dsts --]] -- print ('Getting center segment.') local dist_sum={min=math.huge;min_issegidx=0} local i for i=1,puzzle.seg_count do dist_sum.cur=0 local j for j=1,puzzle.seg_count do dist_sum.cur=dist_sum.cur+structure.GetDistance(i,j) end -- for j -- print('Distance sum for #'..i..' = '..dist_sum.cur) if dist_sum.cur<dist_sum.min then dist_sum.min=dist_sum.cur dist_sum.min_issegidx=i -- print('Distance sum min hit!') -- print('Distance sum min: #'..dist_sum.min_issegidx..' = '..dist_sum.min) end -- if dist_sum.cur end -- for i -- print('Distance sum min: #'..dist_sum.min_issegidx..' = '..dist_sum.min) -- similar to: print('Current center seg: '..dist_sum.min_issegidx) return dist_sum.min_issegidx end -- function seg_center_get2 function seg_furthest_get(seg_target) -- print ('Getting furthest segment to seg#'..seg_target) local dist={max=0;max_issegidx=0} local i for i=1,puzzle.seg_count do dist.cur= structure.GetDistance(seg_target,i) if dist.cur>dist.max then dist.max= dist.cur dist.max_issegidx=i -- print('Distance max hit!') -- print('Distance max: #'..dist.max_issegidx..' = '..dist.max) end -- if dist.cur end -- i -- print('Distance max: #'..dist.max_issegidx..' = '..dist.max) return dist.max_issegidx end -- seg_furthest_get function herd_generate(clu,first,last) print('Generating herd from '..first..' to '..last..'...') local i for i=first,last do -- print('Generating clu: '..i) clu[i]={} local j for j=1,#seg_use do -- with j, cycle through all segment indices given in seg uselist -- print('--seg:'..j..'use:'..seg_use[j]) clu[i][j]=band_generate(j) -- create clu band end -- for i clu[i].name=clu_name_get(i,"r") end -- for i return clu end -- function herd_generate function band_generate(seg_cur) local band={act=random_flag2(herd.mutate.prob.off,true,true)} -- depending on random flag (influenced by herd.mutate.prob.off), -- turn off band when indicated if clu.band_to.val=="space" then -- following vals are pure random float numbers between 0 and <1 band.rho= math.random() band.theta= math.random() -- band.theta= .5; band.phi= math.random() else band.d_cng1= math.random() if clu.band_to.val=="random" then -- random segs? if puzzle.seg_random.get_mode=="list" then -- random seg in list ? repeat band.seg_target= seg_use[math.random(1,#seg_use)] until band.seg_target~=seg_use[seg_cur] -- prevent banding seg to itself -- print('Random seg target of list: '..band.seg_target..' (not '..seg_use[seg_cur]..')') else -- puzzle.seg_random.get_mode=="all" -- random seg in puzzle ? repeat band.seg_target= math.random(1,puzzle.seg_count) until band.seg_target~=seg_use[seg_cur] -- prevent banding seg to itself -- print('Random seg target of all: '..band.seg_target..' (not '..seg_use[seg_cur]..')') end -- if puzzle.seg_random.get_mode end -- if clu.band_to.val end -- if clu.band_to.val if clu.d_cng2.act then -- secondary bands are used band.d_cng2= math.random() end -- if clu.d_cng2.act return band end -- function band_generate function herd_mutate(clu,first,last) local clu= clu print('Mutating herd from '..first..' to '..last..'...') local i for i=first,last do if herd.mutate.genes_show.act then print('Mutating clu#'..i) end -- if if herd.mutate.genes_show.act local j for j=1,#clu[i] do if herd.mutate.genes_show.act then print('Mutating band#'..j) end -- if herd.mutate.genes_show.act clu[i][j]=band_mutate(clu[i][j]) end -- for j end -- for i return clu end -- function herd_mutate function band_mutate(clu_band) local band={act=mutate_bool_val(clu_band.act)} if clu.band_to.val=="space" then band.rho= mutate_float_val(clu_band.rho) band.theta= mutate_float_val(clu_band.theta) band.phi= mutate_float_val(clu_band.phi) else band.d_cng1= mutate_float_val(clu_band.d_cng1) if clu.band_to.val=="random" then band.seg_target= mutate_int_seg(clu_band.seg_target) end -- if clu.band_to.val end -- if clu.band_to.val if clu.d_cng2.act then -- secondary bands are used band.d_cng2= mutate_float_val(clu_band.d_cng2) end -- if clu.d_cng2.act return band end -- function band_mutate function mutate_bool_val(val) -- depending on herd.mutate.prob.vals -- possibly toggle band activity: -- band on -> band off -- band off -> band on local val= val or false -- break reference if random_flag1(herd.mutate.prob.vals) then -- has random hit possibility? mutate_val_show('Old',val) val= not(val) -- invert boolean mutate_val_show('New',val) return val else return val end -- if random_flag1 end -- function mutate_bool_val function mutate_float_val(val) -- depending on herd.mutate.prob.vals -- possibly change normalized val 0...1 -- by a random amount between +/-10^(min...max) local val= val+0 -- break reference if random_flag1(herd.mutate.prob.vals) then -- has random hit possibility? mutate_val_show('Old',val) val= val+(math.random(0,1)*2-1)*(10^(math.random(herd.mutate.rng10e.min,herd.mutate.rng10e.max))) val= range_wrap(val,0,1,0) mutate_val_show('New',val) return val else return val end -- if random_flag1 end -- function mutate_float_val function mutate_int_seg(val) -- depending on herd.mutate.prob.vals -- possibly change segment idx -- by a random amount between +/-1 local val= val+0 -- break reference if random_flag1(herd.mutate.prob.vals) then -- has random hit possibility? mutate_val_show('Old',val) val= val+(random_flag2(0.6,false,false)*2-1) -- 0.6 (0.5 would be 50:50) means appearing: -- +1: more often -- -1: less often val= range_wrap(val,1,puzzle.seg_count,1) mutate_val_show('New',val) return val else return val end -- if random_flag1 end -- function mutate_float_val function mutate_val_show(text,val) if herd.mutate.genes_show.act then print(text..' val: '..tostring(val)) end -- if herd.mutate.genes_show.act end -- function mutate_val_show function clu_name_get(idx,kind) -- create a table with following name elements: return { gen=gen.cur; clu=idx; knd=kind; dat=os.date() } end -- function clu_name_get function clu_name_get_string(clu_name,dat_flag) local OS="" OS= OS..'G'..clu_name.gen OS= OS..'C'..clu_name.clu OS= OS..'K'..clu_name.knd if dat_flag then OS= OS..'D'..clu_name.dat end -- if dat_flag return OS end -- function clu_name_get function clu_apply_bands(band_use) -- apply all fixed bands -- and all which are stored in current clu passed by table band_use -- for each band pair, depending on mode clu.band_to.val -- connect origin seg (given in uselist) -- to another seg ("center", "furthest", "random") or -- to "space" -- apply fixed bands given in clu.fix: local i for i=1,#clu.fix do -- print(clu.fix[i][1],clu.fix[i][2]) band.AddBetweenSegments(clu.fix[i][1],clu.fix[i][2]) local len= structure.GetDistance(clu.fix[i][1],clu.fix[i][2])+clu.fix.lencng len= range_clamp(len,clu.band_sys_lim.min,clu.band_sys_lim.max) band.SetGoalLength(band.GetCount(),len) band.SetStrength(band.GetCount(),clu.fix.str) end -- for i local seg_center if clu.band_to.val=="center" then -- banding to "center" is used -- alternative: -- if (clu.band_to.val=="center") or (clu.band_to.val=="space") then -- banding to "center" or "space" is used seg_center= seg_center_get() -- get current center segment -- fetch it before cycling through all clu bands, -- as it is the same each time end -- if clu.band_to.val -- appply random bands given in clu: for i=1,#band_use do -- with i, cycle through all clu bands and their data if band_use[i].act then -- only if current band i is act (gene is not off) local seg_origin= seg_use[i] -- get origin seg to band from seg uselist if clu.band_to.val=="space" then -- if banding to space, but not to other segs -- get -- axis reference seg indices (seg_axis_x,seg_axis_y) and -- calculated here -- spatial vals (rho, theta,phi) from normalzed vals -- given by clu data local seg_axis_x= seg_get_idx("usepuzz",seg_origin,-1) -- band x-angle reference is previous seg in puzzle local seg_axis_y= seg_get_idx("usepuzz",seg_origin,1) -- band y-angle reference is next seg in puzzle -- alternative: -- local seg_axis_y= seg_center -- band y-angle reference is center seg in puzzle -- rho uses band str1 band.Add2(i, seg_origin, seg_axis_x, seg_axis_y, band_use[i].rho, band_use[i].theta, band_use[i].phi, clu.pull.str1) else -- (clu.band_to.val=="center") or (clu.band_to.val=="furthest") or (clu.band_to.val=="random") then -- not banding to space, but to other segs -- having a relative dst or are random -- calculated here local seg_target1 if clu.band_to.val=="center" then seg_target1= seg_center -- primary target seg is center seg in puzzle elseif clu.band_to.val=="furthest" then seg_target1= seg_furthest_get(seg_origin) -- primary target seg is furthest to origin seg elseif clu.band_to.val=="random" then seg_target1= band_use[i].seg_target -- primary target seg is random else-- clu.band_to.val has other val assert(false,'function clu_apply_bands: clu.band_to.val is invalid: '..tostring(clu.band_to.val)) end -- if clu.band_to.val -- primary target seg uses band str1 band.AddBetweenSegments2(i, seg_origin, seg_target1, band_use[i].d_cng1, clu.d_cng1.min, clu.d_cng1.max, clu.pull.str1) end -- if clu.band_to.val if clu.d_cng2.act then -- secondary bands are used local seg_target2= seg_get_idx("uselist",i,1) -- secondary target seg is next in seg uselist -- secondary target seg uses band str2 band.AddBetweenSegments2(i, seg_origin, seg_target2, band_use[i].d_cng2, clu.d_cng2.min, clu.d_cng2.max, clu.pull.str2) end -- if clu.d_cng2.act else -- print('B#'..i..' off') end -- if band_use[i].act end -- for i end -- function clu_apply_bands function seg_get_idx(seg_option,val,offset) if seg_option=="uselist" then return seg_use[range_wrap((val+offset),1,#seg_use,1)] -- offset=+1 : angle reference is next seg in uselist -- offset=-1 : angle reference is prev seg in uselist elseif seg_option=="usepuzz" then return range_wrap((val+offset),1,puzzle.seg_count,1) -- offset=+1 : angle reference is next seg in puzzle -- offset=-1 : angle reference is prev seg in puzzle else assert(false, 'function seg_get_idx: seg_option has invalid val: '..tostring(seg_get_idx)) end -- if seg_option end -- function seg_get_idx function band.Add2(i, seg_origin, seg_axis_x, seg_axis_y, rho, theta, phi, str) -- print(i,seg_origin,seg_axis_x,seg_axis_y,rho,theta,phi,str) local seg_axis_y,cng_flag= seg_axis_y_check_idx(seg_origin,seg_axis_x,seg_axis_y) -- avoid seg_axis_y having the same val as seg_axis_x if (cng_flag and clu.band_skip_wrong)==false then local R= rho^(1/3) + 0.001 -- bend val rho 0..1 towards 1 by cubic root giving val R local rho= R*(clu.rho.max-clu.rho.min)+clu.rho.min -- stretch R 0...1 to min...max giving rho again rho= range_clamp(rho,0,clu.band_sys_lim.max) -- rho within limits? local theta= math.acos(2*theta-1)+0 -- x-angle (azimut) range: 180 degrees (here: pi) local phi= 2*math.pi*phi+0 -- y-angle (elevation) range: 360 degrees (here: 2*pi) local band_count1= band.GetCount() -- check number of bands before applying another band band.Add(seg_origin, seg_axis_x, seg_axis_y, rho, theta, phi) local band_count2= band.GetCount() -- check number of bands after applying another band if band_count1~=band_count2 then -- only if band has been created -- show its attributes print( 'B#'..i.. ' Seg#'..seg_origin.. ':('..seg_axis_x.. '&'..seg_axis_y.. ') Rho:'..crop_digits(rho,3).. ' Theta:'..crop_digits(math.deg(theta),3).."o".. -- ..string.char(39).. ' Phi:'..crop_digits(math.deg(phi),3).."o".. ' Str:'..crop_digits(str,3) ) band.SetStrength(band_count2,str) -- change str else print('B#'..i..' skipped') end -- if band_count else print('B#'..i..' skipped') end -- if (cng_flag and clu.band_skip_wrong) == false end -- function band.Add2 function seg_axis_y_check_idx(seg_origin,seg_axis_x,seg_axis_y) -- avoid seg_axis_y having the same val as seg_axis_x or seg_origin local seg_axis_y= seg_axis_y -- print('seg_axis_y: '..seg_axis_y) local cng_flag= false if (seg_axis_y==seg_axis_x) or (seg_axis_y==seg_origin) then seg_axis_y= range_wrap((seg_origin+1),1,puzzle.seg_count,1) cng_flag= true end -- if seg_axis_y --[[ if cng_flag then print('Value changed') end -- if cng_flag --]] return seg_axis_y,cng_flag end -- function seg_axis_y_check_idx function band.AddBetweenSegments2(i, seg_origin, seg_target, d_cng, d_cng_min, d_cng_max, str) -- print(i,seg_origin,seg_target,d_cng,d_cng_min,d_cng_max,str) seg_target,cng_flag= seg_target_check_spat(seg_origin,seg_target) -- avoid seg_target beeing too close to seg_origin if (cng_flag and clu.band_skip_wrong)==false then local dst= structure.GetDistance(seg_origin,seg_target) local cng= (d_cng-0.5)*2 -- as d_cng is a normalized val 0...1 internally -- (i don't want to mess with band lens before) -- stretch it to -1...1 -- print('D_cng:'..crop_digits(d_cng,3)..' Cng:'..crop_digits(cng,3)) local sign= above0(cng) -- -1: compression -- +1: expansion cng= sign*(math.abs(cng)*(d_cng_max-d_cng_min)+d_cng_min) -- as sign is stored now, truncate it from cng, -- resulting an absoulte val 0...1 -- do linear transformation like cng*factor+offset -- (factor: delta(extremae): (max-min), offset: min) -- this maps 0...1 to min...max -- finally, multiply by sign again to get direction back local len= dst+cng -- print('Cng:'..crop_digits(cng,3)..' len:'..crop_digits(len,3)) len= range_clamp(len,clu.band_sys_lim.min,clu.band_sys_lim.max) local band_count1= band.GetCount() -- check number of bands before applying another band band.AddBetweenSegments(seg_origin,seg_target) local band_count2= band.GetCount() -- check number of bands after applying another band if band_count1~=band_count2 then -- only if band has been created -- show its attributes print( 'B#'..i.. ' Seg#'..seg_origin.. ':'..seg_target.. ' Len:'..crop_digits(len,3).. ' (Cng:'..crop_digits(cng,3)..')'.. ' Str:'..crop_digits(str,3) ) -- print('Distance:'..crop_digits(dst,3)..' Cng:'..crop_digits(cng,3)) band.SetGoalLength(band_count2,len) -- change len band.SetStrength(band_count2,str) -- change str else print('B#'..i..' skipped') end -- if band_count else print('B#'..i..' skipped') end -- if (cng_flag and clu.band_skip_wrong)==false end -- function band.AddBetweenSegments2 function seg_target_check_spat(seg_origin,seg_target) -- avoid seg_target beeing too close to seg_origin -- by measuring spatial dst local seg_target= seg_target local cng_flag= false while structure.GetDistance(seg_origin,seg_target)<clu.seg_min_dist do seg_target,cng_flag= seg_target_too_close_show(seg_origin,seg_target) end -- structure.GetDistance --[[ if cng_flag then print('Value changed') end -- if cng_flag --]] return seg_target,cng_flag end -- seg_target_check_spat function seg_target_too_close_show(seg_origin,seg_target) -- print('seg_origin='..seg_origin..':'..'seg_target='..seg_target..' too close.') -- print('increasing seg_target idx.') local seg_target= range_wrap((seg_target+1),1,puzzle.seg_count,1) return seg_target,true end -- function seg_too_close_show function pull_do() if pull.act then print('Pulling...') CI_apply(pull.CI_default,'Default CI for pull:') -- if given, apply default CI local i for i=1,#pull do CI_method(pull[i]) end -- for i end -- if pull.act end -- function pull_do function release_n_fuse() if release.act then print('Releasing...') CI_apply(release.CI_default,'Default CI for release: ') -- if given, apply default CI CI_method_batch(release) end -- if release.act if fuse.act then -- print('Fusing...') local i for i=1,#fuse do print('Fuse#'..i..'...') CI_apply(fuse.CI_default,'Default CI for fuse: ') -- if given, apply default CI CI_method_batch(fuse[i]) end -- for i end -- if fuse.act end -- function release_n_fuse function CI_method_batch(CI_method_list) quicksave_do("load",3) local i for i=1,#CI_method_list do CI_method(CI_method_list[i]) quicksave_do("save",4) if quicksave_score[4]>quicksave_score[5] then quicksave_do("save",5) end -- if quicksave_score end -- for i end -- function CI_method_batch function CI_method(prm) -- depending on passed table prm: -- 1. if given, set CI -- 2. for a given number of itrs and -- 3. use a foldit tool (backbone/sidechain move or mutate) -- i intentionally use only shake/wiggle all -- and didn't implement local wiggle, -- as it can make the puzzle too stiff (local maximum) --[[ print('CI_method prms:') local k,v for k,v in pairs(prm) do print(k,v) end --]] CI_apply(prm.CI,'Current CI: ') -- if given, apply CI if type(prm.itr)=="number" then -- number of itrs given? if prm.itr>0 then -- depending on prm.cmd, do one of these tools: if prm.cmd=="m" then if puzzle.mutate.act then -- print('Mutating for '..prm.itr..' iterations...') structure.MutateSidechainsAll(prm.itr) end -- if puzzle.mutate.act elseif prm.cmd=="s" then -- print('Shaking for '..prm.itr..' iterations...') structure.ShakeSidechainsAll(prm.itr) elseif prm.cmd=="w" then -- print('wiggling for '..prm.itr..' iterations...') structure.WiggleAll(prm.itr,prm.backbone,prm.sidechains) end -- if prm.cmd end -- if prm.itr end -- if prm.itr end -- function CI_method function CI_apply(CI,text) if type(CI)=="number" then -- CI given? -- print(text..CI) behavior.SetClashImportance(CI) -- set it end -- if type(CI) end -- function CI_apply function quicksave_do(mode,slot) if mode=="save" then save.Quicksave(slot) quicksave_score[slot]=current.GetEnergyScore() -- print('Quicksaved to slot:'..slot..' - Score:'..crop_digits(quicksave_score[slot],3)) elseif mode=="load" then save.Quickload(slot) -- print('Quickloaded from slot:'..slot..' - Score:'..crop_digits(quicksave_score[slot],3)) end -- if mode end -- function quicksave_do function quicksave_do_batch(mode,first,last) local i for i=first,last do quicksave_do(mode,i) end -- for i end -- function quicksave_do function herd_fitnessandsort(clu) local clu= clu -- print("Getting herd's scoregain minimum...") clu.scoregain_min= clu[1].scoregain local i for i=2,#clu do if clu[i].scoregain<clu.scoregain_min then clu.scoregain_min=clu[i].scoregain end -- if clu[i].scoregain end -- for i -- print('Old minimum clu scoregain: '..clu.scoregain_min) if clu.scoregain_min>1 then -- minimum gain >1? clu.scoregain_min= 1 -- we don't need to push values end -- if clu.scoregain_min -- print('New minimum clu scoregain: '..clu.scoregain_min) -- print("Putting clu's fitness vals...") -- scoregain vals -> fitness vals -- smallest fitness val >=1 (prevent fitnesses <=0) local i for i=1,#clu do clu[i].fitness= clu[i].scoregain-clu.scoregain_min+1 end -- for i clu= herd_normalize(clu) -- sum of all fitness vals==1 if (herd.fitness_e~=nil) and (herd.fitness_e~=1) then -- bending exponent given? -- print("Bending clu's fitnesses...") local i for i=1,#clu do clu[i].fitness= clu[i].fitness^(herd.fitness_e) -- bend fitness val -> 1 (if 0<exponent<1) end -- for i clu= herd_normalize(clu) -- sum of all fitness vals==1 end -- if herd.fitness_e -- print("Sorting herd by clu's fitnesses...") local i for i=1,(#clu-1) do local j for j=(i+1),#clu do if clu[i].fitness<clu[j].fitness then clu[i],clu[j]=clu[j],clu[i] end -- if clu[i].fitness end -- for j end -- for i print('\nHerd after sorting is:') herd_show(clu) -- for debugging: -- list_show_deep(clu,"clu") print() return clu end -- function herd_fitnessandsort function herd_normalize(clu) local clu= clu clu.fitness_sum= clu_fitness_sum_get(clu) -- print("Normalizing clu's fitnesses...") local i for i=1,#clu do clu[i].fitness= clu[i].fitness/clu.fitness_sum end -- for i -- print('Herd with normalized fitness is:') -- herd_show(clu) return clu end -- function herd_normalize function clu_fitness_sum_get(clu) -- print("Getting herd's fitness sum...") local fitness_sum= 0 local i for i=1,#clu do fitness_sum= fitness_sum+clu[i].fitness end -- for i -- print('Clus have fitness sum: '..fitness_sum) return fitness_sum end -- function function herd_show(clu) print('Gen#'..gen.cur..' had start score: '..quicksave_score[2]) local i for i=1,#clu do print( 'Clu#'..i.. ' Name:'..clu_name_get_string(clu[i].name,false).. ' Score:'..crop_digits(clu[i].score,3).. ' Gain:'..crop_digits(clu[i].scoregain,3).. ' Fitness:'..crop_digits(clu[i].fitness,6) ) end -- for i clu.fitness_sum= clu_fitness_sum_get(clu) -- print('Fitness checksum: '..clu.fitness_sum) end -- function herd_show function breed_n_generate(clu) if herd.EO_mode.cur==2 then clu= herd_generate( clu, herd.breed.first, herd.size ) elseif herd.EO_mode.cur==1 then clu= herd_generate( clu, (herd.breed.last+1), herd.size ) else clu= herd_breed( clu, herd.breed.first, herd.breed.last ) if herd.mutate.act then clu= herd_mutate( clu, herd.breed.first, herd.breed.last ) end -- if herd.mutate.act then clu= herd_generate( clu, (herd.breed.last+1), herd.size ) end -- if herd.EO_mode.cur return clu end -- function breed_n_generate function herd_breed(clu,first,last) local kid={} -- intialize kid clus breeding section if herd.breed.mode.val=="dual" then kid= herd_breed_dual(first,last) -- get kid by mom & dad breeding elseif herd.breed.mode.val=="poly" then kid= herd_breed_poly(first,last) -- get kid by orgy breeding else assert(false,'function herd_breed: herd.breed.mode.val is invalid: '..tostring(herd.breed.mode.val)) end -- herd.breed.mode.val local i for i=first,last do -- move kids to herd -- overwrite old clus clu[i]= kid[i] clu[i].name= clu_name_get(i,"b") end -- for i return clu -- return new clu table end -- function herd_breed function herd_breed_dual(first,last) local kid={} -- intialize kid clus breeding section local i for i=first,last do -- fill breeding section local dad if herd.breed.roulette.dad then dad=roulette() else dad= range_wrap((i-first+1),1,herd.size,1) end -- if herd.breed.roulette.dad local mom if herd.breed.roulette.mom then mom= roulette() while mom==dad do mom=roulette() end -- while mom else mom= range_wrap((dad+1),1,herd.size,1) end -- if herd.breed.roulette.dad print('Breeding clu#'..i..'(kid) from: '.. 'clu#'..dad..'(dad*'..crop_digits(herd.breed.dad_genes_prob,3)..')'.. ' + '.. 'clu#'..mom..'(mom*'..crop_digits((1-herd.breed.dad_genes_prob),3)..')') kid[i]={} for j=1,#clu[i] do if random_flag1(herd.breed.dad_genes_prob) then print_gene(j,dad,"dad") kid[i][j]=clu[dad][j] else print_gene(j,mom,"mom") kid[i][j]=clu[mom][j] end -- if random_flag1 end -- for j end -- for i return kid end -- function function herd_breed_dual function herd_breed_poly(first,last) local kid={} -- intialize kid clus breeding section local i for i=first,last do -- fill breeding section print('Breeding clu#'..i..'(kid) from: whole herd') kid[i]={} for j=1,#clu[i] do local parent= roulette() print_gene(j,parent,"roulette") kid[i][j]=clu[parent][j] end -- for j end -- for i return kid end -- function herd_breed_poly function print_gene(j,parent_val,parent_text) if herd.breed.genes_show.act then -- detailed gene view activated? print('Gene #'..j..': clu#'..tostring(parent_val)..'('..tostring(parent_text)..')') end -- if herd.breed.genes_show act end -- function print_gene function roulette() local fitness_target= math.random() -- set random fitness sum goal -- print('Fitness target:'..crop_digits(fitness_target,6)) local wheel= math.random(1,herd.size) -- initialize random roulette wheel position local fitness_sum= clu[wheel].fitness -- get fitness val at wheel position -- print('Wheel current:'..wheel..' Fitness sum:'..crop_digits(fitness_sum,6)) while fitness_sum<=fitness_target do -- as long as fitness sum hasn't reached fitness sum goal if herd.breed.roulette.israndom then -- if roulette is random (not default) -- set another random roulette wheel position wheel= math.random(1,herd.size) else -- if roulette is not random (default) wheel= range_wrap((wheel+1),1,herd.size,1) -- spin wheel end -- if herd.breed.roulette.israndom fitness_sum= fitness_sum+clu[wheel].fitness -- print('Wheel current:'..wheel..' Fitness sum:'..crop_digits(fitness_sum,6)) end -- while fitness_sum -- print('Wheel end:'..wheel..' Fitness sum:'..crop_digits(fitness_sum,6)) return wheel end -- function roulette function band_breed(band_dad,band_mom) local band_child={} local i for i=1,#band_dad do if random_flag1(herd.breed.dad_genes_prob) then band_child[i]=val_get(band_dad[i]) else band_child[i]=val_get(band_mom[i]) end -- if random_flag1 end -- for i return band_child end -- band_breed() function val_get(band) local k,v for k,v in pairs(band) do print(k,v) if type(v)=="boolean" then return (v or false) else return (v+0) end -- if type end -- for k end -- val_get() function main() print('\nMain Programm start.') puzzle.random_seed= random_seed_set_val(puzzle.random_seed) print('Puzzle segs mutability: '..tostring(puzzle.ismutable)) if puzzle.ismutable==false then puzzle.mutate.act= false end -- if puzzle.ismutable print('Puzzle segs mutation: '..tostring(puzzle.mutate.act)) print('Herd genes off prob: '..herd.mutate.prob.off) print('Herd mutability prob: '..herd.mutate.prob.vals) if herd.mutate.prob.vals<=0 then -- herd mutate probability is not given herd.mutate.act= false -- deactivate hed genes mutating -- (not needed, but prevents calling function) end -- if herd.mutate.prob.vals print('Herd mutation: '..tostring(herd.mutate.act)) if herd.breed.first>herd.breed.last then herd.breed.first,herd.breed.last= herd.breed.last,herd.breed.first end -- if herd.breed.first print('EO mode: '..herd.EO_mode.cur) print('Breed mode: '..herd.breed.mode.val) print('Clu band mode old: '..clu.band_to.val) if clu.band_to.val=="center" then -- banding to center if puzzle.seg_center.get_mode=="start" then -- get center seg once? puzzle.seg_center.val= seg_center_get() end -- if puzzle.seg_center.get_mode elseif clu.band_to.val=="user" then -- banding to user-defined seg idx assert((puzzle.seg_center.val>0) and (puzzle.seg_center.val<(puzzle.seg_count+1)),'function main: puzzle.seg_center.val is invalid: '..tostring(puzzle.seg_center.val)) puzzle.seg_center.get_mode="start" clu.band_to.val= "center" elseif string.sub(clu.band_to.val, 1, 6)=="random" then -- banding to random if clu.band_to.val=="random1" then puzzle.seg_random.get_mode= "list" else -- if clu.band_to.val=="random2" puzzle.seg_random.get_mode= "all" end -- if clu.band_to.val clu.band_to.val= "random" end -- if clu.band_to.val print('Clu band mode new: '..clu.band_to.val) print('Seg center get_mode: '..puzzle.seg_center.get_mode) print('Seg random get_mode: '..puzzle.seg_random.get_mode) if (clu.band_to.val=="center") and (puzzle.seg_center.get_mode== "start") then -- always the same "center" seg used? table.insert(seg_use.exclude.idx,puzzle.seg_center.val) -- add it's idx to exclude candidates end -- if clu.band_to.val,puzzle.seg_center.get_mode seg_use= seg_use_list_create(seg_use) -- create seg list based on informations given at declaration -- for debugging before generation testing, uncomment this: -- prms_show() gen.cur= 1 -- initialize gen counter quicksave_score={} -- saveslot 1: script start -- saveslot 2: gen/clu start = before applying bands/pull -- saveslot 3: after pull = before release/fuse start -- saveslot 4: after single release/fuse step -- saveslot 5: after all releases/fuses per clu = best result for current clu -- saveslot 6: best result for current gen -- saveslot 7: best result so far -- save to all slots to initialize states & scores: quicksave_do_batch("save",1,7) recentbest.Save() -- print('Set Recent Best') clu=herd_generate(clu,1,herd.size) -- list_show_deep(clu,"clu") --show all clu informations herd.test={first=1,last=herd.size} -- normally, test all generated clus while gen.cur<=gen.max do print('\n\nGen: '..gen.cur) if herd.use.hybrid.state~=nil then -- hybrid loading is used, change loading state if gen.cur%herd.use.hybrid.gens==1 then -- number of gens to trigger reloading start state is reached herd.use.state.val= "start" -- load start state else -- number of gens to trigger reloading start state is not reached herd.use.state.val= herd.use.hybrid.state -- load specified state end -- if gen.cur end -- if herd.use.hybrid.state local loaded_text -- load puzzle state before pulling: if herd.use.state.val=="start" then quicksave_do("load",1) loaded_text= "Script Start State" elseif herd.use.state.val=="gen" then quicksave_do("load",6) loaded_text= "Gen's Best State" elseif herd.use.state.val=="script" then quicksave_do("load",7) loaded_text= "Script's Best State" else recentbest.Restore() loaded_text= "Recent Best State" end -- if herd.use.state.val print('Loaded: '..loaded_text..' - Score: '..crop_digits(current.GetEnergyScore(),3)) -- save to these slots to initialize states & scores: quicksave_do_batch("save",2,6) if (herd.use.state.val=="start") and (herd.use.hybrid.state==nil) then -- if script start state is always loaded herd.test.first= 1 -- reset test start clu (first clu of gen to test) while clu[herd.test.first].name.gen~=gen.cur do -- test start clu was not generated in this gen (it is an old one) herd.test.first= herd.test.first+1 -- use next clu if herd.test.first>herd.size then -- if next clu is too high (higher than herd size) herd.test.first=1 -- set it to 1 again break -- exit start clu checking end -- if end -- while clu end -- if if herd.use.state.val local clu_cur for clu_cur=herd.test.first,herd.test.last do -- print('clu: '..clu_cur) print('\nTesting: G'..gen.cur.. 'C'..clu_cur.. '/'..herd.size.. ' Name: '..clu_name_get_string(clu[clu_cur].name,true)) quicksave_do("load",2) band.DeleteAll() clu_apply_bands(clu[clu_cur]) pull_do() band.DeleteAll() -- save to these slots to initialize states & scores: quicksave_do_batch("save",3,5) print('Score after pulling: '..crop_digits(quicksave_score[3],3)) release_n_fuse() clu[clu_cur].score=quicksave_score[5] clu[clu_cur].scoregain=clu[clu_cur].score-quicksave_score[2] print("This clu's best Score: "..crop_digits(quicksave_score[5],3).." - Gain: "..crop_digits(clu[clu_cur].scoregain,3)) if (clu_cur==herd.test.first) or (quicksave_score[5]>quicksave_score[6]) then quicksave_do("load",5) quicksave_do("save",6) print('New best result for this gen: '..crop_digits(quicksave_score[6],3)) end -- if clu_cur if quicksave_score[6]>quicksave_score[7] then quicksave_do("load",6) quicksave_do("save",7) print('New best result so far! '..crop_digits(quicksave_score[7],3)) end -- if quicksave_score end -- for clu_cur clu=herd_fitnessandsort(clu) -- for each clu calculate fitness given by it's score gain -- sort clus by fitness, best gain on top gen.cur=gen.cur+1 -- increase generation counter now, -- so newly generated/breeded clus will -- get an actual "birth date" = next gen if (herd.flush_gain_thr.act==false) or (clu[1].scoregain>=herd.flush_gain_thr.thr) then -- best gain checking is off -- or gain is good enough clu=breed_n_generate(clu) -- keep good -- breed good over mediocre -- replace bad by new ones herd.flush_gain_reset.cur= 0 -- reset flush counter else -- best gain checking is on -- and gain is bad enough print('\nTarget result for this gen: '..crop_digits(herd.flush_gain_thr.thr,3)) print('Best result for this gen only: '..crop_digits(clu[1].scoregain,3)) herd.flush_gain_reset.cur= herd.flush_gain_reset.cur+1 -- increase flush counter print('Herd flush #'..herd.flush_gain_reset.cur) clu=herd_generate(clu,1,herd.size) -- flush entire herd -- replace all clus by new ones if herd.flush_gain_reset.act then -- reset after flush activated? if herd.flush_gain_reset.cur>herd.flush_gain_reset.thr then print('Number of flushs exceeded.') print('Restarting from script start state.') quicksave_do("load",1) -- get script start state again -- save to (almost) all slots to flush states & scores: quicksave_do_batch("save",2,7) recentbest.Save() -- print('Set Recent Best') herd.flush_gain_reset.cur= 0 -- reset flush counter end -- if herd.flush_gain_reset.cur end -- if herd.flush_gain_reset.act end -- if herd.flush_gain_thr.act,clu[1].scoregain end -- while gen.cur~=gen.max end -- function main function prms_cng() local dialog_val_cng= false do -- dialog 0 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 0/7') ask.Input10= dialog.AddLabel("Change default values here? There are many!") ask.Input1a= dialog.AddCheckbox("edit",dialog_val_cng) ask.Input12= dialog.AddLabel(" checked: OK to change values") ask.Input13= dialog.AddLabel("not checked: OK to launch script") ask= ask_window(ask) dialog_val_cng= ask.Input1a.value end -- do dialog 0 if dialog_val_cng then if true then -- dialog 1 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 1/7') if puzzle.random_seed==nil then puzzle.random_seed= "" end -- if puzzle.random_seed ask.Input10= dialog.AddTextbox("Random seed ",tostring(puzzle.random_seed)) ask.Label11= dialog.AddLabel("Blank : use new seed".. "\nNumber: use this seed") ask.Label12= dialog.AddLabel(" ") ask.Input20= dialog.AddSlider("Herd size",herd.size,4,16,0) ask.Label21= dialog.AddLabel("Number of clu(ster)s for one gen(eration)") ask= ask_window(ask) puzzle.random_seed= ask.Input10.value herd.size= ask.Input20.value end -- if dialog 1 if true then -- dialog 2 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 2/7') local keep=0 if (herd.size%2)==0 then -- if number of clus is even keep=2 -- keep 2 clus else -- ff number of clu is not even keep=3 -- Keep 3 clus end -- if (herd.size%2) local breed=(herd.size-keep)/2 -- after subtracting kept clus, -- divide the rest by 2 -- first half for breeding: herd.breed.first=keep+1 herd.breed.last=keep+breed -- other half for creating new -- herd.breed.last+1...herd.size will be random ask.Label00= dialog.AddLabel(" ") ask.Label01= dialog.AddLabel("Breed settings:".. "\nAfter one gen,".. "\nreplace mediocre clu# by new breeded ones") ask.Label02= dialog.AddLabel(" ") ask.Input1x= dialog.AddSlider("First",herd.breed.first,1,herd.size,0) ask.Label11= dialog.AddLabel("clu# to be replaced".. "\nAll < are kept as parents") ask.Label12= dialog.AddLabel(" ") ask.Input2x= dialog.AddSlider("Last",herd.breed.last,1,herd.size,0) ask.Label21= dialog.AddLabel("clu# to be replaced".. "\nAll > are created new") ask.Label22= dialog.AddLabel(" ") ask.Label30= dialog.AddLabel("Breed settings are also used by EO as orientation") ask= ask_window(ask) herd.breed.first= ask.Input1x.value herd.breed.last= ask.Input2x.value end -- if dialog 2 if true then -- dialog 3 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 3/7') ask.Label10= dialog.AddLabel("Extremal Optimization (discard mediocre clus)") ask.Input1x= dialog.AddSlider("EO mode",herd.EO_mode.cur,herd.EO_mode.min,herd.EO_mode.max,0) ask.Label12= dialog.AddLabel(" ") ask.Label13= dialog.AddLabel("0: no EO (GA!), replace m.clus by breeded ones".. "\n1: use EO, don't just leave m.clus as they are".. "\n2: use EO, don't replace m.clus by random ones") ask.Label14= dialog.AddLabel(" ") ask.Label15= dialog.AddLabel(" ") local state={list=herd.use.state.list} ask.Label20= dialog.AddLabel("Puzzle state for each gen") ask.Input2x= dialog.AddSlider("Load state",key_getbyval(state.list,herd.use.state.val),1,#state.list,0) ask.Label22= dialog.AddLabel(" ") ask.Label23= dialog.AddLabel(list_text(state.list)) ask.Label24= dialog.AddLabel(" ") ask.Label25= dialog.AddLabel(" ") ask.Label26= dialog.AddLabel(" ") ask.Label27= dialog.AddLabel("start: as now".. "\n (never reach local max)".. "\ngen: best result of last gen".. "\n (avoid local max)".. "\nscript: script's best".. "\n (similar to recent)".. "\nrecent: recent best state".. "\n (can produce local max)") ask.Label28= dialog.AddLabel(" ") ask.Label29= dialog.AddLabel(" ") ask.Label2A= dialog.AddLabel(" ") ask.Label2B= dialog.AddLabel(" ") local band_to={list=clu.band_to.list} ask.Label30= dialog.AddLabel("Band these segs to target seg:".. "\n+ in list (detected by script)".. "\n+ game interface selection (blue)") ask.Label31= dialog.AddLabel(" ") ask.Input3x= dialog.AddSlider("Band mode",key_getbyval(band_to.list,clu.band_to.val),1,#band_to.list,0) ask.Label33= dialog.AddLabel(" ") ask.Label34= dialog.AddLabel(" ") ask.Label35= dialog.AddLabel(list_text(band_to.list)) ask.Label36= dialog.AddLabel(" ") ask.Label37= dialog.AddLabel(" ") ask.Label38= dialog.AddLabel(" ") ask.Label39= dialog.AddLabel("center: 'center' seg is auto-detected".. "\nuser: 'center' seg is set manually".. "\nrandom1: uselist (detected & selection) to uselist".. "\nrandom2: uselist to all of puzzle (classic)") ask= ask_window(ask) herd.EO_mode.cur= ask.Input1x.value herd.use.state.val= state.list[ask.Input2x.value] clu.band_to.val= band_to.list[ask.Input3x.value] end -- if dialog 3 if clu.band_to.val=="user" then -- dialog 3.1 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 3.1/7') ask.Label10= dialog.AddLabel("Set your own 'center' seg") ask.Input1x= dialog.AddSlider("Center seg",seg_center_get2(),1,puzzle.seg_count,0) ask= ask_window(ask) puzzle.seg_center.val= ask.Input1x.value end -- if clu.band_to.val ... then dialog 3.1 if true then -- dialog 4 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 4/7') ask.Input1a= dialog.AddCheckbox("Mutate sidechains",puzzle.mutate.act) ask.Label11= dialog.AddLabel("Works only if puzzle is mutable") ask.Label12= dialog.AddLabel(" ") ask.Input2a= dialog.AddCheckbox("Pull",pull.act) ask.Input2x= dialog.AddSlider("CI with bands",pull.CI_default,0,1,2) ask.Input3a= dialog.AddCheckbox("Release (1-pass)",release.act) ask.Input3x= dialog.AddSlider("CI without bands",release.CI_default,0,1,2) ask.Input4a= dialog.AddCheckbox("Fuse(s) (multiple-pass)",fuse.act) ask.Label42= dialog.AddLabel("CI is varied") ask= ask_window(ask) puzzle.mutate.act= ask.Input1a.value pull.act= ask.Input2a.value pull.CI_default= ask.Input2x.value release.act= ask.Input3a.value release.CI_default= ask.Input3x.value fuse.act= ask.Input4a.value end -- if dialog 4 if true then -- dialog 5 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 5/7') local prim_min,prim_max if clu.band_to.val=="space" then prim_min= clu.rho.min prim_max= clu.rho.max else prim_min= clu.d_cng1.min prim_max= clu.d_cng1.max end -- if clu.band_to.val ask.Input10= dialog.AddLabel("Primary bands push distance by +/-") ask.Input1m= dialog.AddSlider("min",prim_min,0,3.8*5,2) ask.Input1n= dialog.AddSlider("max",prim_max,0,3.8*5,2) ask.Input1s= dialog.AddSlider("strength",clu.pull.str1,0,10,2) ask.Input14= dialog.AddLabel(" ") ask.Input20= dialog.AddLabel("Secondary bands push distance by +/-") ask.Input2a= dialog.AddCheckbox("on",clu.d_cng2.act) ask.Input2m= dialog.AddSlider("min",clu.d_cng2.min,0,3.8*5,2) ask.Input2n= dialog.AddSlider("max",clu.d_cng2.max,0,3.8*5,2) ask.Input2s= dialog.AddSlider("strength",clu.pull.str2,0,10,2) ask.Input25= dialog.AddLabel(" ") ask.Input30= dialog.AddLabel("Segs with this distance are 'too close'") ask.Input3m= dialog.AddSlider("min",clu.seg_min_dist,0,3.8*5,2) ask.Input32= dialog.AddLabel("if 'too close:") ask.Input33= dialog.AddLabel("neither apply band".. "\nnor change (correct) target seg idx") ask.Input3a= dialog.AddCheckbox("skip band / don't change t.seg",clu.band_skip_wrong) ask.Input35= dialog.AddLabel("if banding to many segs, possibly:") ask.Input36= dialog.AddLabel("- activate this or".. "\n- deactivate secondary bands") ask= ask_window(ask) if ask.Input1m.value>ask.Input1n.value then ask.Input1m.value,ask.Input1n.value= ask.Input1n.value,ask.Input1m.value end -- if ask.Input11,ask.Input12 if clu.band_to.val=="space" then clu.rho.min= ask.Input1m.value clu.rho.max= ask.Input1n.value else clu.d_cng1.min= ask.Input1m.value clu.d_cng1.max= ask.Input1n.value end -- if clu.band_to.val clu.pull.str1= ask.Input1s.value clu.d_cng2.act= ask.Input2a.value if ask.Input2m.value>ask.Input2n.value then ask.Input2m.value,ask.Input2n.value= ask.Input2n.value,ask.Input2m.value end -- if ask.Input22,ask.Input23 clu.d_cng2.min= ask.Input2m.value clu.d_cng2.max= ask.Input2n.value clu.pull.str2= ask.Input2s.value clu.seg_min_dist= ask.Input3m.value clu.band_skip_wrong= ask.Input3a.value end -- if dialog 5 if true then -- dialog 6 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 6/7') ask.Input10= dialog.AddLabel("Band genes and mutating ratios") ask.Input11= dialog.AddLabel(" ") ask.Input12= dialog.AddLabel("0.00= 0% (none)".. "\n0.50= 50% (half)".. "\n1.00= 100% (all)") ask.Input13= dialog.AddLabel(" ") ask.Input20= dialog.AddLabel("Turn this many") ask.Input2x= dialog.AddSlider("genes off",herd.mutate.prob.off,0,1,2) ask.Input22= dialog.AddLabel("(EO+GA)") ask.Input23= dialog.AddLabel(" ") ask.Input30= dialog.AddLabel("Probability of") ask.Input3x= dialog.AddSlider("gene mutating",herd.mutate.prob.vals,0,1,2) ask.Input31= dialog.AddLabel("after breeding (GA)") ask.Input32= dialog.AddLabel(" ") ask.Input40= dialog.AddLabel("Probability of") ask.Input4x= dialog.AddSlider("dad genes",herd.breed.dad_genes_prob,0,1,2) ask.Input42= dialog.AddLabel("(first breeding (GA) partner)") ask.Input44= dialog.AddLabel("Without mutating:") ask.Input45= dialog.AddLabel("1.00: kid is like dad".. "\n0.00: kid is like mom") ask.Input46= dialog.AddLabel(" ") ask.Input47= dialog.AddLabel(" ") local breed={mode={list=herd.breed.mode.list}} ask.Input50= dialog.AddLabel("Breeding behaviour (genetic roulette)") ask.Input5x= dialog.AddSlider("Breed mode",key_getbyval(breed.mode.list,herd.breed.mode.val),1,#breed.mode.list,0) -- ask.Input52= dialog.AddLabel(" ") ask.Label53= dialog.AddLabel(list_text(breed.mode.list)) ask.Label54= dialog.AddLabel("Each kid from:") ask.Label55= dialog.AddLabel("dual: 2 clus (mom & dad) (classic)".. "\npoly: any clus (whole herd)") ask.Label56= dialog.AddLabel(" ") ask.Input60= dialog.AddLabel("Roulette is") ask.Input6a= dialog.AddCheckbox("random",herd.breed.roulette.israndom) ask.Label62= dialog.AddLabel("true: spreaded".. "\nfalse: inertial (classic)") ask.Label63= dialog.AddLabel(" ") ask.Input70= dialog.AddLabel("Show breeding details") ask.Input7a= dialog.AddCheckbox("gene origin clu#(parent)",herd.breed.genes_show.act) ask.Input80= dialog.AddLabel("Show mutating details") ask.Input8a= dialog.AddCheckbox("gene value (old & new)",herd.mutate.genes_show.act) ask= ask_window(ask) herd.mutate.prob.off= ask.Input2x.value herd.mutate.prob.vals= ask.Input3x.value herd.breed.dad_genes_prob= ask.Input4x.value herd.breed.mode.val= breed.mode.list[ask.Input5x.value] herd.breed.roulette.israndom= ask.Input6a.value herd.breed.genes_show.act= ask.Input7a.value herd.mutate.genes_show.act= ask.Input8a.value end -- if dialog 6 if true then -- dialog 7 local ask= dialog.CreateDialog('CG303 GAB+EO Redux - Parameters 6/7') ask.Input10= dialog.AddLabel("If best clu gain is less than") ask.Input1m= dialog.AddSlider("min", herd.flush_gain_thr.thr, herd.flush_gain_thr.min, herd.flush_gain_thr.max, 1) ask.Input1a= dialog.AddCheckbox("flush herd completely",herd.flush_gain_thr.act) ask.Input13= dialog.AddLabel(" ") ask.Input20= dialog.AddLabel("When flushed more than this time") ask.Input2n= dialog.AddSlider("max", herd.flush_gain_reset.thr, herd.flush_gain_reset.min, herd.flush_gain_reset.max, 0) ask.Input2a= dialog.AddCheckbox("reset puzzle to script start state",herd.flush_gain_reset.act) ask= ask_window(ask) herd.flush_gain_thr.thr= ask.Input1m.value herd.flush_gain_thr.act= ask.Input1a.value herd.flush_gain_reset.thr= ask.Input2n.value herd.flush_gain_reset.act= ask.Input2a.value end -- if dialog 7 end -- if dialog_val_cng end -- function prms_cng function ask_window(ask) ask.Labelx1= dialog.AddLabel(" ") ask.Labelx2= dialog.AddLabel(" ") ask.OK= dialog.AddButton("OK", 1) ask.Cancel= dialog.AddButton("Cancel", 0) local button_val= dialog.Show(ask) assert((button_val==1),'Script canceled!') return ask end -- function ask_window function list_text(list) -- create output text with multiple lines from list local text= "" local i for i=1,#list do if text~="" then text= text.."\n" end -- if text text= text..tostring(i)..": "..list[i] end -- for i return text end -- function list_text -- declaration section: -- all global variables (mostly organized as table) can be set here. -- some of them here are used as default vals only -- and can be altered by gui in change section puzzle={seg_count= structure.GetCount(); random_seed=nil -- set random seed to this val -- if there is none, -- os.time()+recipe.GetrandomSeed() will be used } print('Puzzle has '..puzzle.seg_count..' segments.') puzzle.ismutable=puzzle_ismutable() -- check if there are mutable segments puzzle.mutate={act=(puzzle.ismutable or false)} -- set mutating puzzle activity to the same val -- (foldit-mutation, not equal to: mutate herd-cluster-bands) puzzle.isligand=puzzle_isligand() -- check if there is a ligand puzzle.seg_center={ val=nil; --[[ set which seg is considered as seg_center integer number between first and last segment: use this segment index as center segment puzzle.seg_count (same as last segment): use last segment index as center segment -- nil: get "start" or "current" center segment --]] get_mode="current" --[[ set when seg_center is to be fetched "start": only at script start use this when setting val to a constant number or banding to ligand "current" (or any other): each time before applying bands --]] } puzzle.seg_random={ -- get random seg from get_mode="all" -- "list": uselist segs -- "all": puzzle segs } -- create list containing following segs: seg_use={ include={ idx={1;puzzle.seg_count}; -- with idx number: 1st and last --[[ you can also set: -- ranges instead of single indices: -- idx={seg_blocks({{1;5}})}; includes segs 1-5 -- idx={seg_blocks({{1;5};{10;20}})}; includes segs 1-5 and 10-20 -- idx={seg_blocks({{1;puzzle.seg_count}})}; includes all segs -- mix single segs and ranges: -- idx=seg_use_list_append_all({1;puzzle.seg_count},seg_blocks({{10;20}}); includes 1st seg, last seg and 10-20 --]] selected={act=true;val=true}; -- if act==true, include: -- val: -- true: selected segs -- false: unselected segs frozen={act=false;backbone=true;sidechain=false}; -- if act==true: -- add frozen segs -- given in backbone and sidechain val ss={}; -- with secondary structures -- "L"=loop -- "H"=helix -- "E"=sheet ss_cngs=true; -- where secondary structure changes occur: yes aa={"g"} -- with amino acids -- here: -- include glycine -- example: -- aa={"f";"g"} -- include phenylalanine; glycine }; exclude={ idx={}; -- without idx number: none selected={act=false;val=false}; -- if act==true, exclude: -- val: -- true: selected segs -- false: unselected segs frozen={act=false;backbone=true;sidechain=false}; -- if act==true: -- remove frozen segs -- given in backbone and sidechain val -- note: -- if you turn this on (act==true) -- and set backbone to false and sidechain to false, -- all unfrozen segs will be removed! ss={}; -- without secondary structures -- "L"=loop -- "H"=helix -- "E"=sheet aa={} -- without amino acids -- example: -- aa={"a";"v";"p";"w"} -- exclude alanine; valine; proline; tryptophan }; append_all=false; -- append all segs to uselist -- false: append only segs which are not already in list -- (more to check when adding) -- true: append all segs (double segs will be removed later when sorting) -- (more to check when removing) remove_double=true; -- remove double segs from uselist after adding -- true: remove -- false: don't remove (may not work, depending on how bands are applied) sort_ascending=true; -- sort segs in uselist by idx -- true: lowest idx first -- false: highest idx first -- nil (or any other): don't sort } gen={max=math.huge} -- set number of maximum gens to test herd={ size=8; -- number of individuums -- won't grow anymore in this script version breed={first=3; -- first mediocre clu to be replaced by breeded one (GA) -- clus with lower idx will be kept as good ones last=5; -- last mediocre clu to be replaced by breeded one (GA) -- clus with higher idx will be replaced by new random ones (flushed) mode={ -- breed mode (if EO_mode==0) -- general breeding behaviour val="dual"; -- set breed mode list={ "dual"; -- breed kid from 2 partners only (regular) -- before breeding, select mom (& dad) once via roulette -- while breeding, select gene from mom or dad "poly" -- breed kid from whole herd (each gene can be from any clu) -- while breeding, select gene-donor for each gene via roulette -- ignore mom & dad options } }; genes_show={ -- show detailed gene information whenn breeding act=false -- show which gene is from which clu -- true: show clu#(parent) -- false: show possible parents only }; roulette={ -- roulette wheel selection dad=false; -- use for dad (1st breeding partner) -- true: use -- false: don't use (1st dad is clu#1. 2ns dad is clu#2 ...) mom=true; -- use for mom (2nd breeding partner) -- true: use -- false: don't use (mom: next clu# after dad) israndom=false -- 'wheel' is complete random -- true: yes, don't spin wheel - let it jump and ignore clu order -- special, makes breeding partner selection behave different. -- -> useful for "poly" breed mode, as genes will be more scattered. -- fitness is considered, nevertheless -- false: no, spin wheel - default, as it is usually done in GA. -- fitness is considered, but more inertially }; dad_genes_prob=0.55 -- set the (probability) ratio of dad genes (1st breeding partner): -- float val between 0 and 1 -- <=0 : mom genes only -- 0.25 : 1/4 dad -- .5 : 1/2 dad and mom -- 0.75 : 3/4 dad -- >=1 : dad genes only }; mutate={ -- mutate herd cluster bands after breeding -- (GA-mutation, not equal to: mutate puzzle-segs) act=true; -- boolean val -- true: mutate vals depending on prob.vals -- false: mutate off (don't mutate breeded bands) genes_show={ -- show detailed gene information whenn mutating act=false -- show which gene is from which clu -- true: show clu#(parent) -- false: show possible parents only }; prob={off=0.25;vals=0.1}; -- mutate probability ratios -- each: float val between 0 and 1 -- <=0 : never -- 0.5 : 1/2 of cases -- >=1 : always -- off ratio: turn bands off when generated -- this prevents too many act bands for 1 clu -- (as a clu loads the complete uselist) -- example: -- if you have 10 segments in uselist, -- and off==0.25, -- 1/4 of clu band genes (about 2...3 will be off) -- vals ratio: mutate band vals when breeded -- toggles (switches) band activity (activated) -- (off bands -> on and vice versa) -- changes band vand vals (len, rho, theta, phi) -- examples for vals: -- <=0.0 : all band genes will be changed (then you can set herd.mutate.act= false) -- ==0.1 : 1/10 of band genes -- ==0.5 : 1/2 of band genes -- >=1.0 : all band genes will be changed rng10e={min=-3;max=-1} -- mutate band float vals (len, rho, phi theta, not segs) -- by +/-10^(min...max) -- examples: -- min=-1;max=-1: change by +/-0.1 -- min=-2;max=-1: change by +/-0.01 -- min=-3;max=-3: change by +/-0.001 -- min=-3;max=-1: change by +/-0.001, +/-0.01 +/-or 0.1 }; EO_mode={ -- Extremal Optimization mode cur=0; -- integer val between 0 and 2 -- 0 : no EO - breed clus (regular GAB) (default: keep 2, breed 3, generate 3) -- 1 : soft EO - keep clus instead of breeding them (default: keep 5, generate 3) -- 2 : hard EO - flush clus instead of breeding them (default: keep 2, generate 6) min=0; -- for gui max=2 -- for gui }; flush_gain_thr={ -- flush herd (and load start state) -- if best gen's clu score gain is less than threshold act=true; -- boolean val -- true: check best gain -- false: don't check (and flush) thr=0.5; -- if score "gain" of best cluster is less than this thr -- can be negative, too min=-200; -- for gui max=200; -- for gui }; flush_gain_reset={ -- reset puzzle to script start state act=false; -- boolean val -- true: reset -- false: don't reset cur=0; -- flush counter thr=2; -- reset if flush counter exceeds this thr min=0; -- for gui max=20 -- for gui }; fitness_e=1; -- exponent for herd fitness "bending" -- allows worse clus to appear more often. -- tweaks distribution of fitness vals -- by passing them through this exponential function: -- fitness^(1/fitness_e) is a root function -- exponent vals <1 bend fitness vals towards 1 -- same is done with small random vals in R= rho^(1/3) calculating section -- value examples: fitness_e -> fitness val -- 1/1 (exponent 1): exponent won't have an effect (routine will will be skipped) -- 1/2 (square root): fitness 0.01 -> 0.1, then normalized -- 1/3 (cubic root): fitness 0.001 -> 0.1, then normalized -- ... etc. use={ state={ -- load state mode val="recent"; -- set which state will be loaded before each cluster pulling -- use one of these vals (also for gui): list={ "start"; -- initial state at script start -- use if you are really stuck or have a new puzzle -- never leads to local maximum "gen"; -- best result of last gen -- use to allow score dropping -- and to (hopefully) escape local maximum "script"; -- script's best (script-internal "recent best") -- what the script considers as best solution so far -- may not catch all states (as scores are gotten after single relax/fuse steps) -- may lead to local maximum "recent" -- recent best -- what the game considers as best solution so far -- catches really best states (also inbetween relax/fuse steps) -- these states may not be stable -- may lead to local maximum } }; hybrid={ -- hybrid state loading -- loads selected puzzle state given below, -- but "start" state each gens to reset puzzle -- useful if you are really stuck -- or just beginning state=nil; -- nil: don't use hybrid state loading -- "gen": best result of last gen -- "script": script's best -- "recent": recent best gens=10; -- load "start" state each this number of gens -- for all other gens, load state given above } } } clu={ fix={ -- here you can set some seg-pairs, which dsts should be fixed by a band -- example: -- {2;21}; -- {31;46}; -- fix seg2 with seg21 and -- seg31 with seg46 str=2; -- set str of fixing bands lencng=0; -- lencng=-3.8/4; -- add this val to measured dst between segs -- giving fixing band len -- use vals -- =0 : just keep dst of segs -- <0 : do some compression -- >1 : do some expansion }; pull={str1=1;str2=0.1}; -- set str of pulling bands -- str1: primary bands -- str2: secondary bands (used if d_cng2==act) band_to={ -- banding mode val="random2"; -- set where segs of uselist will be banded to -- use one of these vals (also for gui): list={ "space"; -- band to space (3D-Angles) "center"; -- band to seg: center (auto-detection) "user"; -- band to seg: center (user-defined in puzzle.seg_center.val) "furthest"; -- band to seg: furthest "random1"; -- band to seg: random in uselist "random2" -- band to seg: random in puzzle (classical behaviour) } }; rho={min=3.8/8;max=3.8}; -- if band_to.val=="space" (seg to "air") -- absolute random bandlen minimum and maximum d_cng1={min=3.8/8;max=3.8}; -- if band_to.val~="space" -- relative primary random bandlen minimum and maximum -- resulting band len: current seg dist+min..max d_cng2={act=true;min=0;max=3.8/8}; -- if act==true -- relative secondary random bandlen (uselist-seg to uselist-seg) -- resulting band len=current seg dist+min..max band_sys_lim={ -- band len allowed (by "system") min=3.8/2; -- you can also put 0 here, -- but then seg to seg bands can get 'too close' max=10000 -- as said on fold.it homepage }; seg_min_dist=3.8*1.25; -- minimum spatial dst for seg-banding -- (bandable segs detection thr based on dst) -- segs with dst less than this val are considered as 'too close' -- use this to prevent: -- connecting one segment to its index-neighbour, but allow closing cuts -- creating sharp edges in puzzle band_skip_wrong=false; -- set if "wrong bands" are corrected or rejected -- wrong bands: segs to be banded are too close -- true: reject actual band -- (you will have fewer bands, in worst case 0!) -- false: correct band target segment -- (will take nearest possible segment) } -- set which methods are performed when pulling: pull={act=true; CI_default=0.5; -- if no CI is given {CI=nil;cmd="w";itr=2;backbone=true;sidechains=false} -- CI=nil: don't change CI, use current (last) set value } -- set which methods are performed at each end of releasing and fusing: release_n_fuse_end={ -- no -- act -- CI_default -- because this list is appended only {CI=nil;cmd="m";itr=1}; -- CI=nil: don't change CI, use current (last) set value {CI=nil;cmd="w";itr=8;backbone=true; sidechains=true}; {CI=nil;cmd="m";itr=1}; {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=1;backbone=true; sidechains=true} } -- set which methods are performed when releasing: release={act=false; -- fast "fuse" (quickstab) activated? CI_default=1; -- if no CI is given {CI=nil;cmd="m";itr=1}; -- CI=nil: don't change CI, use current (last) set value {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; -- at the end, always the same: release_n_fuse_end[1]; release_n_fuse_end[2]; release_n_fuse_end[3]; release_n_fuse_end[4]; release_n_fuse_end[5] } -- set which methods are performed when fusing: -- currently there are 4 sequences of methods, -- but you can change their number (and length for each sequence) -- to do this, -- copy and paste a complete sequence (each begins with --fuse#x:) -- but take care of the braces! fuse={act=true; -- complex fuse activated? CI_default=0.3; -- if no CI is given --sequence start: --fuse#1: { {CI=0.1;cmd="m";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; -- CI=nil: don't change CI, use current (last) set value {CI=0.7;cmd="m";itr=1}; {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; {CI=1.0;cmd="s";itr=0}; -- itr=0: just set CI back to 1 -- at the end, always the same: release_n_fuse_end[1]; release_n_fuse_end[2]; release_n_fuse_end[3]; release_n_fuse_end[4]; release_n_fuse_end[5] }; --fuse#2: { {CI=0.3;cmd="m";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; -- CI=nil: don't change CI, use current (last) set value {CI=0.6;cmd="m";itr=1}; {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; {CI=1.0;cmd="s";itr=0}; -- itr=0: just set CI back to 1 release_n_fuse_end[1]; release_n_fuse_end[2]; release_n_fuse_end[3]; release_n_fuse_end[4]; release_n_fuse_end[5] }; --fuse#3: { {CI=0.2;cmd="m";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; -- CI=nil: don't change CI, use current (last) set value {CI=0.5;cmd="m";itr=1}; {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; {CI=1.0;cmd="s";itr=0}; -- itr=0: just set CI back to 1 release_n_fuse_end[1]; release_n_fuse_end[2]; release_n_fuse_end[3]; release_n_fuse_end[4]; release_n_fuse_end[5] }; --fuse#4: { {CI=0.4;cmd="m";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; -- CI=nil: don't change CI, use current (last) set value {CI=0.3;cmd="m";itr=1}; {CI=nil;cmd="s";itr=1}; {CI=nil;cmd="w";itr=4;backbone=true; sidechains=false}; {CI=1.0;cmd="s";itr=0}; -- itr=0: just set CI back to 1 release_n_fuse_end[1]; release_n_fuse_end[2]; release_n_fuse_end[3]; release_n_fuse_end[4]; release_n_fuse_end[5] } --sequence end: } -- for debugging before prm changes, uncomment this: -- prms_show() -- change section: -- call function to change some variables/tables via gui prms_cng() -- show section: -- for debugging after prm changes, uncomment this: -- prms_show() function prms_show() -- uncomment some of these function calls -- to show full respective table contents -- you can also use them to print out values, -- then copy & paste them back into script list_show_deep(puzzle,"puzzle") -- list_show_deep(seg_use,"seg_use") -- list_show_deep(gen,"gen") -- list_show_deep(herd,"herd") list_show_deep(clu,"clu") -- list_show_deep(release_n_fuse_end,"release_n_fuse_end") -- list_show_deep(release,"release") -- list_show_deep(fuse,"fuse") end -- function prms_show() -- launch section: -- call main program main()

Comments


Crashguard303 Lv 1

  1. introduction:
    1.1 inspired by:
    Cartoon Villain (GA idea & first random band script of this kind)
    Rav3n_pl (OO parameter/variable handling)
    jeff101 (EO & input-boxes idea)

    1.2 idea:
    – This is a further development of my last GAB+EO
    which i wrote from scratch to improve table handling and modularity
    (it was a work of more than one month!)

    -- this script doesn't simply band to random segs,
       it works with a list of "cardinal" segs.
       -- origin segs always stay the same (they are just turned off or on),
       -- target segs change
          -> this will avoid double banding
    -- new idea is to keep the number of random band parameters as small as they can be
       so it works with 2 fixed band strengths (as the length is already random)
    
    -- as i didn't want to overload the starting sequence
       with too many input boxes and selectable vals (more than 7 boxes at the moment!).
       -> you can set all vals (even the other) in the declaration section.
    

    1.3 features:
    – cardinal seg auto-detection: (see 3.3.2.3)
    – secondary-structure changes, amino-acids, etc will create a uselist
    ++ selecting segs via game interface (blue) is possible, too
    – 3 breeding modes: (see 3.2)
    GA
    EO1
    EO2
    – 2 gene modes: (see 3.2.2.2)
    "dual"
    "poly"
    – 6 banding modes (see 3.3)
    "space"
    "center"
    "user"
    "furthest"
    "random1"
    "random2"
    ++ further settings for each

    1.4 known bugs/problems:
    1.4.1 ligand puzzles:
    – are supported by script, but i couldn't test if it works properly!
    -> to band ligand:
    use band to "user", then slide to last segment!

    1.4.2 locked segs (grey in game):
          -- can't be detected at the moment :(
          -> work with a proper seg_use list.
             you can:
             -> pass seg indices as table in declaration section or
             -> make a selection via game's selection interface (blue)
                (although this wouldn't remove locked ones)
    

Crashguard303 Lv 1

1.: In next time, I will have some examinations, so I won't have the time do care much about script bugs, features, spelling mistakes (or other lingual issues, as I'm not a native English speaker)
I hope, the manual is complete in this way that it answers most questions.
Maybe some other scripter can help you if there is a minor problem.

2.: There are already many input-boxes for the most important values in this script.
I neither wanted to write a full-automatic banding-robot
(so I offered "a couple" of parameters changeable by a "frontend")
nor annoy somebody with tons of inputboxes,
but as long as we can't store files, we only can change settings via input dialog boxes or writing them directly into the script code (something for advanced coders).
We definitely use file access to store and load settings!

So it is up to you to choose which parameters you change and in which way,
as this is our human part in this game, the rest can do ROSETTA.
If you don't know how to change parameters, please leave them as they are!
I wrote the script this way that it should be stable if you don't mess up vital things.

3.: (no personal thing):
If you find useful puzzle-adapted settings or script changes, you are free to post them, but please don't just unlock some "hidden" features (e.g. by uncommenting print-functions used for debugging -> more text in output) and do as if you've reinvented the wheel.
A car doesn't go faster if you put some racing-stickers on it.
For scripts, let's concentrate on functionality, the beauty will be found in good puzzle solutions.