This depends on vim-rng for the random number stuff.
let s:Chromosome = {}
function! s:Chromosome.New(...)
let chromosome = copy(self)
let chromosome.code = ''
let chromosome.cost = 9999
let chromosome.pivot = 0
if a:0
let chromosome.code = a:1
let chromosome.pivot = (strchars(chromosome.code) / 2) - 1
endif
return chromosome
endfunction
function! s:Chromosome.random(length)
let self.code = RandomString(a:length)
let self.pivot = (a:length / 2) - 1
return self
endfunction
function! s:Chromosome.mutate(chance)
if (RandomNumber(100) / 100.0) < a:chance
let index = RandomNumber(1, strchars(self.code)) - 1
let upOrDown = RandomNumber(100) <= 50 ? -1 : 1
let exploded = split(self.code, '\zs')
let change = nr2char(char2nr(exploded[index]) + upOrDown)
if index == 0
let self.code = change . join(exploded[index+1:], '')
else
let self.code = join(exploded[0:index-1], '') . change . join(exploded[index+1:], '')
endif
endif
return self
endfunction
function! s:Chromosome.mate(chromosome)
let child1 = strpart(self.code, 0, self.pivot) . strpart(a:chromosome.code, self.pivot)
let child2 = strpart(a:chromosome.code, 0, self.pivot) . strpart(self.code, self.pivot)
return [s:Chromosome.New(child1), s:Chromosome.New(child2)]
endfunction
function! s:Chromosome.calcCost(compareTo)
let total = 0
let i = 0
while i < strchars(self.code)
let diff = char2nr(self.code[i]) - char2nr(a:compareTo[i])
let total += diff * diff
let i += 1
endwhile
let self.cost = total
return self
endfunction
function! s:Chromosome.to_s()
return self.code . ' (' . string(self.cost) . ')'
endfunction
let s:Population = {}
function! s:Population.New(goal, size)
let population = copy(self)
let population.members = []
let population.goal = a:goal
let population.generationNumber = 0
let population.solved = 0
let size = a:size
let length = strchars(population.goal)
while size > 0
let chromosome = s:Chromosome.New()
call chromosome.random(length)
call add(population.members, chromosome)
let size -= 1
endwhile
return population
endfunction
function! s:Population.display()
% delete
call setline(1, "Generation: " . self.generationNumber)
call setline(2, map(copy(self.members), 'v:val.to_s()'))
redraw
return self
endfunction
function! s:Population.costly(a, b)
return float2nr(a:a.cost - a:b.cost)
endfunction
function! s:Population.sort()
call sort(self.members, self.costly, self)
endfunction
function! s:Population.generation()
call map(self.members, 'v:val.calcCost(self.goal)')
call self.sort()
call self.display()
let children = self.members[0].mate(self.members[1])
let self.members = extend(self.members[0:-3], children)
let i = 0
while i < len(self.members)
call self.members[i].mutate(0.5)
call self.members[i].calcCost(self.goal)
if self.members[i].code == self.goal
call self.sort()
call self.display()
let self.solved = 1
break
endif
let i += 1
endwhile
let self.generationNumber += 1
return self
endfunction
enew
let population = s:Population.New('Hello, world!', 20)
while population.solved != 1
call population.generation()
endwhile
To see it run save it in a file and type :so % from within vim.
Why, dear bairui, you ask? Well, at this stage... I don't know. It just looked like fun. However, a couple of wild thoughts occurred to me: finding the ideal (good enough; as in 'correct' enough) combination of various vim options to achieve a desired look and behaviour. Take for example the various C indenting styles - what mad combination of &cinoptions, &cinkeys and &cinwords would you need to achieve Frankenstein's Indentation Style? What about getting &formatlistpat right for your preferred markup style? Sure, these might be totally hair-brained ideas -- but they might give you an idea for something less hairy and actually useful. Either way, I plan to keep playing with Burak's tutorial as he progresses through it. Thanks, Burak! :-)
No comments:
Post a Comment