DaedTech

Stories about Software

By

Chess TDD 14: Figuring Out Blocking Pieces

In this installment, I’m looking to start defining the concept of “blocking” on a chess board. So far, I’ve been defining piece moves in a vacuum — put a rook down in (1, 1) and the rook can go anywhere in column 1 or row 1. But, what about when there’s a pawn at (1, 2)? That’s what’s on the docket in this episode.

Here’s what I accomplish in this clip:

  •  Started implementing the concept of pieces “blocking” one another.
  • Expanded the blocking logic to include horizontal and vertical blocking in both positive and negative directions.
  • Realized I’d been wrong about how Enumerable.Range() worked for a good long time because I had always used it for 0 indexed number generation, which masked my wrongness.

And here are some lessons to take away:

  • If you’re anything like me, sometimes you’ll thrash a bit between micro design decisions.  I introduced the concept of BoardCoordinate to avoid passing (int x, int y) to every method under the sun, but then I started to get annoyed by the ceremony of declaring new BoardCoordinate(x, y) everywhere.  Result was (for now) to implemented BoardCoordinate.For(x, y) which struck me as at least slightly more readable and elegant.  Point is, you probably won’t hit your most simple/elegant design on your first, second, or even third crack.  Keep at it, don’t be afraid to experiment, and solicit opinions and advice.  (Speaking of which, anyone with a good idea for this, please chime in!)
  • “Do the simplest thing you can to make the test pass” is not just a function of what you do in the production code but also of what test you’ve written.  In other words, make sure you don’t write an overly ambitious test like “test_that_all_end_user_requirements_are_satisfied” or the simplest thing you can do to make it pass will be really complicated.
  • TDD is the ultimate in “fail fast.”  I wasted about a minute of my time (twice) and a minute of yours (once) because I couldn’t figure out that I was calling Enumerable.Range(int, int) with the wrong second parameter.  But, seeing NCrunch’s dots turn red told me something was wrong very, very quickly while what I did was still fresh in my mind.  I didn’t know I was misremembering the method signature, but I did know that something about Enumerable.Range() in there was breaking things.  Without TDD, I’d have gone merrily on writing code for minutes or even hours until I fired up the whole application and wondered why the blocked rook could randomly move to one spot on the board in front of the opponent’s pawns or something.  I’d have wasted a lot more of both of our time trying to hunt down the problem then, when I had no clue which line of code was the offender.
  • There are times when the code smell of duplication or duplicate effort appears, but the solution isn’t immediately obvious (such as the case in this video where I was doing similar things for horizontal and vertical).  It’s my experience that you can find yourself in a blind alley when you try to abstract this to a common method where it becomes a snarl of conditionals or very dense, opaque mathematical logic.  Eliminating duplication is extremely important, but beware of times when you have “duplication elimination fools’ gold” and the gains in readability/maintainability actually go into the negative.  Be sure that your abstraction is actually making things simpler.
  • I didn’t like a number of things about this code when the clock started getting pas the 20 minutes mark and I felt the pressure not to make this a horribly long episode.  It might not be the same reason, but there are going to be times when you’re forced to get up and leave for the time being or for the day when you’re not thrilled with the code.  It’s okay — you’ll have another crack at it tomorrow with a fresh mind, and the TDD will help you pick right up where you left off.

By

ChessTDD 12: Blind Alleys

This is one last post of loose ends and looking at what I’ve done so far before I move on to new functionality.  It wasn’t necessarily my finest hour as a programmer and TDD practitioner, but I’m including the good with the bad because nobody is always on when programming.  Also, I wasn’t insanely on in terms of my audio recording either becasue my throat was a little scratchy today.  You might hear me stealing quiet sips of ice water from time to time.  Anyway, here’s what I accomplish:

  • Reworked the knight’s GetMovesFrom() method
  • A fair amount of design flailing

