Poetry Magnets: Third Verse
Charlie Poole
December, 2005

Jagged Lines

In the first and second articles of this series, we solved all the easiest parts of the problem. We can now display correct text for single or multiple lines of text, subject to certain constraints...

Neither of these are likely to be true - they certainly haven't been for most refrigerator magnets I've seen!

In this article, we'll try to remove the second constraint. We'll allow the magnets to be placed in jagged lines, so long as there is sufficient overlap with other magnets so that a human reader would see them as a single line. We don't know exactly what that means yet, but we'll figure it out as we write the tests.

In order to express the concept of "overlap" in the vertical dimension, the magnets must have a height. Since we ordained at the start that all magnets are the same height, the size we pick is irrelevant. More precisely, it's a matter of scaling. Since we started out using integers as positions, I'm picking 100 as the standard height of a magnet. That will allow me to think about my tests as expressing some percentage of overlap, but it's only for my own convenience.

Here's a first test...

[TestFixture]
public class MagnetTests
{
    ...

    [Test]
    public void TwoMagnetsWithVerticalOverlap()
    {
        magnets.Add( new Magnet( 1000, 700, "Hello" ) );
        magnets.Add( new Magnet( 2000, 720, "World" ) );
        Assert.AreEqual( "Hello World", magnets.Text );
    }
}

I scaled up the X values to look right with the Y values, even though this isn't strictly necessary - our code so far doesn't make any use of them except for sorting. To keep the old tests valid, I multiplied all the coordinates by 100 as well.

Running the test above, we're happy to see a red bar! We like our tests to start out failing, so we can have the fun of making them pass. The error message is...

expected: "Hello World"
 but was: "Hello\nWorld"

Unfinished Business

My first cut at making this test pass, was to revise the CompareTo method used in the sort. Here's one version...

public class Magnet : IComparable
{
    ...
    int IComparable.CompareTo(object other)
    {
        Magnet m = (Magnet)other;
		
		if ( Math.Abs( this.Y - m.Y ) < 100 )
		   return this.X.CompareTo(m.X);
		   
		return this.Y.CompareTo(m.Y);
    }
}

I revised this several ways and finally tested the sort. It worked! But the same error message - the one with an extra '\n' in the output - continued to appear. As with many such problems, the cause was obvious when I finally saw it. It's these lines in the MagneticSurface.Text property...

    if ( lastY != mag.Y )
        sb.Append( '\n' );
    else
        sb.Append( ' ' );

Even if two magnets are on the same line, we insert a newline if the Y coordinates are different!

We can fix this, but it seems important to reflect on how it happened first. The original code contains duplication, which I failed to notice and remove. The concept of two magnets being on the same line is expressed in two different ways in the code. Let's fix that...

public class Magnet : IComparable
{
    ...
    int IComparable.CompareTo(object other)
    {
        Magnet m = (Magnet)other;
		
		if ( this.InSameLineAs( m ) )
		   return this.X.CompareTo(m.X);
		   
		return this.Y.CompareTo(m.Y);
    }
	
    private bool InSameLineAs( Magnet other )
    {
        return Math.Abs( this.Y - m.Y ) < 100 );
    }
}