Skip to content

Commit be574c0

Browse files
committed
Another pass over chapter 7
1 parent ca09716 commit be574c0

2 files changed

Lines changed: 90 additions & 91 deletions

File tree

07_elife.txt

Lines changed: 89 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,28 @@ ____
1313

1414
In the practical chapters of this book, I stop pummeling you with new
1515
theory for a brief moment, and instead work through a program. Theory
16-
is indispensable, but reading and understanding programs is the way
17-
you actually learn to program.
16+
is indispensable, when learning to program, but it should be
17+
accompanied by reading and understanding non-trivial programs.
1818

19-
The example project for this chapter is to build a virtual ecosystem,
20-
a little world with creatures moving around in it, in which the
21-
animals are autonomous programs struggling for survival.
19+
Our project in this chapter is to build a virtual ecosystem, a little
20+
world with creatures moving around in it, in which the animals are
21+
autonomous programs struggling for survival.
2222

2323
== Definition ==
2424

2525
In order to make this task manageable, we radically simplify the
26-
concept of a “world”. Namely, we understand it to mean a
26+
concept of a _world_. Namely, we understand it to mean a
2727
two-dimensional grid with each entity taking up a full square of the
28-
grid. Each “turn”, all the animals in the world get a chance to take
28+
grid. Each _turn_, all the animals in the world get a chance to take
2929
some action.
3030

3131
(((discretization)))(((simulation))) Thus, we chop both time and space
32-
into units with a fixed size—squares for space and turns for time.
33-
This tends to make things easier to model. Of course, it has the
34-
drawback of being crude and inaccurate. But this simulation is
35-
intended to be amusing, not accurate, so we can freely cut such
36-
corners.
32+
into units with a fixed size—squares for space and turns for time. Of
33+
course, this has the drawback of being crude and inaccurate. But our
34+
simulation is intended to be amusing, not accurate, so we can freely
35+
cut such corners.
3736

38-
A world can be defined with a “plan”, which is an array of strings
37+
A world can be defined with a _plan_, which is an array of strings
3938
that lay out the world's grid, using one character per square, and a
4039
legend, which is an object that tells us, for each character, what it
4140
means.
@@ -67,9 +66,9 @@ guessed, are empty space.
6766
object. Such an object keeps track of the shape and content of the
6867
world and lets the critters inside move. It has a method `toString`,
6968
which converts the world back to a string similar to the plan it was
70-
based on, so that you can see what is going on inside of it. And a
71-
method `turn`, which allows all the critters in it take one turn, and
72-
updates the world to reflect their actions.
69+
based on, so that you can see what is going on inside of it. And it
70+
has a method `turn`, which allows all the critters in it take one
71+
turn, and updates the world to reflect their actions.
7372

7473
== Representing space ==
7574

@@ -92,7 +91,7 @@ Point.prototype.plus = function(other) {
9291
};
9392
----
9493

95-
Next, we'll want an object type that models the grid itself. A grid is
94+
Next, we need an object type that models the grid itself. A grid is
9695
part of a world, but we make it a separate object (which will be
9796
_part_ of a world object) in order to help keep the world object
9897
simple. The world will concern itself with world-related things, the
@@ -110,7 +109,7 @@ console.log(grid[1][2]);
110109
// → 2,1
111110
----
112111

113-
Or, we can use a single array, with size width × height, and decide
112+
Or, we can use a single array, with size width×height, and decide
114113
that the element at (x,y) is found at position x + y × width in the
115114
array.
116115

@@ -126,8 +125,8 @@ console.log(grid[2 + 1 * 3]);
126125
methods on the grid object type, it doesn't matter to outside code
127126
which approach we take. I chose the second representation, because it
128127
makes it much easier to create the array. When calling the `Array`
129-
constructor with a single number as argument, it creates a new array
130-
of the given length, filled with `undefined` values.
128+
constructor with a single number as argument, it creates a new empty
129+
array of the given length.
131130

132131
This code defines the `Grid` object, with some basic methods:
133132

@@ -166,20 +165,20 @@ console.log(grid.get(new Point(1, 1)));
166165

167166
== A critter's programming interface ==
168167

