internal MovementSegment(MovementSegment previous, OrganismState state, Point startingPoint, int entryTime, int gridX, int gridY)
        {
            Debug.Assert((previous == null && entryTime == 0) || (previous != null && entryTime != 0));

            this.State = state;
            this.StartingPoint = startingPoint;
            this.EntryTime = entryTime;
            this.GridX = gridX;
            this.GridY = gridY;
            this.Previous = previous;
        }
Example #2
0
        internal MovementSegment(MovementSegment previous, OrganismState state, Point startingPoint, int entryTime,
                                 int gridX, int gridY)
        {
            Debug.Assert((previous == null && entryTime == 0) || (previous != null && entryTime != 0));

            State         = state;
            StartingPoint = startingPoint;
            EntryTime     = entryTime;
            GridX         = gridX;
            GridY         = gridY;
            Previous      = previous;
        }
Example #3
0
        // Take an OrganismState and a path to move it on (from Point1 to Point2) and break up this
        // path into MovementSegments when it moves between cells.  Each MovementSegment represents the part of the path
        // that is within a single cell.
        //
        // Even though we are only drawing a single line and rasterizing it along one path through the grid,
        // we can assume that the center of every cell that this organism occupies follows exactly the same path
        // and has segments that break down at exactly the same timeframes.  Thus we call the AddSegment() routine
        // for each MovementSegment.  AddSegment() wraps this MovementSegment with a wrapper (SegmentWrapper) and adds
        // this wrapper to every cell that the creature occupies so that all the cells that are occupied at any given
        // are properly noted.
        //
        // AddPath() records the time that the path entered and left each cell in the path.
        // Each cell of the grid is 2^GridWidthPowerOfTwo x 2^GridHeightPowerOfTwo pixels in size
        // Each is a power of two so we can shift instead of dividing
        // The first square of the grid starts at 0, 0 and goes to 2^GridWidthPowerOfTwo - 1, 2^GridHeightPowerOfTwo - 1
        // Use Bresenham's algorithm to do the rasterization and figure out when the path enters or leaves grid squares.
        public void AddPath(OrganismState state, Point p1, Point p2)
        {
            var x0 = p1.X;
            var y0 = p1.Y;
            var x1 = p2.X;
            var y1 = p2.Y;
            var dy = y1 - y0;
            var dx = x1 - x0;
            int stepx, stepy;
            var timeslice = 0;

            Debug.Assert(x0 > -1 && x1 > -1 && y0 > -1 && y1 > -1);
            if (dy < 0)
            {
                dy = -dy;
                stepy = -1;
            }
            else
            {
                stepy = 1;
            }

            if (dx < 0)
            {
                dx = -dx;
                stepx = -1;
            }
            else
            {
                stepx = 1;
            }

            dy <<= 1;
            dx <<= 1;

            // start the first segment at the initial point at time 0
            var gridX = x0 >> EngineSettings.GridWidthPowerOfTwo;
            var gridY = y0 >> EngineSettings.GridWidthPowerOfTwo;
            Debug.Assert(gridX == state.GridX && gridY == state.GridY);
            var segment = new MovementSegment(null, state, new Point(x0, y0), 0, gridX, gridY)
                              {EndingPoint = new Point(p1.X, p1.Y)};
            AddSegment(segment);
            if (p1 == p2)
            {
                return;
            }

            if (dx > dy)
            {
                // Determine how many points we'll plot and estimate time by that
                if ((x1 - x0) != 0)
                {
                    Debug.Assert((x1 - x0) < TimeWindow);
                    timeslice = TimeWindow/((x1 - x0)*stepx);
                    Debug.Assert(timeslice != 0);
                }

                var fraction = dy - (dx >> 1); // same as 2*dy - dx
                while (x0 != x1)
                {
                    if (fraction >= 0)
                    {
                        y0 += stepy;
                        fraction -= dx; // same as fraction -= 2*dx
                    }
                    x0 += stepx;
                    fraction += dy; // same as fraction -= 2*dy

                    // See if we've crossed into a new grid square
                    gridX = x0 >> EngineSettings.GridWidthPowerOfTwo;
                    gridY = y0 >> EngineSettings.GridHeightPowerOfTwo;
                    segment.ExitTime += timeslice;
                    if (gridX != segment.GridX || gridY != segment.GridY)
                    {
                        // End the segment since we've entered a new grid square
                        var lastSegment = segment;
                        segment = new MovementSegment(lastSegment, state, new Point(x0, y0),
                                                      lastSegment.ExitTime, gridX, gridY)
                                      {ExitTime = lastSegment.ExitTime};
                        lastSegment.Next = segment;
                        AddSegment(segment);
                    }

                    var newEndingPoint = new Point {X = x0, Y = y0};
                    segment.EndingPoint = newEndingPoint;
                }
            }
            else
            {
                if ((y1 - y0) != 0)
                {
                    Debug.Assert((y1 - y0) < TimeWindow);
                    timeslice = TimeWindow/((y1 - y0)*stepy);
                    Debug.Assert(timeslice != 0);
                }

                var fraction = dx - (dy >> 1);
                while (y0 != y1)
                {
                    if (fraction >= 0)
                    {
                        x0 += stepx;
                        fraction -= dy;
                    }
                    y0 += stepy;
                    fraction += dx;

                    // See if we've crossed into a new grid square
                    gridX = x0 >> EngineSettings.GridWidthPowerOfTwo;
                    gridY = y0 >> EngineSettings.GridHeightPowerOfTwo;
                    segment.ExitTime += timeslice;
                    if (gridX != segment.GridX || gridY != segment.GridY)
                    {
                        // End the segment since we've entered a new grid square
                        var lastSegment = segment;
                        segment = new MovementSegment(lastSegment, state, new Point(x0, y0), lastSegment.ExitTime,
                                                      gridX, gridY) {ExitTime = lastSegment.ExitTime};

                        lastSegment.Next = segment;
                        AddSegment(segment);
                    }

                    var newEndingPoint = new Point {X = x0, Y = y0};

                    segment.EndingPoint = newEndingPoint;
                }
            }

            // The last segment doesn't exit the grid, so its exit time is zero
            segment.ExitTime = 0;
        }
