examples/robot.rb in ga-0.1.1 vs examples/robot.rb in ga-0.1.2
- old
+ new
@@ -1,229 +1,118 @@
-require 'bundler/setup'
require 'ga'
-
+require 'pry'
require 'parallel'
+require 'ffi'
-MAP_SIZE = 7
+system 'make sharedlib'
+# env:
+# '12001'
+# 1
+# 2 3 4
+# 5
-# 0 empty 1 dust 2 wall
-# [x][y]
-def new_map
- MAP_SIZE.times.map { [0] * MAP_SIZE }
-end
+# actions:
+# 1
+# 2 3
+# 4
+# 1 2 3 4 move
+# 0 rand move
+# 5 clean
+ACTIONS = [0, 1, 2, 3, 4, 5]
-def rand_map(map, dust_rate = 0.5)
- map.each do |cols|
- cols.length.times {|i| cols[i] = rand() <= dust_rate ? 1 : 0 }
- end
-end
-
-def show_map(map, bx = nil, by = nil)
- puts('-' * 20)
- MAP_SIZE.times do |y|
- MAP_SIZE.times do |x|
- if bx == x and by == y then
- print(map[x][y], '* ')
- else
- print(map[x][y], ' ')
- end
- end
- print("\n")
- end
- puts('-' * 20)
-end
-
-
-# 0 random move
-# 9 clear
-# 1 2 3
-# 4 5
-# 6 7 8
-ACTIONS_DATA = {
- 0 => nil,
- 9 => nil,
-
- # 1 => [-1, -1],
- 2 => [0, -1],
- # 3 => [1, -1],
-
- 4 => [-1, 0],
- # 5 => [1, 0],
-
- 6 => [-1, 1],
- # 7 => [0, 1],
- 8 => [1, 1]
-}
-
-ACTIONS = ACTIONS_DATA.keys
-MOVE_ACTIONS = [2, 4, 6, 8]
-
class Robot
include GA
- # genome
- #
# {env => action}
- #
- # env: '10122'
- # 1
- # 2 3 4
- # 5
- # action:
-
attr_accessor :genome, :fitness
- TOTAL_VALUE_TEST = 150
+ TOTAL_TEST_TIMES = 100
def self.random_new
- self.new({})
+ genome = {}
+ self.new(genome)
end
def initialize(genome)
@genome = genome.dup
end
def fitness
- tester = RobotTester.new(self)
- @fitness ||= TOTAL_VALUE_TEST.times.map { tester.test }.reduce(&:+) / TOTAL_VALUE_TEST
+ @fitness ||= RobotTester.test(TOTAL_TEST_TIMES, 200, self)
end
- def <=>(target)
- fitness <=> target.fitness
- end
-
def analyse_env(env)
- @genome[env] ||= if env[2] == '1' then
- 9
- elsif env == '00000' then
- 0
- else
- MOVE_ACTIONS.sample
- end
+ @genome[env] ||= ACTIONS.sample
end
def cross!(target)
all_genome = (genome.keys + target.genome.keys).uniq
len = all_genome.length
- min_robot = [self, target].min
- rand(len).times do
+ (len / 4 + rand(len / 4)).times do
gene = all_genome[rand(len)]
-
- if genome[gene] == target.genome[gene] and rand() < 0.3 then
- min_robot.genome[gene] = ACTIONS.sample
- else
- genome[gene], target.genome[gene] = target.genome[gene], genome[gene]
- end
+ genome[gene] ||= ACTIONS.sample
+ target.genome[gene] ||= ACTIONS.sample
+ genome[gene], target.genome[gene] = target.genome[gene], genome[gene]
end
end
def mutate!
all_genome = genome.keys
len = all_genome.length
- (rand(len) + 1).times do
+ (len / 4 + rand(len / 2)).times do
gene = all_genome[rand(len)]
- genome[gene] = ACTIONS.sample
+ genome[gene] = (ACTIONS - [genome[gene]]).sample
end
end
end
-class RobotTester
- attr_reader :map, :robot
+module RobotTester
+ extend FFI::Library
- def initialize(robot)
- @map = new_map
- @robot = robot
- end
+ ffi_lib File.expand_path('../librt.so', __FILE__)
+ callback :analyse_cb, [:string], :int
+ attach_function :robot_test, [:int, :int, :pointer, :pointer, :int, :analyse_cb], :int
- def test(step = 70, show = false)
- rand_map(map)
- x = rand(map.length)
- y = rand(map.length)
- @total_value = 0
+ def self.test(times, step, robot)
+ genome = robot.genome
+ len = genome.length
+ env_pointer = FFI::MemoryPointer.new(:pointer, len)
+ action_pointer = FFI::MemoryPointer.new(:int, len)
- step.times do
- env = scan_env(x, y)
- action = robot.analyse_env(env)
- rx, ry = execute_action(x, y, action)
+ eps = genome.keys.map {|k| FFI::MemoryPointer.from_string(k) }
+ env_pointer.write_array_of_pointer eps
+ action_pointer.write_array_of_int genome.values
- x += rx
- y += ry
-
- if x < 0 or y < 0 or x >= MAP_SIZE or y >= MAP_SIZE then
- x -= rx
- y -= ry
- @total_value -= 10
- elsif map[x][y] == 1
- @total_value += 1
- end
-
- if show then
- show_map(map, x, y)
- sleep 0.2
- end
- end
-
- @total_value
+ robot_test(
+ times, step,
+ env_pointer, action_pointer, len,
+ robot.method(:analyse_env)
+ )
end
-
- def execute_action(x, y, action)
- case action
- when 9 then
- if map[x][y] == 1 then
- map[x][y] = 0
- @total_value += 10
- else
- @total_value -= 10
- end
-
- [0, 0]
- when 0 then
- @total_value -= 1
- ACTIONS_DATA[MOVE_ACTIONS.sample]
- else
- @total_value -= 1
- ACTIONS_DATA[action]
- end
- end
-
- SCAN_COORDS = [
- [0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]
- ]
- def scan_env(x, y)
- SCAN_COORDS.map do |sx, sy|
- rx = x + sx
- ry = y + sy
-
- if rx < 0 || ry < 0 || rx >= MAP_SIZE || ry >= MAP_SIZE then
- 2
- else
- map[rx][ry]
- end
- end.join
- end
end
-
ga_zoo = Robot.new_ga_zoo
ga_zoo.debug!
+# ga_zoo.cataclysm(10, 1)
-ga_zoo.before_init_fitness do |units|
+ga_zoo.before_init_fitness do |units, generation|
vs = Parallel.map(units, in_processes: 8) do |unit|
[unit.fitness, unit.genome]
end
units.each_with_index do |unit, index|
unit.fitness, unit.genome = vs[index]
end
end
-robots = ga_zoo.evolve(256, 300)
+srand(Time.now.to_i)
+robots = ga_zoo.evolve(200, 1000, 0.9, 0.2)
robot = robots.max
-RobotTester.new(robot).test(100, true)
-
puts "========= result ============="
puts "fitness: #{robot.fitness}"
puts robot.genome
-
+puts "score: %i" % RobotTester.test(1, 200, robot)
+binding.pry
+puts 'end'