A change of server
I’m having trouble with watchify and watch-http-server because things don’t seem to be building.
I’ve since learned that it was because I was using npm-run-all when I should’ve been using run-p
Despite that, it’s time to replace watchify and watch-http-server with browserify and budo instead.
> npm uninstall --save-dev watchify watch-http-server
> npm install --save-dev browserify budo
and replace the build/start/watch scripts with:
"start": "budo index.js:bundle.js --live",
"watch": "run-p test start"
We can now run the live testing and bundling server
> npm run watch
and load the test page at localhost:9966
The first test is fairly easy, being for an A diamond.
test("An A diamond is just the letter A", function () {
expect(diamond.create("A")).toEqual(["A"]);
});
In the code we’ll check for an empty parameter, and if it’s not empty we’ll give “A” in an array.
create: function (char) {
if (!char) {
return [];
}
return ["A"];
}
Commit that code
> npm status
> npm add *.js
> npm commit -m "Diamond A"
The next test is for a B diamond, which is a bit more complex.
test("The B diamond is a bit more complex", function () {
expect(diamond.create("B")).toEqual([" A ", "B B", " A "]);
});
As usual, we use simple techniques to make it pass, before refactoring to better code.
if (char === "A") {
return ["A"];
}
return [
" A ",
"B B",
" A "
];
This passes the test, but the output on the screen needs working on.
Putting it in a <pre>
tag helps to preserve the spaces
<pre class="results"></pre>
and I’ll update index.js so that it tries to show us a diamond of size Z
results.innerHTML = diamond.create("Z").join("<br>");
Commit this code.
> git status
> git add index.html *.js
> git commit -m "Diamond B"
Before moving on to the next test, we’d better see how we can improve on the current code. Let’s start by defining an array to store the diamond.
create: function (char) {
var diamond = [];
if (!char) {
return diamond;
}
if (char === "A") {
diamond = ["A"];
return diamond;
}
diamond = [
" A ",
"B B",
" A "
];
return diamond;
Instead of adding A, I want to add the char that’s passed to the function.
if (char === "A") {
diamond = [char];
return diamond;
}
diamond = [
" A ",
char + " " + char,
" A "
];
I’m not sure how to simplify things further, so will use Diamond C to help figure that out.
test("The C diamond", function () {
expect(diamond.create("C")).toEqual([" A ", " B B ", "C C", " B B ", " A "]);
});
The code to make this pass isn’t pretty, but should help to provide a good direction for refactoring:
if (char === "B") {
diamond = [
" A ",
char + " " + char,
" A "
];
return diamond;
}
diamond = [
" A ",
" B B ",
char + " " + char,
" B B ",
" A "
];
return diamond;
> git status
> git add index.html *.js
> git commit -m "Diamond C"
I’m seeing a pattern develop, where we can start from the middle and then add on lines to the begin and end.
diamond.push(char + " " + char);
diamond.unshift(" B B ");
diamond.push(" B B ");
diamond.unshift(" A ");
diamond.push(" A ");
I want to use a separate function called charLine that creates a line for each character, but to use another function I’ll need to reorganise the current code.
module.exports = (function iife() {
"use strict";
function create(char) {
var diamond = [];
...
return diamond;
}
return {
create
};
}());
I can now add other functions and easily use them.
function charLine(char) {
return char + " " + char;
}
...
diamond.push(charLine(char));
Now for each character, it will have a different number of spaces between them.
A has no spaces and only 1 A
B has 1 space between
C has 3 spaces
D has 5 spaces
So if n=0 for A, 1 for B, 2 for C, and 3 for D, the number of spaces between is 2n-1
function charLine(char) {
var codeForA = String("A").charCodeAt(0);
var n = char.charCodeAt(0) - codeForA;
return char + " ".repeat(2 * n - 1) + char;
}
...
diamond.push(charLine(char));
diamond.unshift(" " + charLine("B") + " ");
diamond.push(" " + charLine("B") + " ");
diamond.unshift(" " + "A" + " ");
diamond.push(" " + "A" + " ");
return diamond;
With a prevChar() function, the B and C code are getting closer to being identical:
function prevChar(char) {
var charCode = char.charCodeAt(0);
if (char === "A") {
return;
}
return String.fromCharCode(charCode - 1);
}
...
if (char === "B") {
diamond.push(charLine(char));
char = prevChar(char);
diamond.unshift(" " + charLine(char) + " ");
diamond.push(" " + charLine(char) + " ");
return diamond;
}
diamond.push(charLine(char));
char = prevChar(char);
diamond.unshift(" " + charLine(char) + " ");
diamond.push(" " + charLine(char) + " ");
char = prevChar(char);
diamond.unshift(" " + charLine(char) + " ");
diamond.push(" " + charLine(char) + " ");
return diamond;
I’d better commit at this stage:
> git status
> git add *.js
> git commit -m "Using charLine() and prevChar() functions"
The spaces are the next thing to simplify. What if we just use an empty string for the spaces, and add a space to it each time?
var diamond = [];
var spaces = "";
...
if (char === "B") {
diamond.push(charLine(char));
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
return diamond;
}
diamond.push(charLine(char));
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
return diamond;
We can now see that a while loop will easily take care of the code:
if (char === "B") {
diamond.push(charLine(char));
while (char !== "A") {
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
}
return diamond;
}
diamond.push(charLine(char));
while (char !== "A") {
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
}
return diamond;
The B and C sections of code are now identical, so the B section of code can now be deleted:
// if (char === "B") {
// diamond.push(charLine(char));
// while (char !== "A") {
// char = prevChar(char);
// spaces += " ";
// diamond.unshift(spaces + charLine(char) + spaces);
// diamond.push(spaces + charLine(char) + spaces);
// }
// return diamond;
// }
diamond.push(charLine(char));
while (char !== "A") {
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
}
return diamond;
The “A” if statement can also now be removed:
// if (char === "A") {
// diamond.push(char);
// return diamond;
// }
diamond.push(charLine(char));
while (char !== "A") {
char = prevChar(char);
spaces += " ";
diamond.unshift(spaces + charLine(char) + spaces);
diamond.push(spaces + charLine(char) + spaces);
}
return diamond;
And commit this code:
> git status
> git add *.js
> git commit -m "Refactoring to a while loop with an expanding space"
And the webpage now shows the full Z-sized diamond.
Diamond kata - 2017-12-31
A
B B
C C
D D
E E
F F
G G
H H
I I
J J
K K
L L
M M
N N
O O
P P
Q Q
R R
S S
T T
U U
V V
W W
X X
Y Y
Z Z
Y Y
X X
W W
V V
U U
T T
S S
R R
Q Q
P P
O O
N N
M M
L L
K K
J J
I I
H H
G G
F F
E E
D D
C C
B B
A
Push the code, and that brings an end to this kata
> git push