Example #4
0
 public SegmentWrapper(MovementSegment segment, ArrayList parentList)
 {
     Segment = segment;
     ParentList = parentList;
 }
Example #5
0
        // Adds a MovementSegment to all the proper cells in the grid to account for the creature's size by wrapping it
        // with a SegmentWrapper and adding the wrapper to the proper cells. When we calculate a creature's movement we have
        // to mark all the cells in a square around the center that their size covers as occupied.  This function reserves
        // all the cells that a given segment would cover for a creature.
        internal void AddSegment(MovementSegment segment)
        {
            // Figure out how many cells on either side of the center we need to reserve
            var cellRadius = segment.State.CellRadius;

            if (segment.Previous == null)
            {
                // Beginning of a segment
                // We should have never started in a position where the radius of the organism
                // went outside the bounds of the universe
                Debug.Assert(segment.GridX >= 0 && segment.GridY >= 0 &&
                             segment.GridX - cellRadius >= 0 &&
                             segment.GridY - cellRadius >= 0 &&
                             segment.GridX + cellRadius < GameEngine.Current.GridWidth &&
                             segment.GridY + cellRadius < GameEngine.Current.GridHeight);

                Debug.Assert(segment.EntryTime == 0);
                StartSegments.Add(segment);
            }
            else
            {
                Debug.Assert(segment.EntryTime != 0);
                // If this segment pushes the organisms radius outside the bounds of the universe, clip it now
                // and don't bother evaluating it later
                if (segment.GridX < 0 || segment.GridY < 0 ||
                    segment.GridX - cellRadius < 0 ||
                    segment.GridY - cellRadius < 0 ||
                    segment.GridX + cellRadius > GameEngine.Current.GridWidth - 1 ||
                    segment.GridY + cellRadius > GameEngine.Current.GridHeight - 1)
                {
                    segment.Previous.Next = null;
                    return;
                }
            }

            // Do the top and bottom rows
            ArrayList list;
            SegmentWrapper wrapper;
            for (var x = segment.GridX - cellRadius; x <= segment.GridX + cellRadius; x++)
            {
                // *** Top row of square ***
                // Make a unique hash for every square in the grid
                var hash = (x << 16) | (segment.GridY - cellRadius);

                // retrieve the set of SegmentWrappers that are already in the cell of the grid
                list = (ArrayList) _gridSquares[hash];
                if (list == null)
                {
                    // None exist yet, create an ArrayList to hold them
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }

                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));

                // Create a wrapper for this segment, give it a backpointer to the arraylist that contains
                // all the segments in this cell
                wrapper = new SegmentWrapper(segment, list);

                // Add the SegmentWrapper itself to the list of segments in this cell
                list.Add(wrapper);

                // Now add the SegmentWrapper to our master sorted list of all SegmentWrappers anywhere
                _sortedList.Add(wrapper);

                // CellsLeftToResolve is there to recognize the fact that an animal overlaps many squares.
                // Until you know that it can occupy all of the squares it moves into, it can't move into
                // any of them.  This property keeps track of whether we have resolved them all or not.
                // Here, we are adding one to it for every cell we occupy with this segment.
                segment.CellsLeftToResolve++;

                // *** Bottom row of square ***
                hash = (x << 16) | (segment.GridY + cellRadius);
                list = (ArrayList) _gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }

                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;
            }

            // Do left and right columns
            for (var y = segment.GridY - cellRadius + 1; y <= segment.GridY + cellRadius - 1; y++)
            {
                // Make a unique hash for every square in the grid
                var hash = ((segment.GridX - cellRadius) << 16) | y;
                list = (ArrayList) _gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }
                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;

                hash = ((segment.GridX + cellRadius) << 16) | y;
                list = (ArrayList) _gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }
                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;
            }
        }
