Created
May 19, 2014 17:01
-
-
Save bingomanatee/b2e92f239a36ecb478ce to your computer and use it in GitHub Desktop.
Wolves and Goats
This file contains hidden or 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
var Fools = require('./../fools'); | |
var _ = require('lodash'); | |
var util = require('util'); | |
function scenario(ratio, animalCount, mapSize, grassLength, regrowthRate) { | |
var alert_level = 1; | |
var grass = _.map(_.range(0, mapSize), function () { | |
return grassLength; | |
}); | |
var state = _.template('ANIMAL <%= name %>(<%= type %>): <% if (alive){%> hunger: <%= hunger %> <%} else {%> DEAD: <%= note %> <% } %>'); | |
function Animal(name, animalType) { | |
var animal = { | |
name: name, | |
hunger: 0, | |
fatigue: 0, | |
male: true, | |
location: Math.floor(Math.random() * grass.length), | |
awake: true, | |
alive: true, | |
type: animalType, | |
note: '' | |
}; | |
animal.status = function () { | |
return state(animal); | |
}; | |
return animal; | |
} | |
var animals = _.map(_.range(0, animalCount), function (i) { | |
var animalType = i / animalCount > ratio ? 'wolf' : 'goat'; | |
return Animal(animalType + '_' + i, animalType); | |
}); | |
var goats = _.filter(animals, function (a) { | |
return a.type == 'goat'; | |
}); | |
/** tests **/ | |
function is_sleeping(goat) { | |
return !goat.awake; | |
} | |
function is_fatigued(animal) { | |
return animal.fatigue > 5 && animal.hunger < 3; | |
} | |
function is_hungry(animal) { | |
switch (animal.type) { | |
case 'goat': | |
return animal.hunger > 1; | |
break; | |
case 'wolf': | |
return animal.hunger > 3; | |
break; | |
} | |
} | |
function can_eat(animal) { | |
switch (animal.type) { | |
case 'goat': | |
return grass[animal.location] > 0; | |
break; | |
case 'wolf': | |
return prey(animal); | |
break; | |
default: | |
return false; | |
} | |
} | |
function is_starved(goat) { | |
return goat.hunger > 10; | |
} | |
function is_dead(goat) { | |
return !goat.alive; | |
} | |
/** activity **/ | |
function live_goats() { | |
return _.filter(goats, 'alive'); | |
} | |
function prey(wolf) { | |
var nearGoats = Fools.pairs(function (w, g) { | |
return w.location == g.location; | |
}, true)([wolf], live_goats())[0]; | |
if (nearGoats) { | |
var prey = nearGoats[1]; | |
return _.first(_.shuffle(prey)); | |
} else { | |
return false; | |
} | |
} | |
function do_nothing(goat) { | |
if (goat.alive) { | |
if (alert_level > 3) { | |
console.log('-- %s %s is idle', goat.type, goat.name); | |
} | |
} | |
return goat; | |
} | |
function rest(goat) { | |
goat.awake = false; | |
if (alert_level > 2) { | |
console.log('-- %s %s is resting', goat.type, goat.name); | |
} | |
goat.fatigue--; | |
if (goat.fatigue <= 0) { | |
if (alert_level > 1) { | |
console.log('-- %s %s woke up', goat.type, goat.name); | |
} | |
goat.fatigue = 0; | |
goat.awake = true; | |
} | |
return goat; | |
} | |
function wander(animal) { | |
if (animal.location <= 0) { | |
animal.location = 1; | |
} else if (animal.location >= grass.length) { | |
animal.location = grass.length - 1; | |
} else if (Math.random() >= 0.5) { | |
++animal.location; | |
} else { | |
--animal.location; | |
} | |
++animal.hunger; | |
animal.fatigue += 0.5; | |
return animal; | |
} | |
function move(animal) { | |
switch (animal.type) { | |
case 'goat': | |
wander(animal); | |
if (alert_level > 1) { | |
console.log('-- %s ...... moved', animal.status()); | |
} | |
break; | |
case 'wolf': | |
var p = prey(animal); | |
if (p) { | |
// stay put | |
} else { | |
var back = {location: animal.location - 1}; | |
p = prey(back); | |
if (p) { | |
animal.location--; | |
} else { | |
var forward = {location: animal.location + 1}; | |
p = prey(forward); | |
if (forward) { | |
animal.locatioin++; | |
} | |
} | |
} | |
if (p) { | |
if (alert_level > 1) { | |
console.log('%s STALKING %s', animal.status(), p.status()); | |
} | |
} else { | |
if (alert_level > 2) { | |
console.log('%s WANDERING ', animal.status()); | |
} | |
wander(animal); | |
} | |
break; | |
} | |
return animal; | |
} | |
function eat(animal) { | |
switch (animal.type) { | |
case 'goat': | |
var grass_value = grass[animal.location]; | |
if (grass_value <= 0) { | |
return; | |
} else if (grass_value < 1) { | |
animal.hunger -= grass_value; | |
grass[animal.location] = 0; | |
} else { | |
--grass[animal.location]; | |
--animal.hunger; | |
} | |
if (alert_level > 2) { | |
console.log('%s %s ate; hunger is now %s', animal.type, animal.name, animal.hunger); | |
} | |
break; | |
case 'wolf': | |
var my_prey = prey(animal); | |
if (my_prey) { | |
animal.hunger -= Math.ceil(10 / (1 + my_prey.hunger)); | |
my_prey.alive = false; | |
my_prey.note = 'eaten by' + animal.name; | |
if (alert_level > 0) { | |
console.log('!!!!!! %s ATE %s', animal.status(), my_prey.status()); | |
} | |
} | |
break; | |
} | |
return animal; | |
} | |
function starveToDeath(animal) { | |
if (alert_level > 1) { | |
console.log('!!!! %s starved to death!', animal.status()); | |
} | |
animal.alive = false; | |
animal.note = 'starved to death'; | |
return animal; | |
} | |
/** turn processes */ | |
var goat_action = Fools.fork(is_dead) | |
.then(do_nothing) | |
.else(Fools.fork(is_starved) | |
.then(starveToDeath) | |
.else(Fools.fork(is_sleeping) | |
.then(rest) | |
.else(Fools.fork(is_fatigued) | |
.then(rest) | |
.else(Fools.fork(is_hungry) | |
.then(Fools.fork(can_eat) | |
.then(eat) | |
.else(function (animal) { | |
if (alert_level > 2) { | |
console.log('animal %s cannot eat -- looks for food', animal.status()); | |
} | |
move(animal); | |
}) | |
).else(do_nothing) | |
) | |
) | |
) | |
); | |
var animal_turn = function (goat) { | |
Fools.pipe(goat_action, function (goat) { | |
/* if (goat.alive){ | |
console.log('goat %s: awake: %s, fatigue: %s, hunger: %s, location: %s', | |
goat.name, goat.awake, goat.fatigue, goat.hunger, goat.location) | |
} else { | |
console.log('goat %s: xxxxxxxx', goat.name); | |
}*/ | |
})(goat); | |
}; | |
function growGrass() { | |
grass = _.map(grass, function (length) { | |
if (length >= grassLength) { | |
return grassLength; | |
} | |
if (Math.random() < regrowthRate) { | |
return ++length; | |
} else { | |
return length; | |
} | |
}) | |
} | |
var turns = 0; | |
while (_.find(animals, function (a) { | |
return a.alive | |
}) && ++turns < 200) { | |
if (alert_level > 1) { | |
console.log('_________ TURN: %s ______________', turns); | |
} | |
_.each(animals, animal_turn); | |
_.each(animals, function (animal) { | |
animal.hunger += 0.5; | |
}); | |
growGrass(); | |
if (alert_level > 2) { | |
console.log('grass: %s', grass.join(' ')); | |
} | |
} | |
_.each(animals, function (animal) { | |
if (alert_level > 0) { | |
console.log('animal state: %s', animal.status()); | |
} | |
}); | |
return [turns, _.filter(animals, function (animal) { | |
return animal.alive; | |
})]; | |
} | |
var last_length = 0; | |
var animalCount = 10; | |
var grassSize = 8; | |
var growthRate = 0.125; | |
_.each([4, 6, 10, 20], function (mapSize) { | |
_.each(_.range(0, 10), function (range) { | |
var ar = range / 10; | |
console.log('animal ratio: %s', ar); | |
var result = scenario(ar, animalCount, mapSize, grassSize, growthRate); | |
console.log('map size: %s, duration: %s, of %s starting animals, %s survived', mapSize, result[0], animalCount, result[1].length); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment