Pretty banner! :)

For-Loop Defeats Copy/Paste

Copy/pasted code is evil. It can get you into all sorts of trouble. There are a lot of situations in which you might be tempted to perform the dreaded ctrl-c + ctrl-v. You must resist this urge! In certain situations you can write code that is significantly more robust than copy/pasted code and you can probably type it up quicker too.

One case where you might be tempted to copy/paste code is when you’re performing some logic on a group of related objects. In the following example, we test the neighbors of our tile to see if we are surrounded by sea. This technique can be applied effectively to many other scenarios, however, like initializing a set of buttons, or aligning a bunch of logos.

var isSurroundedBySea : Boolean = false;

var leftTile : Tile = map.getTile( m_currentX - 1, m_currentY );
if( leftTile != null && !leftTile.isSea() )
{
    isSurroundedBySea = false;
}

var rightTile : Tile = map.getTile( m_currentX + 1, m_currentY );
if( rightTile != null && !rightTile.isSea() )
{
    isSurroundedBySea = false;
}

var upTile : Tile = map.getTile( m_currentX, m_currentY - 1 );
if( upTile != null && !upTile.isSea() )
{
    isSurroundedBySea = false;
}

var downTile : Tile = map.getTile( m_currentX, m_currentY + 1 );
if( downTile != null && !downTile.isSea() )
{
    isSurroundedBySea = false;
}

Copy/pasting has bloated the size of the code and made it incredibly brittle. If we need to modify the basic logic of the query (like perhaps testing for a modifier called “Sea” instead), we have to hope we remember to do it in all 4 places, and to do it correctly.

Never fear, though, Mr. For-Loop is here to save us. What I like to do in this particular case is plug all the objects into an array and then use a for-loop to make sure the code is written only once:

var isSurroundedBySea : Boolean = false;

var neighbors : Array = [
    map.getTile( m_currentX - 1, m_currentY ),
    map.getTile( m_currentX + 1, m_currentY ),
    map.getTile( m_currentX, m_currentY + 1),
    map.getTile( m_currentX, m_currentY - 1 ) ];

for( var n : int = 0; n < neighbors.length; n++ )
{
    var neighbor : Tile = neighbors[ n ];
    if( neighbor != null && !neighbor.isSea() )
    {
        isSurroundedBySea = false;
        break;
    }
 }

Not only is it faster to write this code than it is to copy/paste all that junk up there, the code is significantly more robust because we’ve converted 4 potential sources of failure into (basically) one.

You have to be able to write bug free for-loops to use this trick effectively. But I’m pretty sure you can handle that. :)

Post Metadata

Date
April 23rd, 2009

Author
urbansquall

Category


5 Comments

  1. I have a feeling you were thinking of me when you wrote this :D



  2. Snurre

    You’re absolutely right. I wonder why you use ‘n’ instead of ‘i’ as the index, but i guess that because of the neighbors?

    Be sure to see this post http://blog.vortixgames.com/factory for more info on patterns.


  3. @Colby: Haha.. :)

    @Snurre: I used ‘n’ because colliding indexes are more trouble then they are worth. :)


  4. Instead of var neighbors : Array = [ map.getTile( m_currentX - 1, m_currentY ), map.getTile( m_currentX + 1, m_currentY ), map.getTile( m_currentX, m_currentY + 1), map.getTile( m_currentX, m_currentY - 1 ) ];

    you’re may be even better off with var neighborCoords : Array = [ {x:-1, y:0}, {x:1, y:0}, {x:0, y:-1}, {x:0, y:1} ];

    for( var n : int = 0; n < neighbors.length; n++ ) { var coord: Object = neighborCoords[n]; var neighbor : Tile = map.getTile( m_currentX + coord.x, m_currentY + coord.y );

    You repeat yourself even less, this way.



Leave a Reply