169-
(((interface)))Before we can start to write a `World` constructor, we
170-
will have to think a little more about these “critter objects that
171-
will be living inside it. I mentioned that the world will ask the
172-
critters what action they want to take. This works as follows: Each
173-
critter object has an `act` method that, when called, returns an
174-
_action_. An action is an object with a `type` property, which names
175-
the type of action the critter wants to take, for example `"move"`.
176-
The action may also contain extra information, such as the direction
177-
the critter wants to move in.
168+
(((interface)))Before we can start on the `World` constructor, we must
169+
get more specific about the critter objects that will be living inside
170+
of it. I mentioned that the world will ask the critters what action
171+
they want to take. This works as follows: Each critter object has an
172+
`act` method that, when called, returns an _action_. An action is an
173+
object with a `type` property, which names the type of action the
174+
critter wants to take, for example `"move"`. The action may also
175+
contain extra information, such as the direction the critter wants to
176+
move in.
178177

179178
Critters are terribly myopic, and thus they can only see the squares
180179
directly around them on the grid. Seeing nearby objects can be useful
181180
when deciding which action to take. When the `act` method is called,
182-
it is given a “view” object that allows the critter to inspect its
181+
it is given a _view_ object that allows the critter to inspect its
183182
surroundings. We name each of the eight nearby squares (cardinal plus
184183
diagonal) with its compass name: `"n"` for North, `"ne"` for
185184
North-East, and so on. We'll use this object to map from direction
@@ -242,8 +241,8 @@ around it (for example when crowded into a corner by other critters).
242241

243242
The `randomElement` function simply picks a random element from an
244243
array, using `Math.random` plus some arithmetic to get a random index.
245-
We'll use that again later (randomness is useful for simulations). We
246-
saw `Object.keys` in the previous chapter. It returns an array
244+
We'll use that again later (randomness can be useful in simulations).
245+
We saw `Object.keys` in the previous chapter. It returns an array
247246
containing all the property names in the given object. In this case,
248247
all the direction names.
249248

@@ -387,16 +386,14 @@ test.method([5]);
387386
// → 15
388387
----
389388

390-
The function passed to `forEach` there is the result of the `bind`
391-
call, and thus have its `this` bound to the first argument given to
392-
`bind`—the outer function's `this` value (which holds the `test`
393-
object).
389+
The function passed to `forEach` is the result of the `bind` call, and
390+
thus has its `this` bound to the first argument given to ++bind++—the
391+
outer function's `this` value (which holds the `test` object).
394392

395-
Most of the standard array higher-order methods, such as `forEach` and
396-
`map`, take an optional second argument that can also be used for this
397-
purpose. The value you pass there will be used as the `this` of the
398-
calls to the iteration function, so you could also express the example
399-
above in a slightly simpler way:
393+
Most of the standard higher-order methods on arrays, such as `forEach`
394+
and `map`, take an optional second argument that can also be used to
395+
provide a `this` for the calls to the iteration function. So you could
396+
also express the example above in a slightly simpler way:
400397

401398
[source,javascript]
402399
----
@@ -415,7 +412,7 @@ test.method([5]);
415412
This only works for higher-order functions that support this feature,
416413
so you'll often need to fall back to one of the other approaches.
417414

