-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
12,487 additions
and
0 deletions.
There are no files selected for viewing
3,001 changes: 3,001 additions & 0 deletions
3,001
Chapter 7 - Money, sex and evolution/evolution/evolution.csv
Large diffs are not rendered by default.
Oops, something went wrong.
13 changes: 13 additions & 0 deletions
13
Chapter 7 - Money, sex and evolution/evolution/evolution.r
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
library(ggplot2) | ||
data <- read.table("evolution.csv", header=F, sep=",") | ||
colnames(data) <- c('population','metabolism','vision_range') | ||
pdf("evolution.pdf") | ||
time = 1:nrow(data) | ||
grid.newpage() | ||
pushViewport(viewport(layout=grid.layout(1,2))) | ||
vplayout <- function(x,y) {viewport(layout.pos.row=x, layout.pos.col=y)} | ||
p <- qplot(time, metabolism, data=data, geom=c("point", "smooth"), main="Evolution in metabolism") | ||
print(p, vp=vplayout(1,1)) | ||
p <- qplot(time, vision_range, data=data, geom=c("point", "smooth"), main="Evolution in vision range") | ||
print(p, vp=vplayout(1,2)) | ||
dev.off() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
class Food | ||
attr_reader :quantity, :position | ||
|
||
def initialize(slot, p) | ||
@position = p | ||
@slot = slot | ||
@quantity = rand(20) + 10 | ||
end | ||
|
||
def eat(much) | ||
@quantity -= much | ||
end | ||
|
||
def draw | ||
@slot.oval :left => @position[0], :top => @position[1], :radius => quantity, :center => true | ||
end | ||
|
||
def tick | ||
if @quantity <= 0 | ||
$food.delete self | ||
end | ||
draw | ||
end | ||
end |
27 changes: 27 additions & 0 deletions
27
Chapter 7 - Money, sex and evolution/evolution/parameters.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
FPS = 6 | ||
ROID_SIZE = 6 | ||
WORLD = {:xmax => ROID_SIZE * 100, :ymax => ROID_SIZE * 100} # boundary of the world | ||
POPULATION_SIZE = 50 | ||
FOOD_COUNT = 30 | ||
OBSTACLE_SIZE = 30 | ||
MAGIC_NUMBER = 10 # the number of roids it will monitor | ||
|
||
SEPARATION_RADIUS = ROID_SIZE * 2 # steer to avoid crowding of flockmates | ||
ALIGNMENT_RADIUS = ROID_SIZE * 35 # steer towards average heading of flockmates | ||
COHESION_RADIUS = ROID_SIZE * 35 # steer to move toward average position of flockmates | ||
|
||
SEPARATION_ADJUSTMENT = 10 # how far away should roids stay from each other (small further away) | ||
ALIGNMENT_ADJUSTMENT = 8 # how aligned are the roids with each other (smaller more aligned) | ||
COHESION_ADJUSTMENT = 100 # how cohesive the roids are with each other (smaller more cohesive) | ||
CENTER_RADIUS = ROID_SIZE * 10 # radius of how close to the center it stays | ||
MAX_ROID_SPEED = 20 | ||
|
||
END_OF_THE_WORLD = 3000 | ||
MAX_LIFESPAN = 100 | ||
MAX_ENERGY = 100 | ||
CHILDBEARING_AGE = 20..50 | ||
CHILDBEARING_ENERGY_LEVEL = 15 | ||
CHILDBEARING_ENERGY_SAP = 0.8 | ||
|
||
MAX_METABOLISM = 4.0 | ||
MAX_VISION_RANGE = ROID_SIZE * 10.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
class Roid | ||
attr_reader :velocity, :position, :energy, :uid, :sex, :lifespan, :age, :metabolism, :vision_range | ||
|
||
def initialize(slot, p, v, id) | ||
@velocity = v # assume v is a Vector with X velocity and Y velocity as elements | ||
@position = p # assume p is a Vector with X and Y as elements | ||
@slot = slot | ||
@energy = rand(MAX_ENERGY) | ||
@uid = id | ||
@sex = rand(2) == 1 ? :male : :female | ||
@lifespan = rand(MAX_LIFESPAN) | ||
@age = 0 | ||
@metabolism = rand(MAX_METABOLISM*10.0)/10.0 | ||
@vision_range = rand(MAX_VISION_RANGE*10.0)/10.0 | ||
end | ||
|
||
def distance_from(roid) | ||
distance_from_point(roid.position) | ||
end | ||
|
||
def distance_from_point(vector) | ||
x = self.position[0] - vector[0] | ||
y = self.position[1] - vector[1] | ||
Math.sqrt(x*x + y*y) | ||
end | ||
|
||
def nearby?(threshold, roid) | ||
return false if roid === self | ||
(distance_from(roid) < threshold) and within_fov?(roid) | ||
end | ||
|
||
def within_fov?(roid) | ||
v1 = self.velocity - self.position | ||
v2 = roid.position - self.position | ||
cos_angle = v1.inner_product(v2)/(v1.r*v2.r) | ||
Math.acos(cos_angle) < 0.75 * Math::PI | ||
end | ||
|
||
def draw | ||
size = ROID_SIZE * @energy.to_f/50.0 | ||
size = 10 if size > 10 | ||
o = @slot.oval :left => @position[0], :top => @position[1], :radius => size, :center => true | ||
o.fill = @slot.lightblue if @sex == :male | ||
@slot.line @position[0], @position[1], @position[0] - @velocity[0], @position[1] - @velocity[1] | ||
end | ||
|
||
def move | ||
@delta = Vector[0,0] | ||
%w(separate align cohere muffle hungry).each do |action| | ||
self.send action | ||
end | ||
@velocity += @delta | ||
@position += @velocity | ||
fallthrough and draw | ||
end | ||
|
||
def separate | ||
distance = Vector[0,0] | ||
r = $roids.sort {|a,b| self.distance_from(a) <=> self.distance_from(b)} | ||
roids = r.first(MAGIC_NUMBER) | ||
roids.each do |roid| | ||
if nearby?(SEPARATION_RADIUS, roid) | ||
distance += self.position - roid.position | ||
end | ||
end | ||
@delta += distance | ||
end | ||
|
||
# roids should look out for roids near it and then fly towards the center of where the rest are flying | ||
def align | ||
alignment = Vector[0,0] | ||
r = $roids.sort {|a,b| self.distance_from(a) <=> self.distance_from(b)} | ||
roids = r.first(MAGIC_NUMBER) | ||
roids.each do |roid| | ||
alignment += roid.velocity | ||
end | ||
alignment /= MAGIC_NUMBER | ||
@delta += alignment/ALIGNMENT_ADJUSTMENT | ||
end | ||
|
||
# roids should stick to each other | ||
def cohere | ||
average_position = Vector[0,0] | ||
r = $roids.sort {|a,b| self.distance_from(a) <=> self.distance_from(b)} | ||
roids = r.first(MAGIC_NUMBER) | ||
roids.each do |roid| | ||
average_position += roid.position | ||
end | ||
average_position /= MAGIC_NUMBER | ||
@delta += (average_position - @position)/COHESION_ADJUSTMENT | ||
end | ||
|
||
# get the roids to move around the center of the displayed world | ||
def center | ||
@delta -= (@position - Vector[WORLD[:xmax]/2, WORLD[:ymax]/2]) / CENTER_RADIUS | ||
end | ||
|
||
# muffle the speed of the roid to dampen the swing | ||
# swing causes the roid to move too quickly out of range to be affected by the rules | ||
def muffle | ||
if @velocity.r > MAX_ROID_SPEED | ||
@velocity /= @velocity.r | ||
@velocity *= MAX_ROID_SPEED | ||
end | ||
end | ||
|
||
def center | ||
@delta -= (@position - Vector[WORLD[:xmax]/2, WORLD[:ymax]/2]) / CENTER_RADIUS | ||
end | ||
|
||
def fallthrough | ||
x = case | ||
when @position[0] < 0 then WORLD[:xmax] + @position[0] | ||
when @position[0] > WORLD[:xmax] then WORLD[:xmax] - @position[0] | ||
else @position[0] | ||
end | ||
y = case | ||
when @position[1] < 0 then WORLD[:ymax] + @position[1] | ||
when @position[1] > WORLD[:ymax] then WORLD[:ymax] - @position[1] | ||
else @position[1] | ||
end | ||
|
||
@position = Vector[x,y] | ||
end | ||
|
||
# get attracted to food | ||
def hungry | ||
$food.each do |food| | ||
if distance_from_point(food.position) < (food.quantity + @vision_range) | ||
@delta -= self.position - food.position | ||
end | ||
if distance_from_point(food.position) <= food.quantity + 15 | ||
eat food | ||
end | ||
end | ||
end | ||
|
||
def reduce_energy_from_childbirth | ||
@energy = @energy * CHILDBEARING_ENERGY_SAP | ||
end | ||
|
||
# consume the food and replenish energy with it | ||
def eat(food) | ||
food.eat 1 | ||
@energy += @metabolism | ||
end | ||
|
||
# lose energy at every tick | ||
def lose_energy | ||
@energy -= 1 | ||
end | ||
|
||
def grow_older | ||
@age += 1 | ||
end | ||
|
||
def inherit(crossover) | ||
@metabolism = crossover[0] | ||
@vision_range = crossover[1] | ||
end | ||
|
||
def procreate | ||
if attractive and @sex == :female | ||
# check for potential nearby mates | ||
r = $roids.sort {|a,b| self.distance_from(a) <=> self.distance_from(b)} | ||
roids = r.first(MAGIC_NUMBER) | ||
roids.each do |roid| | ||
if roid.attractive and roid.sex == :male | ||
baby = Roid.new(@slot, @position, @velocity, 1001) | ||
crossovers = [[@metabolism, @vision_range], | ||
[@metabolism,roid.vision_range], | ||
[roid.metabolism, @vision_range], | ||
[roid.metabolism,roid.vision_range]] | ||
baby.inherit crossovers[rand(4)] | ||
$roids << baby | ||
reduce_energy_from_childbirth | ||
roid.reduce_energy_from_childbirth | ||
end | ||
|
||
end | ||
end | ||
end | ||
|
||
def attractive | ||
CHILDBEARING_AGE.include? @age and @energy > CHILDBEARING_ENERGY_LEVEL | ||
end | ||
|
||
# called at every tick of time | ||
def tick | ||
move | ||
lose_energy | ||
grow_older | ||
procreate | ||
if @energy <= 0 or @age > @lifespan | ||
$roids.delete self | ||
end | ||
end | ||
|
||
end |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
require 'matrix' | ||
require 'csv' | ||
require 'enc/trans/transdb' | ||
require './parameters' | ||
require './roid' | ||
require './food' | ||
|
||
class Vector | ||
def /(x) | ||
if (x != 0) | ||
Vector[self[0]/x.to_f,self[1]/x.to_f] | ||
else | ||
self | ||
end | ||
end | ||
end | ||
|
||
def random_location | ||
Vector[rand(WORLD[:xmax]),rand(WORLD[:ymax])] | ||
end | ||
|
||
def populate | ||
POPULATION_SIZE.times do |i| | ||
random_velocity = Vector[rand(11)-5,rand(11)-5] | ||
$roids << Roid.new(self, random_location, random_velocity, i) | ||
end | ||
end | ||
|
||
def scatter_food | ||
FOOD_COUNT.times do | ||
$food << Food.new(self, random_location) | ||
end | ||
end | ||
|
||
def randomly_scatter_food(probability) | ||
if (0..probability).include?(rand(100)) | ||
$food << Food.new(self, random_location) | ||
end | ||
end | ||
|
||
def write(data) | ||
CSV.open('data.csv', 'w') do |csv| | ||
data.each do |row| | ||
csv << row | ||
end | ||
end | ||
end | ||
|
||
Shoes.app(:title => 'Utopia', :width => WORLD[:xmax], :height => WORLD[:ymax]) do | ||
background ghostwhite | ||
stroke slategray | ||
|
||
$roids = [] | ||
$food = [] | ||
data = [] | ||
populate | ||
scatter_food | ||
|
||
time = END_OF_THE_WORLD | ||
|
||
animate(FPS) do | ||
randomly_scatter_food 30 | ||
clear do | ||
fill yellowgreen | ||
$food.each do |food| food.tick; end | ||
fill gainsboro | ||
$roids.each do |roid| | ||
roid.tick | ||
end | ||
mean_metabolism = $roids.inject(0.0){ |sum, el| sum + el.metabolism}.to_f / $roids.size | ||
mean_vision_range = $roids.inject(0.0){ |sum, el| sum + el.vision_range}.to_f / $roids.size | ||
data << [$roids.size, mean_metabolism.round(2), mean_vision_range.round(2)] | ||
para "countdown: #{time}" | ||
para "population: #{$roids.size}" | ||
para "metabolism: #{mean_metabolism.round(2)}" | ||
para "vision range: #{mean_vision_range.round(2)}" | ||
end | ||
|
||
time -= 1 | ||
close & write(data) if time < 0 or $roids.size <= 0 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
class Food | ||
attr_reader :quantity, :position | ||
|
||
def initialize(slot, p) | ||
@position = p | ||
@slot = slot | ||
@quantity = rand(20) + 10 | ||
end | ||
|
||
def eat(much) | ||
@quantity -= much | ||
end | ||
|
||
def draw | ||
@slot.oval :left => @position[0], :top => @position[1], :radius => quantity, :center => true | ||
end | ||
|
||
def tick | ||
if @quantity <= 0 | ||
$food.delete self | ||
end | ||
draw | ||
end | ||
end |
Oops, something went wrong.