Game of Life :
The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.
Your task is to write a program to calculate the next generation of Conway's game of life, given any starting position.
You start with a two dimensional grid of cells, where each cell is either alive or dead.
The grid is finite, and no life can exist off the edges.
When calculating the next generation of the grid, follow these four rules:
- Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
- Any live cell with more than three live neighbours dies, as if by overcrowding.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any dead cell with exactly three live neighbours becomes a live cell.
Examples: * indicates live cell, . indicates dead cell
Example input: (4 x 8 grid)
........
....*...
...**...
........
Example output:
........
...**...
...**...
........
function nextGeneration(grid) {
return grid.map((row, rowIndex) => {
return row.map((cell, colIndex) => {
if (rowIndex !== 0 && colIndex !== 0 && rowIndex < grid.length - 1 && colIndex < row.length - 1) {
let neighboursCount = 0;
if (grid[rowIndex][colIndex + 1] === 1) neighboursCount++;
if (grid[rowIndex][colIndex - 1] === 1) neighboursCount++;
if (grid[rowIndex + 1][colIndex] === 1) neighboursCount++;
if (grid[rowIndex - 1][colIndex] === 1) neighboursCount++;
if (grid[rowIndex + 1][colIndex + 1] === 1) neighboursCount++;
if (grid[rowIndex + 1][colIndex - 1] === 1) neighboursCount++;
if (grid[rowIndex - 1][colIndex + 1] === 1) neighboursCount++;
if (grid[rowIndex - 1][colIndex - 1] === 1) neighboursCount++;
if (cell === 1) {
if (neighboursCount === 2 || neighboursCount === 3 ) {
return 1;
}
} else {
if (neighboursCount === 3 ) {
return 1;
}
}
return 0;
}
return 0;
});
});
}
describe("Given empty grid", () => {
it("when next generation, should return empty", () => {
Test.assertDeepEquals(nextGeneration([]), []);
});
});
describe("Given a single cell", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0],
[0,1,0],
[0,0,0],
]), [
[0,0,0],
[0,0,0],
[0,0,0],
]);
});
});
describe("Given a cell with 1 neighbour at rows", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0],
[0,1,1,0],
[0,0,0,0],
]), [
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
]);
});
});
describe("Given a cell with 2 neighbours at rows", () => {
it("when next generation, should live", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,1,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 2 neighbours at cols", () => {
it("when next generation, should live", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0],
[0,1,0],
[0,1,0],
[0,1,0],
[0,0,0],
]), [
[0,0,0],
[0,0,0],
[0,1,0],
[0,0,0],
[0,0,0],
]);
});
});
describe("Given a cell with 2 neighbours at \ (diagonal)", () => {
it("when next generation, should live", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,0,0,0],
[0,0,1,0,0],
[0,0,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 2 neighbours at / (diagonal)", () => {
it("when next generation, should live", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,0,0,1,0],
[0,0,1,0,0],
[0,1,0,0,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 4 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,1,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 5 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,1,1,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,1,1,1,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 6 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
[0,1,0,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 7 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,0],
[0,1,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]);
});
});
describe("Given a cell with 8 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,0],
[0,1,1,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]);
});
});
describe("Given a die cell with 2 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,0,0,0],
[0,0,0,0,0],
[0,1,0,0,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a die cell with 3 neighbours", () => {
it("when next generation, should live", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
[0,1,0,0,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]);
});
});
describe("Given a die cell with 4 neighbours", () => {
it("when next generation, should die", () => {
Test.assertDeepEquals(nextGeneration([
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
[0,1,0,1,0],
[0,0,0,0,0],
]), [
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
]);
});
});
function nextGeneration(grid) { return grid.map((row, rowIndex) => { return row.map((cell, colIndex) => { if (rowIndex !== 0 && colIndex !== 0 && rowIndex < grid.length - 1 && colIndex < row.length - 1) { let neighboursCount = 0; if (grid[rowIndex][colIndex + 1] === 1) neighboursCount++; if (grid[rowIndex][colIndex - 1] === 1) neighboursCount++; if (grid[rowIndex + 1][colIndex] === 1) neighboursCount++; if (grid[rowIndex - 1][colIndex] === 1) neighboursCount++; if (grid[rowIndex + 1][colIndex + 1] === 1) neighboursCount++; if (grid[rowIndex + 1][colIndex - 1] === 1) neighboursCount++; if (grid[rowIndex - 1][colIndex + 1] === 1) neighboursCount++; if (grid[rowIndex - 1][colIndex - 1] === 1) neighboursCount++; if (cell === 1) { if (neighboursCount === 2 || neighboursCount === 3 ) { return 1; } } else { if (neighboursCount === 3 ) { return 1; } } return 0; } return 0; }); }); }
- function nextGeneration(grid) {
return grid;- return grid.map((row, rowIndex) => {
- return row.map((cell, colIndex) => {
- if (rowIndex !== 0 && colIndex !== 0 && rowIndex < grid.length - 1 && colIndex < row.length - 1) {
- let neighboursCount = 0;
- if (grid[rowIndex][colIndex + 1] === 1) neighboursCount++;
- if (grid[rowIndex][colIndex - 1] === 1) neighboursCount++;
- if (grid[rowIndex + 1][colIndex] === 1) neighboursCount++;
- if (grid[rowIndex - 1][colIndex] === 1) neighboursCount++;
- if (grid[rowIndex + 1][colIndex + 1] === 1) neighboursCount++;
- if (grid[rowIndex + 1][colIndex - 1] === 1) neighboursCount++;
- if (grid[rowIndex - 1][colIndex + 1] === 1) neighboursCount++;
- if (grid[rowIndex - 1][colIndex - 1] === 1) neighboursCount++;
- if (cell === 1) {
- if (neighboursCount === 2 || neighboursCount === 3 ) {
- return 1;
- }
- } else {
- if (neighboursCount === 3 ) {
- return 1;
- }
- }
- return 0;
- }
- return 0;
- });
- });
- }
describe("Given empty grid", () => { it("when next generation, should return empty", () => { Test.assertDeepEquals(nextGeneration([]), []); }); }); describe("Given a single cell", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0], [0,1,0], [0,0,0], ]), [ [0,0,0], [0,0,0], [0,0,0], ]); }); }); describe("Given a cell with 1 neighbour at rows", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0], [0,1,1,0], [0,0,0,0], ]), [ [0,0,0,0], [0,0,0,0], [0,0,0,0], ]); }); }); describe("Given a cell with 2 neighbours at rows", () => { it("when next generation, should live", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,1,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 2 neighbours at cols", () => { it("when next generation, should live", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0], [0,1,0], [0,1,0], [0,1,0], [0,0,0], ]), [ [0,0,0], [0,0,0], [0,1,0], [0,0,0], [0,0,0], ]); }); }); describe("Given a cell with 2 neighbours at \ (diagonal)", () => { it("when next generation, should live", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 2 neighbours at / (diagonal)", () => { it("when next generation, should live", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,0,0,1,0], [0,0,1,0,0], [0,1,0,0,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 4 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,0,1,0], [0,0,1,0,0], [0,1,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,1,0,0], [0,1,0,1,0], [0,0,1,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 5 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,1,1,0], [0,0,1,0,0], [0,1,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,1,1,1,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 6 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,1,1,0], [0,1,1,0,0], [0,1,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], [0,1,0,0,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 7 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,1,1,0], [0,1,1,1,0], [0,1,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], ]); }); }); describe("Given a cell with 8 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,1,1,0], [0,1,1,1,0], [0,1,1,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], ]); }); }); describe("Given a die cell with 2 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,0,0,0], [0,0,0,0,0], [0,1,0,0,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], ]); }); }); describe("Given a die cell with 3 neighbours", () => { it("when next generation, should live", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], [0,1,0,0,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,0,0,0], [0,0,1,0,0], [0,0,0,0,0], [0,0,0,0,0], ]); }); }); describe("Given a die cell with 4 neighbours", () => { it("when next generation, should die", () => { Test.assertDeepEquals(nextGeneration([ [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], [0,1,0,1,0], [0,0,0,0,0], ]), [ [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], ]); }); });
- describe("Given empty grid", () => {
- it("when next generation, should return empty", () => {
assert.deepEqual(nextGeneration([]), []);- Test.assertDeepEquals(nextGeneration([]), []);
- });
- });
- describe("Given a single cell", () => {
- it("when next generation, should die", () => {
assert.deepEqual(nextGeneration([0,0,0,0,1,0,0,0,0,]), [0,0,0,0,0,0,0,0,0,- Test.assertDeepEquals(nextGeneration([
- [0,0,0],
- [0,1,0],
- [0,0,0],
- ]), [
- [0,0,0],
- [0,0,0],
- [0,0,0],
- ]);
- });
- });
- describe("Given a cell with 1 neighbour at rows", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0],
- [0,1,1,0],
- [0,0,0,0],
- ]), [
- [0,0,0,0],
- [0,0,0,0],
- [0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 2 neighbours at rows", () => {
- it("when next generation, should live", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 2 neighbours at cols", () => {
- it("when next generation, should live", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0],
- [0,1,0],
- [0,1,0],
- [0,1,0],
- [0,0,0],
- ]), [
- [0,0,0],
- [0,0,0],
- [0,1,0],
- [0,0,0],
- [0,0,0],
- ]);
- });
- });
- describe("Given a cell with 2 neighbours at \ (diagonal)", () => {
- it("when next generation, should live", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,0,0,0],
- [0,0,1,0,0],
- [0,0,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 2 neighbours at / (diagonal)", () => {
- it("when next generation, should live", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,0,0,1,0],
- [0,0,1,0,0],
- [0,1,0,0,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 4 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,1,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,1,0,1,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 5 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,0,1,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 6 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,1,1,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- [0,1,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 7 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,1,1,1,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a cell with 8 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,1,1,0],
- [0,1,1,1,0],
- [0,1,1,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a die cell with 2 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,0,0,0],
- [0,0,0,0,0],
- [0,1,0,0,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a die cell with 3 neighbours", () => {
- it("when next generation, should live", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- [0,1,0,0,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,1,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
- describe("Given a die cell with 4 neighbours", () => {
- it("when next generation, should die", () => {
- Test.assertDeepEquals(nextGeneration([
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- [0,1,0,1,0],
- [0,0,0,0,0],
- ]), [
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- [0,0,0,0,0],
- ]);
- });
- });
After doing:
- Extract method
- Move method donde calcular el precio (movie or rental)?
- Unit test smell: Indirect Testing
- Replace temp with query
- Replace Type Code with State/Strategy
- Replace Conditional with Polymorphism
- Replace Constructor with Factory Method
class RentalReport { statement(customer) { let result = "Rental Record for " + customer.name + "\n"; customer.rentals.forEach((each) => { //show figures for this rental result += "\t" + each.movie.title+ "\t" + each.getAmount() + "\n"; }); //add footer lines result += "Amount owed is " + customer.getTotalAmount() + "\n"; result += "You earned " + customer.getFrequentPoints() + " frequent renter points"; return result; } } const MovieType = { CHILDREN: Symbol('CHILDREN'), REGULAR: Symbol('REGULAR'), NEW_RELEASE: Symbol('NEW_RELEASE'), } class Price { static create(priceCode) { switch (priceCode) { case MovieType.REGULAR: return new RegularPrice(); case MovieType.NEW_RELEASE: return new NewReleasePrice(); case MovieType.CHILDREN: return new ChildrenPrice(); } } get code() { throw "not implemented"; } getAmount(daysRented) { throw "not implemented"; } getFrequentPoints(daysRented) { return 1; } } class ChildrenPrice extends Price { get code() { return MovieType.CHILDREN; } getAmount(daysRented) { let amount = 1.5; if (daysRented > 3) amount += (daysRented - 3) * 1.5; return amount; } } class RegularPrice extends Price { get code() { return MovieType.REGULAR; } getAmount(daysRented) { let amount = 2; if (daysRented > 2) amount += (daysRented - 2) * 1.5; return amount; } } class NewReleasePrice extends Price { get code() { return MovieType.NEW_RELEASE; } getAmount(daysRented) { return daysRented * 3; } getFrequentPoints(daysRented) { return daysRented > 1 ? 2 : 1; } } class Movie { constructor (title, priceCode) { this._title = title; this.priceCode = priceCode; } set priceCode(priceCode) { this._price = Price.create(priceCode); } get priceCode() { return this._price.code; } get title() { return this._title; } getAmount(daysRented) { return this._price.getAmount(daysRented); } getFrequentPoints(daysRented) { return this._price.getFrequentPoints(daysRented); } } class Rental { constructor(movie, daysRented) { this._movie = movie; this._daysRented = daysRented; } get daysRented() { return this._daysRented; } get movie() { return this._movie; } getAmount() { return this.movie.getAmount(this.daysRented); } getFrequentPoints() { return this.movie.getFrequentPoints(this.daysRented); } } class Customer { constructor(name) { this._name = name; this._rentals = []; } get name() { return this._name; } get rentals() { return this._rentals; } addRental(rental) { this._rentals.push(rental); } getFrequentPoints() { return this.rentals.reduce((frequentPoints, rental) => frequentPoints + rental.getFrequentPoints(), 0); } getTotalAmount() { return this.rentals.reduce((amount, rental) => amount + rental.getAmount(), 0); } }
- class RentalReport {
- statement(customer) {
let totalAmount = 0;let frequentRenterPoints = 0;- let result = "Rental Record for " + customer.name + "\n";
- customer.rentals.forEach((each) => {
let thisAmount = 0;//determine amounts for each lineswitch (each.movie.priceCode) {case MovieType.REGULAR:thisAmount += 2;if (each.daysRented > 2)thisAmount += (each.daysRented - 2) * 1.5;break;case MovieType.NEW_RELEASE:thisAmount += each.daysRented * 3;break;case MovieType.CHILDREN:thisAmount += 1.5;if (each.daysRented > 3)thisAmount += (each.daysRented - 3) * 1.5;break;}// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.movie.priceCode == MovieType.NEW_RELEASE) && each.daysRented > 1) frequentRenterPoints ++;- //show figures for this rental
result += "\t" + each.movie.title+ "\t" + thisAmount + "";totalAmount += thisAmount;- result += "\t" + each.movie.title+ "\t" + each.getAmount() + "
- ";
- });
- //add footer lines
result += "Amount owed is " + totalAmount + "";result += "You earned " + frequentRenterPoints + " frequent renter points";- result += "Amount owed is " + customer.getTotalAmount() + "
- ";
- result += "You earned " + customer.getFrequentPoints() + " frequent renter points";
- return result;
- }
- }
- const MovieType = {
- CHILDREN: Symbol('CHILDREN'),
- REGULAR: Symbol('REGULAR'),
- NEW_RELEASE: Symbol('NEW_RELEASE'),
- }
- class Price {
- static create(priceCode) {
- switch (priceCode) {
- case MovieType.REGULAR:
- return new RegularPrice();
- case MovieType.NEW_RELEASE:
- return new NewReleasePrice();
- case MovieType.CHILDREN:
- return new ChildrenPrice();
- }
- }
- get code() {
- throw "not implemented";
- }
- getAmount(daysRented) {
- throw "not implemented";
- }
- getFrequentPoints(daysRented) {
- return 1;
- }
- }
- class ChildrenPrice extends Price {
- get code() {
- return MovieType.CHILDREN;
- }
- getAmount(daysRented) {
- let amount = 1.5;
- if (daysRented > 3)
- amount += (daysRented - 3) * 1.5;
- return amount;
- }
- }
- class RegularPrice extends Price {
- get code() {
- return MovieType.REGULAR;
- }
- getAmount(daysRented) {
- let amount = 2;
- if (daysRented > 2)
- amount += (daysRented - 2) * 1.5;
- return amount;
- }
- }
- class NewReleasePrice extends Price {
- get code() {
- return MovieType.NEW_RELEASE;
- }
- getAmount(daysRented) {
- return daysRented * 3;
- }
- getFrequentPoints(daysRented) {
- return daysRented > 1 ? 2 : 1;
- }
- }
- class Movie {
- constructor (title, priceCode) {
- this._title = title;
this._priceCode = priceCode;- this.priceCode = priceCode;
- }
- set priceCode(priceCode) {
- this._price = Price.create(priceCode);
- }
- get priceCode() {
return this._priceCode;- return this._price.code;
- }
- get title() {
- return this._title;
- }
- getAmount(daysRented) {
- return this._price.getAmount(daysRented);
- }
- getFrequentPoints(daysRented) {
- return this._price.getFrequentPoints(daysRented);
- }
- }
- class Rental {
- constructor(movie, daysRented) {
- this._movie = movie;
- this._daysRented = daysRented;
- }
- get daysRented() {
- return this._daysRented;
- }
- get movie() {
- return this._movie;
- }
- getAmount() {
- return this.movie.getAmount(this.daysRented);
- }
- getFrequentPoints() {
- return this.movie.getFrequentPoints(this.daysRented);
- }
- }
- class Customer {
- constructor(name) {
- this._name = name;
- this._rentals = [];
- }
- get name() {
- return this._name;
- }
- get rentals() {
- return this._rentals;
- }
- addRental(rental) {
- this._rentals.push(rental);
- }
}- getFrequentPoints() {
- return this.rentals.reduce((frequentPoints, rental) => frequentPoints + rental.getFrequentPoints(), 0);
- }
- getTotalAmount() {
- return this.rentals.reduce((amount, rental) => amount + rental.getAmount(), 0);
- }
- }
Taken from the great book Refactoring: Improving the Design of Existing Code by Martin Fowler.
This kata is about practicing refactoring rhythm: small refactor, test, small refactor, test.
The task here is to refactor code (you can use a refactoring catalog) and compare your solution with others.
class RentalReport {
statement(customer) {
let totalAmount = 0;
let frequentRenterPoints = 0;
let result = "Rental Record for " + customer.name + "\n";
customer.rentals.forEach((each) => {
let thisAmount = 0;
//determine amounts for each line
switch (each.movie.priceCode) {
case MovieType.REGULAR:
thisAmount += 2;
if (each.daysRented > 2)
thisAmount += (each.daysRented - 2) * 1.5;
break;
case MovieType.NEW_RELEASE:
thisAmount += each.daysRented * 3;
break;
case MovieType.CHILDREN:
thisAmount += 1.5;
if (each.daysRented > 3)
thisAmount += (each.daysRented - 3) * 1.5;
break;
}
// add frequent renter points
frequentRenterPoints ++;
// add bonus for a two day new release rental
if ((each.movie.priceCode == MovieType.NEW_RELEASE) && each.daysRented > 1) frequentRenterPoints ++;
//show figures for this rental
result += "\t" + each.movie.title+ "\t" + thisAmount + "\n";
totalAmount += thisAmount;
});
//add footer lines
result += "Amount owed is " + totalAmount + "\n";
result += "You earned " + frequentRenterPoints + " frequent renter points";
return result;
}
}
const MovieType = {
CHILDREN: Symbol('CHILDREN'),
REGULAR: Symbol('REGULAR'),
NEW_RELEASE: Symbol('NEW_RELEASE'),
}
class Movie {
constructor (title, priceCode) {
this._title = title;
this._priceCode = priceCode;
}
get priceCode() {
return this._priceCode;
}
get title() {
return this._title;
}
}
class Rental {
constructor(movie, daysRented) {
this._movie = movie;
this._daysRented = daysRented;
}
get daysRented() {
return this._daysRented;
}
get movie() {
return this._movie;
}
}
class Customer {
constructor(name) {
this._name = name;
this._rentals = [];
}
get name() {
return this._name;
}
get rentals() {
return this._rentals;
}
addRental(rental) {
this._rentals.push(rental);
}
}
describe("given a rental report and movies", () => {
let rentalReport = new RentalReport();
let regularMovie = new Movie("regular movie", MovieType.REGULAR);
let releaseMovie = new Movie("release movie", MovieType.NEW_RELEASE);
let childrenMovie = new Movie("children movie", MovieType.CHILDREN);
describe("and a customer without rentals", () => {
let customer = new Customer("Forest");
it("should own 0 amount and earn 0 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"Amount owed is 0",
"You earned 0 frequent renter points"
].join("\n"));
});
});
describe("when rent a regular movie for 1 day", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(regularMovie, 1));
it("should own 2 amount and earn 1 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\tregular movie\t2",
"Amount owed is 2",
"You earned 1 frequent renter points"
].join("\n"));
});
});
describe("when rent a regular movie for 3 days", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(regularMovie, 3));
it("should own 3.5 amount and earn 1 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\tregular movie\t3.5",
"Amount owed is 3.5",
"You earned 1 frequent renter points"
].join("\n"));
});
});
describe("when rent a new release movie for 1 day", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(releaseMovie, 1));
it("should own 3 amount and earn 1 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\trelease movie\t3",
"Amount owed is 3",
"You earned 1 frequent renter points"
].join("\n"));
});
});
describe("when rent a new release movie for 2 days", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(releaseMovie, 2));
it("should own 6 amount and earn 2 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\trelease movie\t6",
"Amount owed is 6",
"You earned 2 frequent renter points"
].join("\n"));
});
});
describe("when rent a children movie for 1 day", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(childrenMovie, 1));
it("should own 1.5 amount and earn 1 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\tchildren movie\t1.5",
"Amount owed is 1.5",
"You earned 1 frequent renter points"
].join("\n"));
});
});
describe("when rent a children movie for 4 days", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(childrenMovie, 4));
it("should own 3 amount and earn 1 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\tchildren movie\t3",
"Amount owed is 3",
"You earned 1 frequent renter points"
].join("\n"));
});
});
describe("when rent 3 movies", () => {
let customer = new Customer("Forest");
customer.addRental(new Rental(regularMovie, 1));
customer.addRental(new Rental(releaseMovie, 1));
customer.addRental(new Rental(childrenMovie, 1));
it("should own 6.5 amount and earn 3 points", () => {
Test.assertEquals(rentalReport.statement(customer), [
"Rental Record for Forest",
"\tregular movie\t2",
"\trelease movie\t3",
"\tchildren movie\t1.5",
"Amount owed is 6.5",
"You earned 3 frequent renter points"
].join("\n"));
});
});
});
Game of Life :
The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.
Your task is to write a program to calculate the next generation of Conway's game of life, given any starting position.
You start with a two dimensional grid of cells, where each cell is either alive or dead.
The grid is finite, and no life can exist off the edges.
When calculating the next generation of the grid, follow these four rules:
- Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
- Any live cell with more than three live neighbours dies, as if by overcrowding.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any dead cell with exactly three live neighbours becomes a live cell.
Examples: * indicates live cell, . indicates dead cell
Example input: (4 x 8 grid)
........
....*...
...**...
........
Example output:
........
...**...
...**...
........
function nextGeneration(grid) {
return grid;
}
describe("Given empty grid", () => {
it("when next generation, should return empty", () => {
assert.deepEqual(nextGeneration([]), []);
});
});
describe("Given a single cell", () => {
it("when next generation, should die", () => {
assert.deepEqual(nextGeneration([
0,0,0,
0,1,0,
0,0,0,
]), [
0,0,0,
0,0,0,
0,0,0,
]);
});
});
given we are anonymous
then should say "Hello World!"
given we are "McFly"
then should say "Hello McFly!"
function sayHello() {
}
describe("given we are annonymous", () => {
it("should say hello world", () => {
Test.assertEquals(sayHello(), "Hello World!");
});
});