Example #6
0
 public SegmentWrapper(MovementSegment segment, ArrayList parentList)
 {
     Segment    = segment;
     ParentList = parentList;
 }
Example #7
0
        // Adds a MovementSegment to all the proper cells in the grid to account for the creature's size by wrapping it
        // with a SegmentWrapper and adding the wrapper to the proper cells. When we calculate a creature's movement we have
        // to mark all the cells in a square around the center that their size covers as occupied.  This function reserves
        // all the cells that a given segment would cover for a creature.
        internal void AddSegment(MovementSegment segment)
        {
            // Figure out how many cells on either side of the center we need to reserve
            var cellRadius = segment.State.CellRadius;

            if (segment.Previous == null)
            {
                // Beginning of a segment
                // We should have never started in a position where the radius of the organism
                // went outside the bounds of the universe
                Debug.Assert(segment.GridX >= 0 && segment.GridY >= 0 &&
                             segment.GridX - cellRadius >= 0 &&
                             segment.GridY - cellRadius >= 0 &&
                             segment.GridX + cellRadius < GameEngine.Current.GridWidth &&
                             segment.GridY + cellRadius < GameEngine.Current.GridHeight);

                Debug.Assert(segment.EntryTime == 0);
                StartSegments.Add(segment);
            }
            else
            {
                Debug.Assert(segment.EntryTime != 0);
                // If this segment pushes the organisms radius outside the bounds of the universe, clip it now
                // and don't bother evaluating it later
                if (segment.GridX < 0 || segment.GridY < 0 ||
                    segment.GridX - cellRadius < 0 ||
                    segment.GridY - cellRadius < 0 ||
                    segment.GridX + cellRadius > GameEngine.Current.GridWidth - 1 ||
                    segment.GridY + cellRadius > GameEngine.Current.GridHeight - 1)
                {
                    segment.Previous.Next = null;
                    return;
                }
            }

            // Do the top and bottom rows
            ArrayList      list;
            SegmentWrapper wrapper;

            for (var x = segment.GridX - cellRadius; x <= segment.GridX + cellRadius; x++)
            {
                // *** Top row of square ***
                // Make a unique hash for every square in the grid
                var hash = (x << 16) | (segment.GridY - cellRadius);

                // retrieve the set of SegmentWrappers that are already in the cell of the grid
                list = (ArrayList)_gridSquares[hash];
                if (list == null)
                {
                    // None exist yet, create an ArrayList to hold them
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }

                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));

                // Create a wrapper for this segment, give it a backpointer to the arraylist that contains
                // all the segments in this cell
                wrapper = new SegmentWrapper(segment, list);

                // Add the SegmentWrapper itself to the list of segments in this cell
                list.Add(wrapper);

                // Now add the SegmentWrapper to our master sorted list of all SegmentWrappers anywhere
                _sortedList.Add(wrapper);

                // CellsLeftToResolve is there to recognize the fact that an animal overlaps many squares.
                // Until you know that it can occupy all of the squares it moves into, it can't move into
                // any of them.  This property keeps track of whether we have resolved them all or not.
                // Here, we are adding one to it for every cell we occupy with this segment.
                segment.CellsLeftToResolve++;

                // *** Bottom row of square ***
                hash = (x << 16) | (segment.GridY + cellRadius);
                list = (ArrayList)_gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }

                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;
            }

            // Do left and right columns
            for (var y = segment.GridY - cellRadius + 1; y <= segment.GridY + cellRadius - 1; y++)
            {
                // Make a unique hash for every square in the grid
                var hash = ((segment.GridX - cellRadius) << 16) | y;
                list = (ArrayList)_gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }
                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;

                hash = ((segment.GridX + cellRadius) << 16) | y;
                list = (ArrayList)_gridSquares[hash];
                if (list == null)
                {
                    list = new ArrayList();
                    _gridSquares[hash] = list;
                }
                // Make sure two organisms didn't start in the same place
                Debug.Assert(segment.EntryTime != 0 || !HasStartingSegments(list));
                wrapper = new SegmentWrapper(segment, list);
                list.Add(wrapper);
                _sortedList.Add(wrapper);
                segment.CellsLeftToResolve++;
            }
        }
