開発環境
- OS X El Capitan - Apple (OS)
- Emacs (Text Editor)
- JavaScript (プログラミング言語)
- kjs-array (JavaScript Library)
- Safari(Web browser)
Eloquent JavaScript(Marijn Haverbeke 著、No Starch Press)のPart 1(Language)、Chapter 7(Project: Electronic Life)、Exercises(Predators)を取り組んでみる。
Exercises(Predators)
コード(Emacs)
HTML5
<pre>
<div id="output0"></div>
</pre>
<label for="speed0">speed: </label>
<input id="speed0" type="number" min="1" step="1" value="500">
<script src="array.js"></script>
<script src="sample1.js"></script>
JavaScript
(function () {
'use strict';
var vector,
grid,
directions,
elementFromChar,
charFromElement,
directionNames,
world,
wall,
lifelikeWorld,
view,
valley,
i,
plan,
dirPlus,
wallFollower,
actionTypes,
plant,
plantCount = 0,
plantEater,
smartPlantEater,
king,
div_output = document.querySelector('#output0'),
input_speed = document.querySelector('#speed0'),
intervalID;
vector = function (x, y) {
var that;
that = {
x: x,
y: y,
plus: function (other) {
return vector(x + other.x, y + other.y);
},
};
return that;
};
grid = function (width, height) {
var that,
space = new Array(width * height);
that = {
get width() { return width; },
get height() { return height;},
get space() { return space; },
isInside: function(vector) {
return vector.x >= 0 && vector.x < width &&
vector.y >= 0 && vector.y < height;
},
get: function (vector) {
return space[vector.x + width * vector.y];
},
set: function (vector, value) {
space[vector.x + width * vector.y] = value;
},
forEach: function (f, context) {
var x,
y,
y_max = height,
x_max = width,
value;
for (y = 0; y < height; y += 1) {
for (x = 0; x < width; x += 1) {
value = space[x + y * width];
if (value !== null) {
f(value, vector(x, y));
}
}
}
},
};
return that;
};
directions = {
n: vector(0, -1),
ne: vector(1, -1),
e: vector(1, 0),
se: vector(1, 1),
s: vector(0, 1),
sw: vector(-1, 1),
w: vector(-1, 0),
nw: vector(-1, -1),
};
directionNames = Object.keys(directions);
elementFromChar = function (legend, ch) {
var elem;
if (ch === ' ') {
return null;
}
elem = legend[ch]();
elem.originChar = ch;
return elem;
};
charFromElement = function (elem) {
if (elem === null) {
return ' ';
}
return elem.originChar;
};
world = function (map, legend) {
var that,
grid0 = grid(map[0].length, map.length),
letAct,
turn,
checkDestination;
map.forEach(function (line, y) {
var x,
x_max = line.length;
for (x = 0; x < x_max; x += 1) {
grid0.set(vector(x, y), elementFromChar(legend, line[x]));
}
});
checkDestination = function (action, vector) {
var dest;
if (directions.hasOwnProperty(action.direction)) {
dest = vector.plus(directions[action.direction]);
if (grid0.isInside(dest)) {
return dest;
}
}
};
letAct = function (critter, vector) {
var action = critter.act(view(that, vector)),
dest;
if (action && action.type === 'move') {
dest = checkDestination(action, vector);
if (dest && grid0.get(dest) === null) {
grid0.set(vector, null);
grid0.set(dest, critter);
}
}
};
turn = function () {
var acted = [];
grid0.forEach(function (critter, vector) {
if (critter.act && acted.indexOf(critter) === -1) {
acted.push(critter);
that.letAct(critter, vector);
}
});
};
that = {
get legend() { return legend; },
get grid() { return grid0; },
letAct: letAct,
toString: function () {
var output = '',
y,
y_max = grid0.height,
x,
x_max = grid0.width,
elem;
for (y = 0; y < y_max; y += 1) {
for (x = 0; x < x_max; x += 1) {
elem = grid0.get(vector(x, y));
output += charFromElement(elem);
}
output += '<br>';
}
return output;
},
turn: turn,
checkDestination: checkDestination
};
return that;
};
wall = function () {
var that = {};
return that;
};
view = function (world, vector) {
var that,
look,
findAll,
find,
pre;
look = function (dir) {
var target = vector.plus(directions[dir]);
if (world.grid.isInside(target)) {
return charFromElement(world.grid.get(target));
}
return '#';
};
findAll = function (ch) {
var found = [];
directionNames.forEach(function (dir) {
if (look(dir) === ch) {
found.push(dir);
}
});
return found;
};
find = function (ch) {
var found = findAll(ch),
t;
if (found.length === 0) {
return null;
}
for (t = found.random(); pre === t; t = found.random()) {
;
}
return t;
};
that = { look: look, findAll: findAll, find: find};
return that;
};
dirPlus = function (dir, n) {
var index = directionNames.indexOf(dir);
return directionNames[(index + n + 8) % 8];
};
wallFollower = function () {
var that,
dir = 's';
that = {
dir: dir,
act: function (view) {
var start = dir;
if (view.look(dirPlus(dir, -3)) !== ' ') {
start = dir = dirPlus(dir, -2);
}
for (; view.look(dir) !== ' '; ) {
dir = dirPlus(dir, 1);
if (dir === start) {
break;
}
}
return {type: 'move', direction: dir};
},
};
return that;
};
lifelikeWorld = function (map, legend) {
var that = world(map, legend);
that.letAct = function (critter, vector) {
var action = critter.act(view(that, vector)),
handled;
handled = action && action.type in actionTypes &&
actionTypes[action.type](that, critter, vector, action);
if (!handled) {
critter.energy -= 0.2;
if (critter.energy <= 0) {
that.grid.set(vector, null);
}
}
};
return that;
};
actionTypes = Object.create(null);
actionTypes.grow = function (that, critter) {
critter.energy += 0.5;
return true;
};
actionTypes.move = function (that, critter, vector, action) {
var dest = that.checkDestination(action, vector);
if (dest === null || critter.energy <= 1 ||
that.grid.get(dest) !== null) {
return false;
}
critter.energy -= 1;
that.grid.set(vector, null);
that.grid.set(dest, critter);
return true;
};
actionTypes.stay = function () {
return true;
};
actionTypes.eat = function (that, critter, vector, action) {
var dest = that.checkDestination(action, vector),
atDest = dest !== null && that.grid.get(dest);
plantCount -= 1;
if (!atDest || atDest.energy === null) {
return false;
}
critter.energy += atDest.energy;
that.grid.set(dest, null);
return true;
};
actionTypes.reproduce = function (that, critter, vector, action) {
var baby = elementFromChar(that.legend, critter.originChar),
dest = that.checkDestination(action, vector);
if (dest === null || critter.energy <= 2 * baby.energy ||
that.grid.get(dest) !== null) {
return false;
}
critter.energy -= 2 * baby.energy;
that.grid.set(dest, baby);
return true;
};
plant = function () {
var that,
energy = 3 + Math.random() * 4;
plantCount += 1;
that = {
energy: energy,
act: function (context) {
var space;
if (this.energy > 15) {
space = context.find(' ');
if (space) {
return {type: 'reproduce', direction: space};
}
}
if (this.energy < 20) {
return {type: 'grow'};
}
},
};
return that;
};
plantEater = function () {
var that,
energy = 20;
that = {
energy: energy,
act: function (context) {
var space = context.find(' '),
plant;
if (energy > 60 && space) {
return {type: 'reproduce', direction: space};
}
plant = context.find('*');
if (plant) {
return {type: 'eat', direction: plant};
}
if (space) {
return {type: 'move', direction: space};
}
},
};
return that;
};
smartPlantEater = function () {
var that = plantEater();
that.act = function (context) {
var space = context.find(' '),
plant;
if (that.energy > 60 && space) {
return {type: 'reproduce', direction: space};
}
plant = context.find('*');
if (plant) {
if (plantCount === 5) {
return {type: 'stay'};
}
return {type: 'eat', direction: plant};
}
if (space) {
return {type: 'move', direction: space};
}
};
return that;
};
king = function () {
var that = plantEater();
that.act = function (context) {
var space = context.find(' '),
plantEater;
if (that.energy > 60 && space) {
return {type: 'reproduce', direction: space};
}
plantEater = context.find('o');
if (plantEater) {
return {type: 'eat', direction: plantEater};
}
if (space) {
if (that.energy < 20 && Math.random() > 0.5) {
return {type: 'stay'};
}
if (Math.random() > 0.5) {
return {type: 'stay'};
}
return {type: 'move', direction: space};
}
};
return that;
};
valley = lifelikeWorld(
["############################",
'##### X #####',
'## *** **##',
'# *##** ** O *##',
'# *** O ##** *#',
'# O X ##*** #',
'# ##** #',
'# O #* #',
'#* #** O #',
'#*** ##** O **#',
'##**** ###*** X*###',
'############################'],
{
'#': wall,
'O': smartPlantEater,
'X': king,
'*': plant,
}
);
intervalID = setInterval(function () {
valley.turn();
div_output.innerHTML = valley.toString();
}, parseInt(input_speed.value, 10));
input_speed.onchange = function () {
var t = parseInt(input_speed.value, 10);
clearInterval(intervalID);
intervalID = setInterval(function () {
valley.turn();
div_output.innerHTML = valley.toString();
}, t);
};
}());
0 コメント:
コメントを投稿