Skip to content

Instantly share code, notes, and snippets.

@jschaub30
Last active September 1, 2015 15:52
Show Gist options
  • Save jschaub30/0951eadb13e671ad86a0 to your computer and use it in GitHub Desktop.
Save jschaub30/0951eadb13e671ad86a0 to your computer and use it in GitHub Desktop.
Eloquent javascript solutions
{
"ecmaFeatures": {
"blockBindings": true
},
"rules": {
"semi": 2,
"no-console": 0
},
"globals": {
"console": true
}
}
var ANCESTRY_FILE = "[\n " + [
'{"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"}',
'{"name": "Emma de Milliano", "sex": "f", "born": 1876, "died": 1956, "father": "Petrus de Milliano", "mother": "Sophia van Damme"}',
'{"name": "Maria de Rycke", "sex": "f", "born": 1683, "died": 1724, "father": "Frederik de Rycke", "mother": "Laurentia van Vlaenderen"}',
'{"name": "Jan van Brussel", "sex": "m", "born": 1714, "died": 1748, "father": "Jacobus van Brussel", "mother": "Joanna van Rooten"}',
'{"name": "Philibert Haverbeke", "sex": "m", "born": 1907, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"}',
'{"name": "Jan Frans van Brussel", "sex": "m", "born": 1761, "died": 1833, "father": "Jacobus Bernardus van Brussel", "mother":null}',
'{"name": "Pauwels van Haverbeke", "sex": "m", "born": 1535, "died": 1582, "father": "N. van Haverbeke", "mother":null}',
'{"name": "Clara Aernoudts", "sex": "f", "born": 1918, "died": 2012, "father": "Henry Aernoudts", "mother": "Sidonie Coene"}',
'{"name": "Emile Haverbeke", "sex": "m", "born": 1877, "died": 1968, "father": "Carolus Haverbeke", "mother": "Maria Sturm"}',
'{"name": "Lieven de Causmaecker", "sex": "m", "born": 1696, "died": 1724, "father": "Carel de Causmaecker", "mother": "Joanna Claes"}',
'{"name": "Pieter Haverbeke", "sex": "m", "born": 1602, "died": 1642, "father": "Lieven van Haverbeke", "mother":null}',
'{"name": "Livina Haverbeke", "sex": "f", "born": 1692, "died": 1743, "father": "Daniel Haverbeke", "mother": "Joanna de Pape"}',
'{"name": "Pieter Bernard Haverbeke", "sex": "m", "born": 1695, "died": 1762, "father": "Willem Haverbeke", "mother": "Petronella Wauters"}',
'{"name": "Lieven van Haverbeke", "sex": "m", "born": 1570, "died": 1636, "father": "Pauwels van Haverbeke", "mother": "Lievijne Jans"}',
'{"name": "Joanna de Causmaecker", "sex": "f", "born": 1762, "died": 1807, "father": "Bernardus de Causmaecker", "mother":null}',
'{"name": "Willem Haverbeke", "sex": "m", "born": 1668, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Pieter Antone Haverbeke", "sex": "m", "born": 1753, "died": 1798, "father": "Jan Francies Haverbeke", "mother": "Petronella de Decker"}',
'{"name": "Maria van Brussel", "sex": "f", "born": 1801, "died": 1834, "father": "Jan Frans van Brussel", "mother": "Joanna de Causmaecker"}',
'{"name": "Angela Haverbeke", "sex": "f", "born": 1728, "died": 1734, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"}',
'{"name": "Elisabeth Haverbeke", "sex": "f", "born": 1711, "died": 1754, "father": "Jan Haverbeke", "mother": "Maria de Rycke"}',
'{"name": "Lievijne Jans", "sex": "f", "born": 1542, "died": 1582, "father":null, "mother":null}',
'{"name": "Bernardus de Causmaecker", "sex": "m", "born": 1721, "died": 1789, "father": "Lieven de Causmaecker", "mother": "Livina Haverbeke"}',
'{"name": "Jacoba Lammens", "sex": "f", "born": 1699, "died": 1740, "father": "Lieven Lammens", "mother": "Livina de Vrieze"}',
'{"name": "Pieter de Decker", "sex": "m", "born": 1705, "died": 1780, "father": "Joos de Decker", "mother": "Petronella van de Steene"}',
'{"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"}',
'{"name": "Daniel Haverbeke", "sex": "m", "born": 1652, "died": 1723, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Lieven Haverbeke", "sex": "m", "born": 1631, "died": 1676, "father": "Pieter Haverbeke", "mother": "Anna van Hecke"}',
'{"name": "Martina de Pape", "sex": "f", "born": 1666, "died": 1727, "father": "Vincent de Pape", "mother": "Petronella Wauters"}',
'{"name": "Jan Francies Haverbeke", "sex": "m", "born": 1725, "died": 1779, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"}',
'{"name": "Maria Haverbeke", "sex": "m", "born": 1905, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"}',
'{"name": "Petronella de Decker", "sex": "f", "born": 1731, "died": 1781, "father": "Pieter de Decker", "mother": "Livina Haverbeke"}',
'{"name": "Livina Sierens", "sex": "f", "born": 1761, "died": 1826, "father": "Jan Sierens", "mother": "Maria van Waes"}',
'{"name": "Laurentia Haverbeke", "sex": "f", "born": 1710, "died": 1786, "father": "Jan Haverbeke", "mother": "Maria de Rycke"}',
'{"name": "Carel Haverbeke", "sex": "m", "born": 1796, "died": 1837, "father": "Pieter Antone Haverbeke", "mother": "Livina Sierens"}',
'{"name": "Elisabeth Hercke", "sex": "f", "born": 1632, "died": 1674, "father": "Willem Hercke", "mother": "Margriet de Brabander"}',
'{"name": "Jan Haverbeke", "sex": "m", "born": 1671, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Anna van Hecke", "sex": "f", "born": 1607, "died": 1670, "father": "Paschasius van Hecke", "mother": "Martijntken Beelaert"}',
'{"name": "Maria Sturm", "sex": "f", "born": 1835, "died": 1917, "father": "Charles Sturm", "mother": "Seraphina Spelier"}',
'{"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"}'
].join(",\n ") + "\n]";
// This makes sure the data is exported in node.js —
// `require(./path/to/ancestry.js)` will get you the array.
if (typeof module != "undefined" && module.exports)
module.exports = ANCESTRY_FILE;
/*
Write a simple module similar to the weekDay module that can convert month
numbers (zero-based, as in the Date type) to names and can convert names back
to numbers. Give it its own namespace since it will need an internal array of
month names, and use plain JavaScript, without any module loader system.
*/
var month = function(){
"use strict";
var months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
return {
name: function (idx) { return months[idx]; },
number: function (name) { return months.indexOf(name); }
};
}();
console.log(month.name(2));
// → March
console.log(month.number("November"));
// → 10
(function(){
"use strict";
function skipSpace(string) {
var first = string.search(/\S/);
if (first == -1) { return ""; }
return string.slice(first);
}
function parseExpression(program) {
program = skipSpace(program);
var match, expr;
if (match = /^"([^"]*)"/.exec(program)) {
expr = {type: "value", value: match[1]}; }
else if (match = /^\d+\b/.exec(program)) {
expr = {type: "value", value: Number(match[0])}; }
else if (match = /^[^\s(),"]+/.exec(program)) {
expr = {type: "word", name: match[0]}; }
else {
throw new SyntaxError("Unexpected syntax: " + program); }
return parseApply(expr, program.slice(match[0].length));
}
})();
(function(){
// reverse exercises
"use strict";
function reverseArray(arr) {
var newArray = [];
for (var i = arr.length; i > 0; i = i - 1) {
newArray.push(arr[i - 1]);
}
return newArray;
}
function reverseArrayInPlace(arr) {
var oldArray = reverseArray(arr);
for (var i = arr.length - 1; i >= 0; i = i - 1) {
arr[i] = oldArray[i];
}
}
console.log(reverseArray(["A", "B", "C"]));
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
})();
(function(){
// list exercises
"use strict";
function nth(list, number){
if (number === 0){
return list.value;
} else {
return nth(list.rest, number - 1);
}
}
function prepend(val, list){
return {value: val, rest: list};
}
function arrayToList(arr){
var list = null;
for (var i = arr.length; i > 0; i--){
list = prepend(arr[i - 1], list);
}
return list;
}
function listToArray(list){
var arr = [];
for (var node = list; node; node = node.rest) {
arr.push(node.value);
}
return arr;
}
// Recursive version of listToArray
// function listToArray(list){
// if (list.rest === null) { return list.value; }
// else {
// return [].concat(list.value, listToArray(list.rest));
// }
// }
console.log(arrayToList([10, 20]));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(listToArray(arrayToList([10, 20, 30])));
// → [10, 20, 30]
console.log(prepend(10, prepend(20, null)));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(nth(arrayToList([10, 20, 30]), 1));
// → 20
})();
(function(){
"use strict";
function deepEqual(val1, val2){
if (typeof (val1) === "object" && typeof (val1) === "object" && val1 != null) {
var state = true;
for (var obj in val1) {
state = state && deepEqual(val1[obj], val2[obj]);
}
return state;
}
else {
return val1 === val2;
}
}
var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true
console.log(deepEqual(obj, {here: {is: "an"}, object: 3}));
// → false
console.log(deepEqual(obj, {here: {is: "an"}, obj: 2}));
// → false
})();
(function(){
"use strict";
/*Use the reduce method in combination with the concat method to “flatten”
an array of arrays into a single array that has all the elements of the input arrays.
*/
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function (a, b) { return a.concat(b); }));
// Your code here.
// → [1, 2, 3, 4, 5, 6]
/*
Using the example data set from this chapter, compute the average age
difference between mothers and children (the age of the mother when the child
is born).
*/
/*eslint-disable quotes */
var ANCESTRY_FILE = "[\n " + [
'{"name": "Carolus Haverbeke", "sex": "m", "born": 1832, "died": 1905, "father": "Carel Haverbeke", "mother": "Maria van Brussel"}',
'{"name": "Emma de Milliano", "sex": "f", "born": 1876, "died": 1956, "father": "Petrus de Milliano", "mother": "Sophia van Damme"}',
'{"name": "Maria de Rycke", "sex": "f", "born": 1683, "died": 1724, "father": "Frederik de Rycke", "mother": "Laurentia van Vlaenderen"}',
'{"name": "Jan van Brussel", "sex": "m", "born": 1714, "died": 1748, "father": "Jacobus van Brussel", "mother": "Joanna van Rooten"}',
'{"name": "Philibert Haverbeke", "sex": "m", "born": 1907, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"}',
'{"name": "Jan Frans van Brussel", "sex": "m", "born": 1761, "died": 1833, "father": "Jacobus Bernardus van Brussel", "mother":null}',
'{"name": "Pauwels van Haverbeke", "sex": "m", "born": 1535, "died": 1582, "father": "N. van Haverbeke", "mother":null}',
'{"name": "Clara Aernoudts", "sex": "f", "born": 1918, "died": 2012, "father": "Henry Aernoudts", "mother": "Sidonie Coene"}',
'{"name": "Emile Haverbeke", "sex": "m", "born": 1877, "died": 1968, "father": "Carolus Haverbeke", "mother": "Maria Sturm"}',
'{"name": "Lieven de Causmaecker", "sex": "m", "born": 1696, "died": 1724, "father": "Carel de Causmaecker", "mother": "Joanna Claes"}',
'{"name": "Pieter Haverbeke", "sex": "m", "born": 1602, "died": 1642, "father": "Lieven van Haverbeke", "mother":null}',
'{"name": "Livina Haverbeke", "sex": "f", "born": 1692, "died": 1743, "father": "Daniel Haverbeke", "mother": "Joanna de Pape"}',
'{"name": "Pieter Bernard Haverbeke", "sex": "m", "born": 1695, "died": 1762, "father": "Willem Haverbeke", "mother": "Petronella Wauters"}',
'{"name": "Lieven van Haverbeke", "sex": "m", "born": 1570, "died": 1636, "father": "Pauwels van Haverbeke", "mother": "Lievijne Jans"}',
'{"name": "Joanna de Causmaecker", "sex": "f", "born": 1762, "died": 1807, "father": "Bernardus de Causmaecker", "mother":null}',
'{"name": "Willem Haverbeke", "sex": "m", "born": 1668, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Pieter Antone Haverbeke", "sex": "m", "born": 1753, "died": 1798, "father": "Jan Francies Haverbeke", "mother": "Petronella de Decker"}',
'{"name": "Maria van Brussel", "sex": "f", "born": 1801, "died": 1834, "father": "Jan Frans van Brussel", "mother": "Joanna de Causmaecker"}',
'{"name": "Angela Haverbeke", "sex": "f", "born": 1728, "died": 1734, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"}',
'{"name": "Elisabeth Haverbeke", "sex": "f", "born": 1711, "died": 1754, "father": "Jan Haverbeke", "mother": "Maria de Rycke"}',
'{"name": "Lievijne Jans", "sex": "f", "born": 1542, "died": 1582, "father":null, "mother":null}',
'{"name": "Bernardus de Causmaecker", "sex": "m", "born": 1721, "died": 1789, "father": "Lieven de Causmaecker", "mother": "Livina Haverbeke"}',
'{"name": "Jacoba Lammens", "sex": "f", "born": 1699, "died": 1740, "father": "Lieven Lammens", "mother": "Livina de Vrieze"}',
'{"name": "Pieter de Decker", "sex": "m", "born": 1705, "died": 1780, "father": "Joos de Decker", "mother": "Petronella van de Steene"}',
'{"name": "Joanna de Pape", "sex": "f", "born": 1654, "died": 1723, "father": "Vincent de Pape", "mother": "Petronella Wauters"}',
'{"name": "Daniel Haverbeke", "sex": "m", "born": 1652, "died": 1723, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Lieven Haverbeke", "sex": "m", "born": 1631, "died": 1676, "father": "Pieter Haverbeke", "mother": "Anna van Hecke"}',
'{"name": "Martina de Pape", "sex": "f", "born": 1666, "died": 1727, "father": "Vincent de Pape", "mother": "Petronella Wauters"}',
'{"name": "Jan Francies Haverbeke", "sex": "m", "born": 1725, "died": 1779, "father": "Pieter Bernard Haverbeke", "mother": "Livina de Vrieze"}',
'{"name": "Maria Haverbeke", "sex": "m", "born": 1905, "died": 1997, "father": "Emile Haverbeke", "mother": "Emma de Milliano"}',
'{"name": "Petronella de Decker", "sex": "f", "born": 1731, "died": 1781, "father": "Pieter de Decker", "mother": "Livina Haverbeke"}',
'{"name": "Livina Sierens", "sex": "f", "born": 1761, "died": 1826, "father": "Jan Sierens", "mother": "Maria van Waes"}',
'{"name": "Laurentia Haverbeke", "sex": "f", "born": 1710, "died": 1786, "father": "Jan Haverbeke", "mother": "Maria de Rycke"}',
'{"name": "Carel Haverbeke", "sex": "m", "born": 1796, "died": 1837, "father": "Pieter Antone Haverbeke", "mother": "Livina Sierens"}',
'{"name": "Elisabeth Hercke", "sex": "f", "born": 1632, "died": 1674, "father": "Willem Hercke", "mother": "Margriet de Brabander"}',
'{"name": "Jan Haverbeke", "sex": "m", "born": 1671, "died": 1731, "father": "Lieven Haverbeke", "mother": "Elisabeth Hercke"}',
'{"name": "Anna van Hecke", "sex": "f", "born": 1607, "died": 1670, "father": "Paschasius van Hecke", "mother": "Martijntken Beelaert"}',
'{"name": "Maria Sturm", "sex": "f", "born": 1835, "died": 1917, "father": "Charles Sturm", "mother": "Seraphina Spelier"}',
'{"name": "Jacobus Bernardus van Brussel", "sex": "m", "born": 1736, "died": 1809, "father": "Jan van Brussel", "mother": "Elisabeth Haverbeke"}'
].join(",\n ") + "\n]";
/*eslint-enable quotes */
var ancestry = JSON.parse(ANCESTRY_FILE);
function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
var byName = {};
ancestry.forEach(function(person) {
byName[person.name] = person;
});
function mothersAge(person){
var mother = byName[person.mother];
if (mother) {
return person.born - mother.born;
}
}
console.log(average(ancestry.map(mothersAge).filter(function(p) { if (p) { return p; } })));
// Your code here.
// → 31.2
/*
Compute and output the average age of the people in the ancestry data set
per century.
*/
//My first attempt
// function ageInCentury(century) {
// var people = ancestry.filter(function (p){
// return Math.ceil(p.died / 100) === century;
// });
// return average(people.map(function (p) {
// return p.died - p.born;
// }));
// }
// console.log([16, 17, 18, 19, 20].map(ageInCentury));
//After reading the hints
function findCentury(person) {
return Math.ceil(person.died / 100);
}
function groupAgesByCentury(array) {
var groups = {};
array.forEach(function (person){
var century = findCentury(person);
if (!(century in groups)) {
groups[century] = [];
}
groups[century].push(person.died - person.born);
});
return groups;
}
var groups = groupAgesByCentury(ancestry);
console.log(groups);
for (var century in groups) {
console.log(century + ": " + average(groups[century]));
}
// → 16: 43.5
// 17: 51.2
// 18: 52.8
// 19: 54.8
// 20: 84.7
// 21: 94
/*
For bonus points, write a function groupBy that abstracts the grouping
operation. It should accept as arguments an array and a function that
computes the group for an element in the array and returns an object that
maps group names to arrays of group members.
*/
function groupBy(array, func) {
var allGroups = {};
array.forEach(function (member){
var group = func(member);
if (!(group in allGroups)) {
allGroups[group] = [];
}
allGroups[group].push(member);
});
return allGroups;
}
groups = groupBy(ancestry, findCentury);
console.log(groups);
for (century in groups) {
console.log(century + ": " + average(groups[century].map(function (p) {
return p.died - p.born;
})));
}
/*
Write two functions, every and some, that behave like these methods, except
that they take the array as their first argument rather than being a method.
*/
function every(array, func) {
return array.map(func).reduce(function (a, b) { return a && b; });
}
function some(array, func) {
return array.map(func).reduce(function (a, b) { return a || b; });
}
console.log(every([NaN, NaN, NaN], isNaN));
// → true
console.log(every([NaN, NaN, 4], isNaN));
// → false
console.log(some([NaN, 3, 4], isNaN));
// → true
console.log(some([2, 3, 4], isNaN));
// → false
})();
(function(){
"use strict";
/*
Write a constructor Vector that represents a vector in two-dimensional space.
It takes x and y parameters (numbers), which it should save to properties of
the same name.
Give the Vector prototype two methods, plus and minus, that take another
vector as a parameter and return a new vector that has the sum or difference
of the two vectors’ (the one in this and the parameter) x and y values.
Add a getter property length to the prototype that computes the length of the
vector—that is, the distance of the point (x, y) from the origin (0, 0).
*/
function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.plus = function (vector) {
return new Vector(this.x + vector.x, this.y + vector.y);
};
Vector.prototype.minus = function (vector) {
return new Vector(this.x - vector.x, this.y - vector.y);
};
Object.defineProperty(Vector.prototype, "length", {
get: function() {
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
}
});
console.log(new Vector(1, 2).plus(new Vector(2, 3)));
// → Vector{x: 3, y: 5}
console.log(new Vector(1, 2).minus(new Vector(2, 3)));
// → Vector{x: -1, y: -1}
console.log(new Vector(3, 4).length);
// → 5
/*
Design an interface that abstracts iteration over a collection of values. An
object that provides this interface represents a sequence, and the interface
must somehow make it possible for code that uses such an object to iterate
over the sequence, looking at the element values it is made up of and having
some way to find out when the end of the sequence is reached.
When you have specified your interface, try to write a function logFive that
takes a sequence object and calls console.log on its first five elements—or
fewer, if the sequence has fewer than five elements.
Then implement an object type ArraySeq that wraps an array and allows iteration
over the array using the interface you designed. Implement another object type
RangeSeq that iterates over a range of integers (taking from and to arguments
to its constructor) instead.
*/
function Sequence(values) {
this.values = values;
var count = 0;
Object.defineProperty(this, "next", {
get: function() {
if (count === (this.values.length - 1)){
this.isDone = true;
}
count += 1;
return this.values[count - 1];
}
});
Object.defineProperty(this, "isDone", {
value: false,
writable: true
});
}
function ArraySeq(arr) {
Sequence.call(this, arr);
}
function RangeSeq(start, stop) {
var arr = [];
for (var i = start; i < stop; i++){
arr.push(i);
}
Sequence.call(this, arr);
}
function logFive(seq) {
for (var i = 0; i < 5; i++) {
if (!seq.isDone) { console.log(seq.next); }
}
}
logFive(new ArraySeq([1, 2]));
// → 1
// → 2
logFive(new RangeSeq(100, 1000));
// → 100
// → 101
// → 102
// → 103
// → 104
})();
(function(){
"use strict";
var plan = ["############################",
"# # # o ##",
"# #",
"# ##### #",
"## # # ## #",
"### ## # #",
"# ### # #",
"# #### #",
"# ## o #",
"# o # o ### #",
"# # #",
"############################"];
function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
function Grid(width, height) {
this.space = new Array(width * height);
this.width = width;
this.height = height;
}
Grid.prototype.isInside = function(vector) {
return vector.x >= 0 && vector.x < this.width &&
vector.y >= 0 && vector.y < this.height;
};
Grid.prototype.get = function(vector) {
return this.space[vector.x + this.width * vector.y];
};
Grid.prototype.set = function(vector, value) {
this.space[vector.x + this.width * vector.y] = value;
};
Grid.prototype.forEach = function(f, context) {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var value = this.space[x + y * this.width];
if (value != null)
f.call(context, value, new Vector(x, y));
}
}
};
/*eslint-disable key-spacing, no-multi-spaces */
var directions = {
"n": new Vector( 0, -1),
"ne": new Vector( 1, -1),
"e": new Vector( 1, 0),
"se": new Vector( 1, 1),
"s": new Vector( 0, 1),
"sw": new Vector(-1, 1),
"w": new Vector(-1, 0),
"nw": new Vector(-1, -1)
};
/*eslint-enable key-spacing, no-multi-spaces */
function randomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
/*
each critter object has an act method that, when called, returns an action.
An action is an object with a type property, which names the type of action
the critter wants to take, for example "move". The action may also contain
extra information, such as the direction the critter wants to move in.
*/
function Action(type) {
this.type = type;
}
var directionNames = "n ne e se s sw w nw".split(" ");
function BouncingCritter() {
this.direction = randomElement(directionNames);
};
BouncingCritter.prototype.act = function(view) {
if (view.look(this.direction) != " ")
this.direction = view.find(" ") || "s";
return {type: "move", direction: this.direction};
};
function elementFromChar(legend, ch) {
if (ch == " ")
return null;
var element = new legend[ch]();
element.originChar = ch;
return element;
}
function World(map, legend) {
var grid = new Grid(map[0].length, map.length);
this.grid = grid;
this.legend = legend;
map.forEach(function(line, y) {
for (var x = 0; x < line.length; x++) {
grid.set(new Vector(x, y),
elementFromChar(legend, line[x]));
}
});
}
function charFromElement(element) {
if (element == null)
return " ";
else
return element.originChar;
}
World.prototype.toString = function() {
var output = "";
for (var y = 0; y < this.grid.height; y++) {
for (var x = 0; x < this.grid.width; x++) {
var element = this.grid.get(new Vector(x, y));
output += charFromElement(element);
}
output += "\n";
}
return output;
};
World.prototype.turn = function() {
var acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
};
World.prototype.letAct = function(critter, vector) {
var action = critter.act(new View(this, vector));
if (action && action.type == "move") {
var dest = this.checkDestination(action, vector);
if (dest && this.grid.get(dest) == null) {
this.grid.set(vector, null);
this.grid.set(dest, critter);
}
}
};
World.prototype.checkDestination = function(action, vector) {
if (directions.hasOwnProperty(action.direction)) {
var dest = vector.plus(directions[action.direction]);
if (this.grid.isInside(dest))
return dest;
}
};
function View(world, vector) {
this.world = world;
this.vector = vector;
}
View.prototype.look = function(dir) {
var target = this.vector.plus(directions[dir]);
if (this.world.grid.isInside(target))
return charFromElement(this.world.grid.get(target));
else
return "#";
};
View.prototype.findAll = function(ch) {
var found = [];
for (var dir in directions)
if (this.look(dir) == ch)
found.push(dir);
return found;
};
View.prototype.find = function(ch) {
var found = this.findAll(ch);
if (found.length == 0) return null;
return randomElement(found);
};
function Wall() {}
var world = new World(plan, {"#": Wall,
"o": BouncingCritter});
console.log(world.toString());
for (var i = 0; i < 5; i++) {
world.turn();
console.log(world.toString());
}
animateWorld(world);
})();
(function(){
"use strict";
function MultiplicatorUnitFailure() {}
function primitiveMultiply(a, b) {
if (Math.random() < 0.5)
{ return a * b; }
else
{ throw new MultiplicatorUnitFailure(); }
}
MultiplicatorUnitFailure.prototype = Object.create(Error.prototype);
MultiplicatorUnitFailure.prototype.name = "MultiplyError";
function reliableMultiply(a, b) {
for (;;) {
try {
return primitiveMultiply(a, b);
} catch (e) {
if (e instanceof MultiplicatorUnitFailure) {
console.log("MultiplyError");
} else {
throw e;
}
}
}
}
console.log(reliableMultiply(8, 8));
var box = {
locked: true,
unlock: function() { this.locked = false; },
lock: function() { this.locked = true; },
_content: [],
get content() {
if (this.locked) { throw new Error("Locked!"); }
return this._content;
}
};
/*
Write a function called withBoxUnlocked that takes a function value as
argument, unlocks the box, runs the function, and then ensures that the box
is locked again before returning, regardless of whether the argument function
returned normally or threw an exception.
*/
function withBoxUnlocked(body) {
var locked = box.lock;
if (locked) { box.unlock(); }
try {
body();
} finally {
if (locked) { box.lock(); }
}
}
withBoxUnlocked(function() {
box.content.push("gold piece");
});
try {
withBoxUnlocked(function() {
throw new Error("Pirates on the horizon! Abort!");
});
} catch (e) {
console.log("Error raised:", e);
}
console.log(box.locked);
// → true
})();
(function(){
"use strict";
/*
car and cat
pop and prop
ferret, ferry, and ferrari
Any word ending in ious
A whitespace character followed by a dot, comma, colon, or semicolon
A word longer than six letters
A word without the letter e
*/
// Fill in the regular expressions
function verify(regexp, yes, no) {
// Ignore unfinished exercises
if (regexp.source === "...") { return; }
yes.forEach(function(s) {
if (!regexp.test(s)) {
console.log("Failure to match '" + s + "'");
}
});
no.forEach(function(s) {
if (regexp.test(s)) {
console.log("Unexpected match for '" + s + "'");
}
});
}
verify(/ca[rt]/,
["my car", "bad cats"],
["camper", "high art"]);
verify(/pr?op/,
["pop culture", "mad props"],
["plop"]);
verify(/ferr[et|y|ari]/,
["ferret", "ferry", "ferrari"],
["ferrum", "transfer A"]);
verify(/\wious\b/,
["how delicious", "spacious room"],
["ruinous", "consciousness"]);
verify(/\s[.,:;]/,
["bad punctuation ."],
["escape the dot"]);
verify(/\w{7,}/,
["hottentottententen"],
["no", "hotten totten tenten"]);
verify(/\b[^e\s]+\b/,
["red platypus", "wobbling nest"],
["earth bed", "learning ape"]);
var text = "'I'm the cook,' he said, 'it's my job.'";
// Change this call.
console.log(text.replace(/(^)'|(\W)'|'(\W)/g, "$1$2\"$3"));
/*
Write an expression that matches only JavaScript-style numbers. It must
support an optional minus or plus sign in front of the number, the decimal
dot, and exponent notation—5e-3 or 1E10— again with an optional sign in front
of the exponent. Also note that it is not necessary for there to be digits in
front of or after the dot, but the number cannot be a dot alone. That is, .5
and 5. are valid JavaScript numbers, but a lone dot isn’t.
*/
// Fill in this regular expression.
var number = /^([-|+]?\d+\.?(\d+)?(e[-|+]?\d+)?|[-|+]?\.(\d+)(e[-|+]?\d+)?)$/i;
// Tests:
["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4",
"1e+12"].forEach(function(s) {
if (!number.test(s)) {
console.log("Failed to match '" + s + "'"); }
});
["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5",
"."].forEach(function(s) {
if (number.test(s)) {
console.log("Incorrectly accepted '" + s + "'"); }
});
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
Edit index.html, pick your chapter, and check the console.
<!-- <script type="text/javascript" src="chapter4.js"></script> -->
<!-- <script type="text/javascript" src="chapter5.js"></script> -->
<!-- <script type="text/javascript" src="chapter6.js"></script> -->
<!-- <script type="text/javascript" src="chapter7_elife.js"></script> -->
<!-- <script type="text/javascript" src="chapter8_errors.js"></script> -->
<script type="text/javascript" src="chapter9_regexp.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment