Problem given at the event :
Given a letter print a diamond starting
with 'A' with the supplied letter at the widest point.
Development Approach we need to practice : "Test Driven Development"
During the coderetreat event conducted by
Johannes Brodwall we practiced the same problem over and over with different devs with different approaches. In this post what I am trying to summarize the things I got through out this valuable event.
The way I looked at the the problem is more biased in to a geometrical view. My observation is that we can put our elements in a 2D Cartesian space as follows. Following is the example for Letter C.
red dot - Letter A
green dot - Letter B
blue dot - Letter C
So if we can find the equation of the line XY then we can find the points by substituting Y=0,1,2 (We can write a simple logic to get the Y's range)
Equation for line XY : Y = -X +2
by substituting y values we can get following points.
(0,2),( 1,1) and (2,0)
Similarly you can obtain the equation for line LM.
Equation for line LM : Y = -X -2
by substituting y values we can get following points.(y=0,-1,-2)
(0,-2), (-1,-1) and (-2,0)
Equation for line XM : Y = X
-2
by substituting y values we can get following points.(y=0,-1,-2)
(0,-2),(1,-1) and (2,0)
Equation for line LY : Y = X +2
by substituting y values we can get following
points.(y=0,-1,-2)
(0,-2),(1,-1) and (2,0)
If you are familiar with the absolute value function you can write above functions with a single function as follows.
| X | + | Y | = 2
so our task has reduced to find a simple liner equation for a given charactor.
So the general form of the equation is |X| + |Y| = c
When it comes to TDD approach first thing we need to do is write a simple test against our business logic which gives an assertion failure.
Test should be smaller enough to fail.
Following are the test cases I wanted to cover :
1). Find the interceptor of the equation for a given input character :
@Test
public void test_the_intercept_of_the_line() {
Diamond diamond = new Diamond();
Assert.assertEquals(0, diamond.getInterceptor('A'));
Assert.assertEquals(1, diamond.getInterceptor('B'));
Assert.assertEquals(2, diamond.getInterceptor('C'));
}
Source for this test case is simple as follows.
public class Diamond {
public int getInterceptor(char c) {
return c-65;
}
}
In this case what you need to make sure is
just write enough code which is capable of passing you test case.
2) As for my second test scenario I used Input character 'B' and test the points list where I need to print characters.
@Test
public void test_output_diamond_for_input_charactor_B() {
Diamond diamond = new Diamond();
Set pointSet = diamond.getPointSet('B');
Assert.assertEquals(true, pointSet.contains(new Point(0, 1)));
Assert.assertEquals(true, pointSet.contains(new Point(0, -1)));
Assert.assertEquals(true, pointSet.contains(new Point(1, 0)));
Assert.assertEquals(true, pointSet.contains(new Point(-1, 0)));
Assert.assertEquals(false, pointSet.contains(new Point(-1, -1)));
}
In order to pass the test case I used following algorithm which i was planing to apply in above explanation.(Implementation of absolute value function with +/- notations)
public class Diamond {
public int getInterceptor(char c) {
return c - 65;
}
public Set getPointSet(char c) {
Set pointsSet = new HashSet();
int interceptor = getInterceptor(c);
for (int yIndex = interceptor; yIndex >= 0; yIndex--) {
int xIndex = interceptor - yIndex;
pointsSet.add(new Point(xIndex, yIndex));
pointsSet.add(new Point(xIndex, -yIndex));
pointsSet.add(new Point(-xIndex, yIndex));
pointsSet.add(new Point(-xIndex, -yIndex));
}
return pointsSet;
}
}
Since I wanted to apply some OOP concepts I created Point class and implement the constructor which we can pass X,Y coordinates. And also I implement the hash code , toString and equals methods in the Point class.(This can be generated by using eclipse IDE). In addition to that I am going to store relevant character with that point object as follows.
public Point(int i, int j) {
this.setX(i);
this.setY(j);
this.setCharacter((char)(Math.abs(j)+65));
}
3) During the workshop I heard that some guys were taking about write test cases to figure out whether the points are located with in the boundary. But in this approach we don't need such kind of a test since we are substituting the correct range of Y values. That range is indirectly tested by test_interceptor_of_the_line method. I found that it is not compulsory to write a test for figure out the boundary of the grid.
At this point I know my algorithm is working properly. And then I need to print the character pattern in to the console output. In order to do so I just had to write following simple logic.
public void printDiamond(char c) {
int interceptor = getInterceptor(c);
int gridArea = 2 * interceptor + 1;
// Create an array with all 'X' values
char[][] diamond = new char[gridArea][gridArea];
for (char[] ds : diamond) {
for (int i = 0; i < ds.length; i++) {
ds[i] = 'X';
}
}
Set pointSet = getPointSet(c);
for (Point point : pointSet) {
diamond[point.getX() + interceptor][point.getY() + interceptor] = point
.getCharacter();
}
for (int row = 0; row < gridArea; row++) {
for (int column = 0; column < gridArea; column++) {
System.out.print(String.valueOf(diamond[row][column]));
}
System.out.println();
}
}
It will gives the expected diamond for a given input character.
Since my tests are passing I thought of having some refactoring to Dimond class. for and example I do not need to expose getInterceptor method to public. So i can make it private.
Another self finding of this workshop is that we should think the test case as a opposition party and always write the test cases which will give the maximum pain for the source code end. (Class under Test). At the same time you need to make sure to write a simple test which gives enough potential to fail.
So its about how to treat our code. Overall it was a wonderful event and I could deal with the people who has different kind of mind sets. At the end of the day what I had is a green color sticky note which says that you should practice some code kata with TDD until you will get the right skill. Kudos for Johannes.
Note : In the case where you need to have a closer look in to the code you can get it from
here.