Here are some lessons to take away:

  • An interesting side effect of a long time spent at TDD is a tendency to look to unit tests when wanting to dive into and explore code functionality.  The reason for this is that you know the unit tests will show you how the class’s public API is being used.  So, it’s like finding the class instantiated in-situ in the code, except that in the test class, it’s going to have a bunch of methods with descriptive names telling you how things work.
  • When doing exploratory refactorings, try to keep things compiling as much as you can and check with as much granularity as possible that you’re still passing.  Some of my flailing with tuples of ints wasn’t really necessary, but I kept my eye on the tests anyway so as not to get stuck in the middle of a refactoring where tests are red, things are broken, and I don’t know how to go forward or back.
  • Check out around 9:50.  Copy and paste will always burn you.  Even when it’s tiny.  Even when you’re using TDD.
  • Sometimes you wind up in a blind alley.  That’s okay.  Go back to where things were working and tackle the problem again later.  This isn’t just punting; it’s an important skill.  You need to ship code and you can get lost in trying to perfect it if you aren’t careful.  So, let it go, think on it later — in the shower or while eating dinner or something — the answer will smack you in the forehead like a ton a bricks at some point.

By

Recall, Retrieval, and the Scientific Method

Improving Readability with Small Things

In my series on building a Chess game using TDD I’ve defined a value type called BoardCoordinate that I introduced instead of passing around X and Y coordinate integer primitives everywhere. It’s a simple enough construct:

public struct BoardCoordinate
{
    private readonly int _x;
    public int X { get { return _x; } }

    private readonly int _y;
    public int Y { get { return _y; } }

    public BoardCoordinate(int x, int y)
    {
        _x = x;
        _y = y;
    }

    public bool IsCoordinateValidForBoardSize(int boardSize)
    {
        return IsDimensionValidForBoardSize(X, boardSize) && IsDimensionValidForBoardSize(Y, boardSize);
    }

    private static bool IsDimensionValidForBoardSize(int dimensionValue, int boardSize)
    {
        return dimensionValue > 0 && dimensionValue <= boardSize;
    }
}

This was a win early on the series to get me away from a trend toward Primitive Obsession, and I haven't really revisited it since. However, I've found myself in the series starting to think that I want a semantically intuitive way to express equality among BoardCoordinates. Here's why:

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_1_2_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 1 && bc.Y == 2));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_2_2_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 2 && bc.Y == 2));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Returns_3_3_For_1_1()
{
    Assert.IsTrue(MovesFrom11.Any(bc => bc.X == 3 && bc.Y == 3));
}

[TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
public void Does_Not_Return_0_0_From_1_1()
{
    Assert.IsFalse(MovesFrom11.Any(bc => bc.X == 0 || bc.Y == 0));
}

This is a series of unit tests of the "Queen" class that represents, not surprisingly, the Queen piece in chess. The definition of "MovesFrom11" is elided, but it's a collection of BoardCoordinate that represents the possible moves a queen has from piece 1, 1 on the chess board.

This series of tests was my TDD footprint for driving the functionality of determining the queen's moves. So, I started out saying that she should be able to move from (1,1) to (1,2), then had her also able to move to (2,2), etc. If you read the test, what I'm doing is saying that this collection of BoardCoordinates to which she can move should have in it one that has X coordinate of 1 and Y coordinate of 2, for instance.

What I don't like here and am making mental note to change is this "and". That's not as clear as it could be. I don't want to say, "there should be a coordinate in this collection with X property of such and such and Y property of such and such." I want to say, "the collection should contain this coordinate." This may seem like a small semantic difference, but I value readability to the utmost. And readability is a journey, not a destination -- the more you practice it, the more naturally you'll write readable code. So, I never let my foot off the gas.

During the course of the series, this nagging readability hiccup has caused me to note and refer to a TODO of implementing some kind of concept of equals. In the latest post, Sten asks in the comments, referring to my desire to implement equals, "isn't that unnecessary since structs that doesn't contain reference type members does a byte-by-byte in-memory comparison as default Equals implementation?" It is this question I'd like to address here in this post.

Not directly, mind you, because the assessment is absolutely spot on. According to
MSDN:

If none of the fields of the current instance and obj are reference types, the Equals method performs a byte-by-byte comparison of the two objects in memory. Otherwise, it uses reflection to compare the corresponding fields of obj and this instance.

So, the actual answer to that question is simply, "yes," with nothing more to say about it. But I want to provide my answer to that question as it occurred to me off the cuff. I'm a TDD practitioner and a C# veteran, for context.

Answering Questions on Language Knowledge

My answer, when I read the question was, "I don't remember what the default behavior of Equals is for value types -- I have to look that up." What surprised me wasn't my lack of knowledge on this subject (I don't find myself using value types very often), but rather my lack of any feeling that I should have known that. I mean, C# has been my main language for the last 4 years, and I've worked with it for more years than that besides. Surely, I just failed some hypothetical job interview somewhere, with a cabal of senior developers reviewing my quiz answers and saying, "for shame, he doesn't even know the default Equals behavior for value types." I'd be laughed off of stack overflow's C# section, to be certain.

