public TrackEnds[] getTrackEnds()
        {
            TrackEnds[] result = new TrackEnds[4];
            for (TrackSide side = TrackSide.North;
                 side <= TrackSide.West; side++)
            {
                result[(int)side] = getEndOnSide(side);
            }
#if DEBUG
            //sanity check!
            int aCount = 0;
            int bCount = 0;
            foreach (var item in result)
            {
                if (item == TrackEnds.A)
                {
                    aCount++;
                }
                else if (item == TrackEnds.B)
                {
                    bCount++;
                }
            }
            if (aCount != bCount)
            {
                throw new InvalidOperationException(aCount + "!=" + bCount);
            }
#endif
            return(result);
        }
        /// <summary>
        /// (For multi-piece support)
        /// Returns the piece that connects to the given side or null if there is none
        /// </summary>
        /// <returns>The piece on side.</returns>
        /// <param name="side">Side.</param>
        public virtual AbstractTrackBlock getPieceOnSide(TrackSide side)
        {
            TrackEnds trackEnd = getEndOnSide(side);

            if (trackEnd == TrackEnds.None)
            {
                return(null);
            }
            return(this);
        }
        public override AbstractTrackBlock getPieceOnSide(TrackSide side)
        {
            TrackEnds end = a.getEndOnSide(side);

            if (end == TrackEnds.None)
            {
                return(b);
            }
            return(a);
        }
        /// <summary>
        /// Get the train block adjacent to this one, optionally only if it
        /// connects to this one. Returns null if one was not found or if
        /// <code>connectingOnly</code> and the found one is not connected.
        /// </summary>
        /// <returns>The adjacent block.</returns>
        /// <param name="source">Source.</param>
        /// <param name="direction">Direction.</param>
        private AbstractTrackBlock getAdjacentBlock(AbstractTrackBlock source,
                                                    TrackSide direction,
                                                    bool connectingOnly)
        {
            TrackEnds side = source.getEndOnSide(direction);

            if (connectingOnly && side == TrackEnds.None)
            {
                return(null);
            }
            AbstractTrackBlock result;

            switch (direction)
            {
            case TrackSide.North:
                if (source.y == 0)
                {
                    return(null);
                }
                result = this[source.x, source.y - 1];
                break;

            case TrackSide.East:
                if (source.x == 0)
                {
                    return(null);
                }
                result = this[source.x - 1, source.y];
                break;

            case TrackSide.South:
                if (source.y == EngineOptions.gridHeight - 1)
                {
                    return(null);
                }
                result = this[source.x, source.y + 1];
                break;

            case TrackSide.West:
                if (source.x == EngineOptions.gridWidth - 1)
                {
                    return(null);
                }
                result = this[source.x + 1, source.y];
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
            if (!connectingOnly)
            {
                return(result);
            }
            TrackSide otherSide = invertSide(direction);
            TrackEnds otherEnd  = result.getEndOnSide(otherSide);

            if (otherEnd == TrackEnds.None)
            {
                return(null);
            }
            return(result);
        }
        private void moveTrain()
        {
            int speed = speeds[curSpeedIndex];
            AbstractTrainCar crashCar = null;

            foreach (var item in trainList)
            {
                int sections = item.progress;
                sections += speed;
                AbstractTrackBlock curBlock = this[item.x, item.y];
                TrackSide          entry    = item.entry;
                while (sections > EngineOptions.blockSections)
                {
                    sections -= EngineOptions.blockSections;
                    TrackSide entrySide = entry;
                    //discover the output edge
                    AbstractTrackBlock subBlock = curBlock.getPieceOnSide(
                        entrySide);
                    TrackEnds endOnSide = subBlock.getEndOnSide(entrySide);
                    //find the side the other end is on
                    TrackEnds otherEnd = endOnSide == TrackEnds.A ?
                                         TrackEnds.B : TrackEnds.A;
                    TrackSide   exit = (TrackSide)(-1);
                    TrackEnds[] ends = subBlock.getTrackEnds();
                    for (int i = 0; i < (int)TrackSide.West + 1; i++)
                    {
                        if (ends[i] == otherEnd)
                        {
                            exit = (TrackSide)i;
                            break;
                        }
                    }
                    AbstractTrackBlock nextBlock = getAdjacentBlock(
                        curBlock, exit, true);
                    if (nextBlock == null)
                    {
                        //crash
                        crashCar = item;
                        //so other blocks will travel the right distance...
                        speed   -= sections;
                        sections = EngineOptions.blockSections;
                    }
                    else
                    {
                        curBlock = nextBlock;
                        entry    = invertSide(exit);
                    }
                }
                item.progress = sections;
                item.x        = curBlock.x;
                item.y        = curBlock.y;
                item.entry    = entry;
                debugLog("Car " + item.trainIndex + " is now at " + item.x + "," +
                         item.y + " and " + item.progress + "sections entering " +
                         "from side " + item.entry + ".");
            }
            if (crashCar != null)
            {
                gameOverHappened = true;
                throw new GameOverException(crashCar);
            }
        }