Skip to content

Instantly share code, notes, and snippets.

@bingomanatee
Created May 19, 2014 17:01
Show Gist options
  • Save bingomanatee/b2e92f239a36ecb478ce to your computer and use it in GitHub Desktop.
Save bingomanatee/b2e92f239a36ecb478ce to your computer and use it in GitHub Desktop.
Wolves and Goats
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