I use Seb Rose description as it is simple and efficient.
Given a letter, print a diamond starting with ‘A’ with the supplied letter at the widest point.
For example: print-diamond ‘C’ prints
I heard a lot about this kata. Recently1, it was the main subject of a two hours workshop at the awesome NCrafts conference.
So long time after the initial battle2, I decided to give it a try.
Well, as usual, when I’m told that something is difficult or impossible, I just don’t care and do it as I will have if no one told me anything.
So I apply TDD as I usually do: red, green, refactor. And that’s all.
Write a test, make it pass, clean the code. One step at a time.
the first test
So let’s start with the first test.
Test is Red, it’s time to make it go to green:
Yep, that’s a stupid implementation. But it works :)
And quite clean by itself. So let’s add another test.
a second test
And make it pass stupidly.
And it’s green, so it’s time to refactor.
But I can’t see duplication yet.
Well, there are twice the same “ A “ line in the ‘B’ diamond. But I can’t give a meaning to it. I probably don’t have enough data.
So, I’ll keep things like that for now, and add a third test.
and the third
As I try to be coherent, my way to make it green won’t differ from the previous one :)
Yep, again that is a stupid implementation.
Because at this moment I don’t care being clever, doing nice, clean things. At this moment I just want to be sure that the test I wrote is the one I intended to write.
By making it goes green with a simple solution, there are very few risks I introduced a mistake in the implementation. So when I see the test going from red to green, I can be confident on my test.
That’s my way to test my tests.
So now, I may have enough materials to see if I can make some interesting things appear.
time to start refactoring
There should be some logic in the letters and spaces distribution.
As I don’t understand it yet, I will artificially separate them. Yes, I’m going to create kind of artificial duplication.
Now I can notice that first and last lines are z spaces - A - z spaces.
If a diamond ‘A’ has a size of 1, ‘B’ of 2, ‘C’ 3 and so on, then z = (size - 1)
Great, lets put that logic in a method.
I also notice that other lines follow the same pattern : x spaces - a char - y spaces - same char - x spaces
So lets make that appear in the code.
There is definitely something that tie everything, but i don’t see it yet.
a fourth test
Let’s add the ‘D’ diamond. I won’t show you the test, it the same than previous but with a ‘D’ diamond.
Make it pass by adding this in our current solution.
From now, I will only show you the refactoring on the ‘D’ diamond.
All those integers must have some kind of relationship. Maybe we can figure it by rearranging them.
Let’s introduce the width of the diamond.
I draw some diamond on a paper and manage to figure quite rapidly that the width is size * 2 - 1
Well, I think I got it. Let make appear a floor level.
Yes, every lines really look like the same now, we can give it a name.
That’s better, but we are not done yet.
There is a pattern in these calls. Let’s see what happend if we renumber floors so they become a sequence, from +size to -size
Well, there seems to have a loop hidden there. The problem to introduce it are those characters in the parameters of diamondWall call.
But we can easily get rid of them as the character used in a floor can be compute from the size of the diamond and the current floor.
As I usually do I’ve made things with very small steps in order to keep my test green all the time.
I tried to let the algorithm emerge from the refactoring, and not the opposite.
Of course, it did not emerge by itself. With that kind of problem, I had in mind that there will be some kind of loop in the solution. So my refactorings aimed to make that loop appears: introducing deliberate duplication so that each line looks the same, then unifying indices.
Introducing one change at a time allow my test to be very efficient in helping me to find the errors I’ve made4.
The full code is available on my github. Look in the branches :)5
Once there, if it was real code, I will probably give name to size -2 and -(size - 2). Maybe attic and cellar to stick with the building metaphore. And size should probably become height.
I also don’t really like the use of a Character everywhere will in fact it is a kind of diamond. So i’ll introduce a DiamondKind class to hold the Character and size6, width calculation. Maybe other Character operations too7.