And yet, I don't really care that I don't know that (of course, now I do know the answer, but you get what I'm saying). I find myself having an attitude of "I'll figure things out when I need to know them, and hopefully I'll remember them." Pursuing encyclopedic knowledge of a language's behavior doesn't much interest me, particularly since those goalposts may move, or I may wind up coding in an entirely different language next month. But there's something deeper going on here because I don't care now, but that wasn't always true -- I used to.

The Scientific Method

When I began to think back on this, I think the drop off in valuing this type of knowledge correlated with my adoption of TDD. It then became obvious to me why my attitude had changed. One of the more subtle value propositions of TDD is that it basically turns your programming into an exercise in the Scientific Method with extremely rapid feedback. Think of what TDD has you doing. You look at the code and think something along the lines of, "I want it to do X, but it doesn't -- why not?" You then write a test that fails. Next, you look at the code and hypothesize about what would make it pass. You then do that (experimentation) and see if your test goes green (testing). Afterward, you conduct analysis (do other tests pass, do you want to refactor, etc).

ScientificMethod

Now you're probably thinking (and correctly) that this isn't unique to TDD. I mean, if you write no unit tests ever, you still presumably write code for a while and then fire up the application to see if it's doing what you hypothesized that it would while writing it. Same thing, right?

Well, no, I'd argue. With TDD, the feedback loop is tight and the experiments are more controlled and, more importantly, isolated. When you fire up the GUI to check things out after 10 minutes of coding, you've doubtless economized by making a number of changes. When you see a test go green in TDD, you've made only one specific, focused change. The modify and verify application behavior method has too many simultaneous variables to be scientific in approach.

Okay, fine, but what does this have to do with whether or not I value encyclopedic language knowledge? That's a question with a slightly more nuanced answer. After years of programming according to this mini-scientific method, what's happened is that I've devalued anything but "proof is in the pudding" without even realizing it. In other words, I sort of think to myself, "none of us really knows the answer until there's a green test proving it to all of us." So, my proud answer to questions like, "wouldn't it work to use the default equals method for value types" has become, "dunno for certain, let's write a test and see."

False Certainty

Why proud? Well, I'll tell you a brief story about a user group I attended a while back. The presenter was doing a demonstration on Linq, closures, and deferred execution and he made the presentation interactive. He'd show us methods that exposed subtle, lesser known behaviors of the language in this context and the (well made) point was that these things were complex and trying to get the answers right was humbling.

It's generally knowledgeable people that attend user groups and often even more knowledgeable people that brave the crowd to go out on a limb and answer questions. So, pretty smart C# experts were shouting out their answers to "what will this method return" and they were getting it completely wrong because it was hard and it required too much knowledge of too many edge cases in too short a period of time. A friend of mine said something like, "man, I don't know -- slap a unit test on it and see." And... he's absolutely right, in my opinion. We're not language authors, much less compilers and runtimes, and thus the most expedient answer to the question comes not from applying amassed language knowledge but from experimentation.

Think now of the world of programming over the last 50 years. In times where compiles and executions were extremely costly or lengthy, you needed to be quite sure that you got everything right ahead of time. And doing so required careful analysis that could only be done well with a lot of knowledge. Without prodigious knowledge of the libraries and languages you were using, you would struggle mightily. But that's really no longer true. We're living in an age of abundant hardware power and lightning fast feedback where knowing where to get the answers quickly and accurately is more valuable than knowing them. It's like we've been given the math textbook with the answers in the back and the only thing that matters is coming up with the answers. Yeah, it's great that you're enough of a hotshot to get 95% of the answers right by hand, but guess what -- I can get 100% of them right and much, much faster than you can. And if the need to solve new problems arises, it's still entirely possible for me to work out a good way to do it by using the answer to deduce how the calculation process works.

Caveats

In the course of writing this, I can think of two valid objections/comments that people might have critiquing what I'm saying, so I'd like to address them. First of all, I'm not saying that you should write production unit tests to answer questions about how the framework/language works. Unit testing the libraries and languages that you use is an anti-pattern. I'm talking about writing tests to see how your code will behave as it uses the frameworks and languages. (Although, a written and then deleted unit test is a great, fast-feedback way to clarify language behavior to yourself.)

Secondly, I'm not devaluing knowledge of the language/framework nor am I taking pride in my ignorance of it. I didn't know how the default Equals behavior worked for value types yesterday and today I do. That's an improvement. The reason it's an improvement is that the knowledge is now stored in a more responsive cache. I maintain having the knowledge is trumped by knowing how to acquire it, and I look at reaching into my own personal memory stores as like having it in a CPU cache versus the memory of writing a quick test to see versus the disk space location of looking it up on the internet or asking a friend.

The more knowledge you have of the way the languages and frameworks you use work, the less time you'll have to sink into proving behaviors to yourself, so that's clearly a win. To continue the metaphor, what I'm saying is that there's no value or sense in going out preemptively and loading as much as you can from disk into the CPU cache so that you can show others that it's there. In our world, memory and disk lookups are just no longer expensive enough to make that desirable.

By

Chess TDD 11: Tying Up Loose Ends

Now that all of the pieces are implemented, I decided to change it up a little and get rid of a few yellow items in one fell swoop. I thought it would also be a good time to
Here’s what I accomplish in this clip:

  • Move Knight into it’s own class.
  • Separated test and production into their own assemblies.
  • Got rid of the DefaultBoardSize constant that had been defined in many test classes by consolidating it into one place.
  • Went back to earlier test classes and implemented “MovesFromXX” pattern that I had settled on.

Here are some lessons to take away:

  • If Ctrl-M, O stops working (or any normal VS shortcut), it might be because some other process has claimed part or all of the sequence and is preempting Studio.  Kill processes or reboot.
  • If you have some mundane but necessary tasks (e.g. moving a bunch of classes into a new namespace), this can be a good way to “warm up” when you haven’t looked at a code base in a while.  Generally speaking, look for tasks that can ease you back into the code base while being productive.
  • Even duplication that is seemingly mundane should be avoided.  The DRY principle states that systems should have a single point of definition for all pieces of knowledge, and that includes things like database schemas, config files, and yes, even your unit test classes.  You need to maintain your unit tests, and duplication creates maintenance pain.
  • Pay attention to your test method names as you’re making changes.  These are documentation as to how your code should behave, so if you change the behavior, make sure to change the names to reflect those changes.
  • “Lean on the compiler” to help you with your refactorings.  If you’re getting rid of a bunch of references to a variable, delete the definition of the variable and the compiler will then force you to get rid of all references.  If you do it in the opposite order, you might miss references and waste time.  As a more general rule, you should always favor failing early and doing things in such a way that mistakes will result in non-compiling.
  • When you do large refactorings, it might not hurt to run all of the unit tests explicitly in the test runner instead of relying on the dots in NCrunch (or any continuous testing tool that you’re using).

By

Chess TDD 10: A Knight’s Tale

Okay, I promise, no more of these titles after this edition.  But, to make up for it, this time I actually wrote the code and did the audio in the same day, so I remember very well what I was talking about.

Here’s what I accomplish in this clip:

  • Move Queen into it’s own class.
  • Implement Knight with GetMovesFrom()

Here are some lessons to take away:

  • It’s okay to be obtuse for a while in your TDD, if doing so helps your understanding. I started with a hard-coded thing to get the first test pass and then did another for the second. Both tests would need to continue passing, and I was feeling my way through what to do. As long as you get going at some point, this is okay. Whatever helps.
  • Try to avoid getting into a routine. By getting a fresh look, I was able to define a method that I thought improved readability and aided code use. I’d written a number of things like this before, but I always try to think of new ways to approach problems, rather than settling into a routine of semi-mindless work.
  • If you’re not careful, you can get a little ahead of yourself. I was on auto-pilot and didn’t realize I wasn’t compiling for a while before I corrected myself.
  • Get it working first, and then worry about cleanup later. You can always make things more elegant later, when it’s working (at least when you do TDD).