418-
In our own higher-order functions, we can support such a “context”
415+
In our own higher-order functions, we can support such a _context_
419416
parameter by using the `call` method to call our argument function.
420417
For example, this `forEach` method for our `Grid` type, which calls a
421418
given function for each element in the grid that isn't null or
@@ -438,12 +435,13 @@ Grid.prototype.forEach = function(f, context) {
438435

439436
== Animating life ==
440437

441-
(((simulation)))The next goal is to write a method `turn` for the
438+
(((simulation)))The next step is to write a method `turn` for the
442439
world object, that gives the critters inside of it a chance to act. It
443-
will go over the whole grid, square by square, looking for objects
444-
with an `act` method. When it finds one, it calls that method to get
445-
an action object, and carries out the resulting action if it makes
446-
sense. For now, only `"move"` actions are understood.
440+
will go over the grid using the `forEach` method we just defined,
441+
looking for objects with an `act` method. When it finds one, it calls
442+
that method to get an action object, and carries out the resulting
443+
action if it makes sense. For now, only `"move"` actions are
444+
understood.
447445

448446
There is one potential problem with this approach. Can you spot it? If
449447
we let critters move as we come across them, they may move to a square
@@ -507,27 +505,27 @@ the square where the critter used to be to hold null, and store the
507505
critter in the destination square.
508506

509507
(((interface)))(((private property)))These two methods are not part of
510-
the external interface of a `World` object. They are internal details.
511-
Some languages provide ways to explicitly declare certain methods and
512-
properties “private” and signal an error when you try to use them from
513-
outside the object. JavaScript does not, so you will have to rely on
514-
some other form of communication to describe what is part of an
515-
object's interface. Sometimes it can be useful to use a naming scheme
516-
to distinguish between external and internal properties, for example
517-
by prefixing all internal ones with an underscore (‘_’). This will
518-
make accidental uses of properties that are not part of an object's
519-
interface easier to spot.
508+
the external interface of a `World` object. They are an internal
509+
detail. Some languages provide ways to explicitly declare certain
510+
methods and properties _private_ and signal an error when you try to
511+
use them from outside the object. JavaScript does not, so you will
512+
have to rely on some other form of communication to describe what is
513+
part of an object's interface. Sometimes it can help to use a naming
514+
scheme to distinguish between external and internal properties, for
515+
example by prefixing all internal ones with an underscore character
516+
(“_”). This will make accidental uses of properties that are not part
517+
of an object's interface easier to spot.
520518

521519
(((defensive programming)))Note that `letAct` takes care to ignore
522520
nonsense input—it doesn't assume that the action's `direction`
523521
property is valid, or that the `type` property makes sense. This kind
524-
of “defensive” programming makes sense in some situations. The main
522+
of _defensive_ programming makes sense in some situations. The main
525523
reason for doing it is to validate inputs coming from sources we do
526524
not control (such as user or file input), but it can also be useful to
527-
isolate subsystems from each other. In this case, the idea is that the
528-
critters themselves can be programmed sloppily—they don't have to
529-
verify if their intended actions make sense, they can just request an
530-
action and the world will figure out whether to allow it.
525+
isolate subsystems from each other. In this case, the intention is
526+
that the critters themselves can be programmed sloppily—they don't
527+
have to verify if their intended actions make sense, they can just
528+
request an action and the world will figure out whether to allow it.
531529

532530
The one missing part, the `View` type, looks like this:
533531

@@ -672,17 +670,17 @@ WallFollower.prototype.act = function(view) {
672670
First, we need to be able to “compute” with directions. Since they are
673671
modeled by a set of strings, we need to define our own operation
674672
(`dirPlus`) to calculate relative directions. So `dirPlus("n", 1)`
675-
means one 45-degree turn clockwise from North, so `"ne"`. Similarly,
673+
means one 45-degree turn clockwise from North, giving `"ne"`. Similarly,
676674
`dirPlus("s", -2)` means 90 degrees counterclockwise from South, which
677675
is East.
678676

679677
The function first looks up the index of the given direction in the
680678
`directionNames` array, then offsets it, takes the remainder of the
681679
result and 8, and then fetches the corresponding name from the array
682-
again. The extra “+++ 8++” is there because the ‘%’ operator, when
680+
again. The extra “+++ 8++” is there because the “%” operator, when
683681
given a negative number on the left hand side, will return a negative
684682
result. So we add another eight to make sure the sum is positive,
685-
under the requirement that the `n` argument is never less than -8.
683+
assuming that the `n` argument is never less than -8.
686684

687685
The `act` method, in the simple case, only has to do the following: It
688686
starts “scanning” the critter's surroundings starting from its
@@ -781,8 +779,8 @@ LifeLikeWorld.prototype.letAct = function(critter, point) {
781779
};
782780
----
783781

784-
The pattern of deriving a type from another type was explained in
785-
Chapter 6. This `letAct` method delegates the work of actually
782+
The principle of deriving a type from another type was explained in
783+
Chapter 6. This new `letAct` method delegates the work of actually
786784
performing an action to various functions stored in the `actionTypes`
787785
object, under property names corresponding to the action's name. We
788786
will define these functions in a moment. It first checks whether an
@@ -824,7 +822,8 @@ Moving is more involved:
824822
----
825823
actionTypes.move = function(critter, point, action) {
826824
var dest = this.checkDestination(action, point);
827-
if (dest == null || critter.energy <= 1 ||
825+
if (dest == null ||
826+
critter.energy <= 1 ||
828827
this.grid.get(dest) != null)
829828
return false;
830829
critter.energy -= 1;
@@ -836,11 +835,10 @@ actionTypes.move = function(critter, point, action) {
836835

837836
This one first checks whether the action provides a valid destination
838837
(using the `direction` property from the action) through the
839-
`checkDestination` method defined earlier, whether the critter has
840-
the energy required—moving costs one unit of energy—and whether the
841-
destination square is actually empty. If any of those fail, it returns
842-
false to tell `letAct` that no action was taken. Otherwise, it moves
843-
the critter, and subtracts the energy cost.
838+
`checkDestination` method defined earlier. If not, or when the
839+
destination isn't empty or the critter does not have the required
840+
energy, it returns false to tell `letAct` that no action was taken.
841+
Otherwise, it moves the critter, and subtracts the energy cost.
844842

845843
// include_code
846844

@@ -871,7 +869,8 @@ actionTypes.reproduce = function(critter, point, action) {
871869
var baby = elementFromChar(this.legend,
872870
critter.originChar);
873871
var dest = this.checkDestination(action, point);
874-
if (dest == null || critter.energy <= 2 * baby.energy ||
872+
if (dest == null ||
873+
critter.energy <= 2 * baby.energy ||
875874
this.grid.get(dest) != null)
876875
return false;
877876
critter.energy -= 2 * baby.energy;
@@ -892,7 +891,7 @@ longer hypothetical) and the energy is spent.
892891

893892
== Populating the new world ==
894893

895-
We now have the framework needed to simulate these lifelike
894+
We now have the framework needed to simulate these lifelike
896895
creatures. We could put the critters from the old world into it, but
897896
they would just die, since they don't have an energy attribute. So let
898897
us make new ones. First we'll write a plant, which is a rather simple
@@ -942,14 +941,14 @@ PlantEater.prototype.act = function(context) {
942941
};
943942
----
944943

945-
We'll use the ‘*’ character for plants, so that is what this creature
944+
We'll use the “*” character for plants, so that is what this creature
946945
looks for when it searches for food.
947946

948947
== Bringing it to life ==
949948

950-
And that gives us enough elements to try our new world. Imagine a
951-
grassy valley with a herd of herbivores in it, some boulders, and lush
952-
plant life everywhere.
949+
And that gives us enough elements to try our new world. Imagine the
950+
map below as a grassy valley with a herd of herbivores in it, some
951+
boulders, and lush plant life everywhere.
953952

954953
// include_code
955954

@@ -1051,15 +1050,15 @@ Having the inhabitants of our world go extinct after a few minutes is
10511050
kind of depressing. To deal with this, we could try to create a
10521051
smarter plant eater.
10531052

1054-
Observing our ecosystem, there are several problems that I notice.
1055-
Firstly, the herbivores are terribly greedy, stuffing themselves with
1056-
any plant they see, and thus wiping out plant life in any area where
1057-
they are plentiful. Secondly, their randomized movement (the
1058-
`view.find` method returns a random direction when multiple directions
1059-
are possible) causes them to stumble around ineffectively, often
1060-
causing them to starve when there happen to be no plans in their
1061-
immediate vicinity. And finally, they breed very fast, which makes the
1062-
cycling between abundance and famine especially intense.
1053+
Observing our ecosystem, there are several obvious problems. Firstly,
1054+
the herbivores are terribly greedy, stuffing themselves with any plant
1055+
they see, and thus wiping out plant life in any area where they are
1056+
plentiful. Secondly, their randomized movement (due to the `view.find`
1057+
method returning a random direction when multiple directions match)
1058+
causes them to stumble around ineffectively, often causing them to
1059+
starve when there happen to be no plans in their immediate vicinity.
1060+
And finally, they breed very fast, which makes the cycling between
1061+
abundance and famine especially intense.
10631062

10641063
Write a new critter type that tries to address one or more of these
10651064
points, and substitute it for the old `PlantEater` type in the valley
@@ -1116,7 +1115,7 @@ minimum energy level at which they reproduce. Of course, making the
11161115
ecosystem more stable also makes it more boring. A handful of fat,
11171116
immobile critters forever munching on a sea of plants, never
11181117
reproducing, makes for a very stable ecosystem. But no one wants to
1119-
see that.
1118+
watch that.
11201119

11211120
!!solution!!
11221121

@@ -1170,7 +1169,7 @@ animateWorld(new LifeLikeWorld(
11701169

11711170
Many of the same tricks that worked for in the previous exercise also
11721171
apply here. Making the predators big (large energy), and having them
1173-
reproduce slowly, is recommended. That'll make them less vulnerable to
1172+
reproduce slowly is recommended. That'll make them less vulnerable to
11741173
periods of starvation when the herbivores are scarce.
11751174

11761175
Beyond staying alive, keeping its food stock alive is the critter's

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ These are the sources used to build the second edition of Eloquent
44
JavaScript.
55

66
The rewrite is a work in progress. Feedback welcome, in the form of
7-
issues and pull requests. Chapter 0 to 6 are in a more or less stable
7+
issues and pull requests. Chapter 0 to 7 are in a more or less stable
88
state. Don't waste too much time on detailed correction of anything
99
beyond that yet.
1010

0 commit comments

Comments
 (0)