Example #8
0
        // Take an OrganismState and a path to move it on (from Point1 to Point2) and break up this
        // path into MovementSegments when it moves between cells.  Each MovementSegment represents the part of the path
        // that is within a single cell.
        //
        // Even though we are only drawing a single line and rasterizing it along one path through the grid,
        // we can assume that the center of every cell that this organism occupies follows exactly the same path
        // and has segments that break down at exactly the same timeframes.  Thus we call the AddSegment() routine
        // for each MovementSegment.  AddSegment() wraps this MovementSegment with a wrapper (SegmentWrapper) and adds
        // this wrapper to every cell that the creature occupies so that all the cells that are occupied at any given
        // are properly noted.
        //
        // AddPath() records the time that the path entered and left each cell in the path.
        // Each cell of the grid is 2^GridWidthPowerOfTwo x 2^GridHeightPowerOfTwo pixels in size
        // Each is a power of two so we can shift instead of dividing
        // The first square of the grid starts at 0, 0 and goes to 2^GridWidthPowerOfTwo - 1, 2^GridHeightPowerOfTwo - 1
        // Use Bresenham's algorithm to do the rasterization and figure out when the path enters or leaves grid squares.
        public void AddPath(OrganismState state, Point p1, Point p2)
        {
            var x0 = p1.X;
            var y0 = p1.Y;
            var x1 = p2.X;
            var y1 = p2.Y;
            var dy = y1 - y0;
            var dx = x1 - x0;
            int stepx, stepy;
            var timeslice = 0;

            Debug.Assert(x0 > -1 && x1 > -1 && y0 > -1 && y1 > -1);
            if (dy < 0)
            {
                dy    = -dy;
                stepy = -1;
            }
            else
            {
                stepy = 1;
            }

            if (dx < 0)
            {
                dx    = -dx;
                stepx = -1;
            }
            else
            {
                stepx = 1;
            }

            dy <<= 1;
            dx <<= 1;

            // start the first segment at the initial point at time 0
            var gridX = x0 >> EngineSettings.GridWidthPowerOfTwo;
            var gridY = y0 >> EngineSettings.GridWidthPowerOfTwo;

            Debug.Assert(gridX == state.GridX && gridY == state.GridY);
            var segment = new MovementSegment(null, state, new Point(x0, y0), 0, gridX, gridY)
            {
                EndingPoint = new Point(p1.X, p1.Y)
            };

            AddSegment(segment);
            if (p1 == p2)
            {
                return;
            }

            if (dx > dy)
            {
                // Determine how many points we'll plot and estimate time by that
                if ((x1 - x0) != 0)
                {
                    Debug.Assert((x1 - x0) < TimeWindow);
                    timeslice = TimeWindow / ((x1 - x0) * stepx);
                    Debug.Assert(timeslice != 0);
                }

                var fraction = dy - (dx >> 1); // same as 2*dy - dx
                while (x0 != x1)
                {
                    if (fraction >= 0)
                    {
                        y0       += stepy;
                        fraction -= dx; // same as fraction -= 2*dx
                    }
                    x0       += stepx;
                    fraction += dy; // same as fraction -= 2*dy

                    // See if we've crossed into a new grid square
                    gridX             = x0 >> EngineSettings.GridWidthPowerOfTwo;
                    gridY             = y0 >> EngineSettings.GridHeightPowerOfTwo;
                    segment.ExitTime += timeslice;
                    if (gridX != segment.GridX || gridY != segment.GridY)
                    {
                        // End the segment since we've entered a new grid square
                        var lastSegment = segment;
                        segment = new MovementSegment(lastSegment, state, new Point(x0, y0),
                                                      lastSegment.ExitTime, gridX, gridY)
                        {
                            ExitTime = lastSegment.ExitTime
                        };
                        lastSegment.Next = segment;
                        AddSegment(segment);
                    }

                    var newEndingPoint = new Point {
                        X = x0, Y = y0
                    };
                    segment.EndingPoint = newEndingPoint;
                }
            }
            else
            {
                if ((y1 - y0) != 0)
                {
                    Debug.Assert((y1 - y0) < TimeWindow);
                    timeslice = TimeWindow / ((y1 - y0) * stepy);
                    Debug.Assert(timeslice != 0);
                }

                var fraction = dx - (dy >> 1);
                while (y0 != y1)
                {
                    if (fraction >= 0)
                    {
                        x0       += stepx;
                        fraction -= dy;
                    }
                    y0       += stepy;
                    fraction += dx;

                    // See if we've crossed into a new grid square
                    gridX             = x0 >> EngineSettings.GridWidthPowerOfTwo;
                    gridY             = y0 >> EngineSettings.GridHeightPowerOfTwo;
                    segment.ExitTime += timeslice;
                    if (gridX != segment.GridX || gridY != segment.GridY)
                    {
                        // End the segment since we've entered a new grid square
                        var lastSegment = segment;
                        segment = new MovementSegment(lastSegment, state, new Point(x0, y0), lastSegment.ExitTime,
                                                      gridX, gridY)
                        {
                            ExitTime = lastSegment.ExitTime
                        };

                        lastSegment.Next = segment;
                        AddSegment(segment);
                    }

                    var newEndingPoint = new Point {
                        X = x0, Y = y0
                    };

                    segment.EndingPoint = newEndingPoint;
                }
            }

            // The last segment doesn't exit the grid, so its exit time is zero
            segment.ExitTime = 0;
        }
Example #9
0
 public SegmentWrapper(MovementSegment segment, ArrayList parentList)
 {
     this.segment = segment;
     this.parentList = parentList;
 }