public void StationaryMelee()
        {
            Board board = new Board(BoardCommon.GRID_12X8);
            Combatant attacker = new Combatant("Attacker", board, new Point(5, 4));
            Combatant defender = new Combatant("Defender", board, new Point(7, 4));
            board.AddPawn(attacker);
            board.AddPawn(defender);

            attacker.Health = 10;
            defender.Health = 10;

            attacker.BaseStats = new Stats() {
                Attack = 10,
                Stamina = 10
            };

            MeleeAttackSkill attack = new MeleeAttackSkill(attacker, new Point[] { Point.Right, 2 * Point.Right }) {
                ActionPoints = 3
            };
            attack.SetDirection(CardinalDirection.East);

            attacker.AddSkill(attack);
            attack.Fire();

            board.BeginTurn();
            Assert.AreEqual(10, attacker.ActionPoints);

            board.Turn();
            Assert.AreEqual(0, defender.Health);
            Assert.AreEqual(7, attacker.ActionPoints);
        }
        /// <summary>
        /// Lights up cells visible from the current position.  Clear all lighting before calling.
        /// </summary>
        /// <param name="grid">The cell grid definition.</param>
        /// <param name="gridPosn">The player's position within the grid.</param>
        /// <param name="viewRadius">Maximum view distance; can be a fractional value.</param>
        public static HashSet<Point> ComputeVisibility(Board board, Point gridPosn, float viewRadius)
        {
            //Debug.Assert(gridPosn.x >= 0 && gridPosn.x < grid.xDim);
            //Debug.Assert(gridPosn.y >= 0 && gridPosn.y < grid.yDim);

            HashSet<Point> visible = new HashSet<Point>();

            // Viewer's cell is always visible.
            visible.Add(gridPosn);

            // Cast light into cells for each of 8 octants.
            //
            // The left/right inverse slope values are initially 1 and 0, indicating a diagonal
            // and a horizontal line.  These aren't strictly correct, as the view area is supposed
            // to be based on corners, not center points.  We only really care about one side of the
            // wall at the edges of the octant though.
            //
            // NOTE: depending on the compiler, it's possible that passing the octant transform
            // values as four integers rather than an object reference would speed things up.
            // It's much tidier this way though.
            for (int txidx = 0; txidx < s_octantTransform.Length; txidx++) {
                CastLight(board, gridPosn, viewRadius, 1, 1.0f, 0.0f, s_octantTransform[txidx], visible);
            }

            return visible;
        }
        public void PawnMovedButNotInBoard()
        {
            Board board = new Board(BoardCommon.GRID_12X8);
            Pawn pawn = new Pawn("Add", board, new Point(board.Width / 2, board.Height / 2));

            // Expect an ArgumentException because the pawn doesn't exist on the board yet
            Assert.Throws<ArgumentException>(delegate () {
                pawn.Offset(Point.One);
            }, "Offset was called on a Pawn that was not added to a Board");
        }
        public void GridWidthAndHeight()
        {
            int[,] grid = {
                {0, 0, 0, 0 },
                {0, 0, 0, 0 },
                {0, 0, 0, 0 }
            };

            Board board = new Board(grid);

            Assert.AreEqual(board.Width, 4);
            Assert.AreEqual(board.Height, 3);
        }
        public void ApproachMeleeRange()
        {
            Board board = new Board( new int[,] {
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
                { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
            });
            Combatant attacker = new Combatant("Attacker", board, new Point(1, 7));
            Combatant defender = new Combatant("Defender", board, new Point(10, 1));
            board.AddPawn(attacker);
            board.AddPawn(defender);

            attacker.Health = 10;
            defender.Health = 10;

            attacker.BaseStats = new Stats() {
                Attack = 10,
                Stamina = 25
            };

            attacker.AddPawnToView(defender);

            MeleeAttackSkill attack = new MeleeAttackSkill(attacker, new Point[] { Point.Right, 2 * Point.Right }) {
                ActionPoints = 3
            };
            attacker.AddSkill(attack);
            WalkSkill walk = new WalkSkill(attacker);
            attacker.AddSkill(walk);

            LostGen.Decision.ApproachMeleeRange approach = new LostGen.Decision.ApproachMeleeRange(attacker);
            approach.Target = defender;

            approach.Setup();
            approach.Run();

            board.BeginTurn();
            Assert.AreEqual(25, attacker.ActionPoints);

            board.Turn();
            Assert.AreEqual(new Point(10, 3), attacker.Position);
        }
Exemple #6
0
    public void OnStateUpdate(LostGen.Board world)
    {
        var toRemove = _activePawns.Keys.Except(world.Pawns.Keys);
        var toAdd    = world.Pawns.Keys.Except(_activePawns.Keys);

        foreach (var key in toRemove)
        {
            _activePawns[key].QueueFree();
        }

        foreach (var key in toAdd)
        {
            var pawn = (PawnCharacter)_pawnScene.Instance();
            pawn.PlayerID = key;
            AddChild(pawn);
            pawn.OnStateUpdate(world);
        }
    }
        public void CollisionTest()
        {
            // Arrange
            Board board = new Board(BoardCommon.GRID_12X8);

            Point pos1 = new Point(board.Width / 2, board.Height / 2); // Center of board
            Point pos2 = pos1 + (3 * Point.Right); // Two tiles to the right of center

            List<Point> footprint = new List<Point>(new Point[] {
                Point.Zero, Point.Up, Point.Down, Point.Left, Point.Right
            });

            Pawn pawn1 = new Pawn("First", board, pos1, footprint, true, true, true);
            Pawn pawn2 = new Pawn("Second", board, pos2, footprint, true, true, true);

            board.AddPawn(pawn1);
            board.AddPawn(pawn2);

            bool p1Triggered = false;
            bool p2Triggered = false;

            Pawn.CollisionDelegate p1Collisions = delegate (Pawn source, Pawn other) {
                p1Triggered = true;
                Assert.AreSame(source, pawn1);
                Assert.AreSame(other, pawn2);
            };

            Pawn.CollisionDelegate p2Collisions = delegate (Pawn source, Pawn other) {
                p2Triggered = true;
                Assert.AreSame(source, pawn2);
                Assert.AreSame(other, pawn1);
            };

            pawn1.CollisionEntered += p1Collisions;
            pawn2.CollisionEntered += p2Collisions;

            // Act
            pawn1.Position = pawn1.Position + Point.Right;

            // Assert
            Assert.IsTrue(p1Triggered, "Pawn 1's OnCollisionEnter was not triggered");
            Assert.IsTrue(p2Triggered, "Pawn 2's OnCollisionEnter was not triggered");
        }
        public void MoveIntoWall()
        {
            Board board = new Board(BoardCommon.GRID_12X8);
            Pawn pawn = new Pawn("Mover", board, Point.One, null, true, true);

            board.AddPawn(pawn);

            Point expectedPosition = new Point(2, 1);

            bool moveRight = pawn.Offset(Point.Right);
            Point position1 = pawn.Position;

            bool moveUp = pawn.Offset(Point.Up);
            Point position2 = pawn.Position;

            Assert.IsTrue(moveRight, "Offset returned false when moving right");
            Assert.AreEqual(expectedPosition, position1, "Pawn failed to move right to open tile");

            Assert.IsFalse(moveUp, "Offset returned true when moving up into a wall");
            Assert.AreEqual(expectedPosition, position2, "Pawn failed to stay on one goddamn place");
        }
        private void ArrangeBoard(int[,] grid, Point start, Point end, out Board board, out Combatant pawn)
        {
            board = new Board(grid);
            pawn = new Combatant("Walker", board, Point.One);

            Stats stats = new Stats() { Stamina = 100 };
            pawn.BaseStats = stats;

            board.AddPawn(pawn);

            WalkSkill walk = new WalkSkill(pawn);
            pawn.AddSkill(walk);

            walk.SetTarget(end);

            board.BeginTurn();
        }
            /// <summary>
            /// Construct a new Node
            /// </summary>
            /// <param name="board"></param>
            /// <param name="point"></param>
            /// <param name="lookup"></param>
            public Node(Board board, Point point, EdgeCostLookup lookup = null)
            {
                if (board == null) { throw new ArgumentNullException("board"); }
                if (lookup == null) { throw new ArgumentNullException("lookup"); }

                _board = board;
                _point = point;
                _edgeCostLookup = lookup;
            }
        /// <summary>
        /// Recursively casts light into cells.  Operates on a single octant.
        /// </summary>
        /// <param name="grid">The cell grid definition.</param>
        /// <param name="gridPosn">The player's position within the grid.</param>
        /// <param name="viewRadius">The view radius; can be a fractional value.</param>
        /// <param name="startColumn">Current column; pass 1 as initial value.</param>
        /// <param name="leftViewSlope">Slope of the left (upper) view edge; pass 1.0 as
        ///   the initial value.</param>
        /// <param name="rightViewSlope">Slope of the right (lower) view edge; pass 0.0 as
        ///   the initial value.</param>
        /// <param name="txfrm">Coordinate multipliers for the octant transform.</param>
        ///
        /// Maximum recursion depth is (Ceiling(viewRadius)).
        private static void CastLight(Board grid, Point gridPosn, float viewRadius,
            int startColumn, float leftViewSlope, float rightViewSlope, OctantTransform txfrm, HashSet<Point> visible)
        {
            //Debug.Assert(leftViewSlope >= rightViewSlope);

            // Used for distance test.
            float viewRadiusSq = viewRadius * viewRadius;

            int viewCeiling = (int)Math.Ceiling(viewRadius);

            // Set true if the previous cell we encountered was blocked.
            bool prevWasBlocked = false;

            // As an optimization, when scanning past a block we keep track of the
            // rightmost corner (bottom-right) of the last one seen.  If the next cell
            // is empty, we can use this instead of having to compute the top-right corner
            // of the empty cell.
            float savedRightSlope = -1;

            int xDim = grid.Width;
            int yDim = grid.Height;

            // Outer loop: walk across each column, stopping when we reach the visibility limit.
            for (int currentCol = startColumn; currentCol <= viewCeiling; currentCol++) {
                int xc = currentCol;

                // Inner loop: walk down the current column.  We start at the top, where X==Y.
                //
                // TODO: we waste time walking across the entire column when the view area
                //   is narrow.  Experiment with computing the possible range of cells from
                //   the slopes, and iterate over that instead.
                for (int yc = currentCol; yc >= 0; yc--) {
                    // Translate local coordinates to grid coordinates.  For the various octants
                    // we need to invert one or both values, or swap X for Y.
                    int gridX = gridPosn.X + xc * txfrm.xx + yc * txfrm.xy;
                    int gridY = gridPosn.Y + xc * txfrm.yx + yc * txfrm.yy;

                    // Range-check the values.  This lets us avoid the slope division for blocks
                    // that are outside the grid.
                    //
                    // Note that, while we will stop at a solid column of blocks, we do always
                    // start at the top of the column, which may be outside the grid if we're (say)
                    // checking the first octant while positioned at the north edge of the map.
                    if (gridX < 0 || gridX >= xDim || gridY < 0 || gridY >= yDim) {
                        continue;
                    }

                    // Compute slopes to corners of current block.  We use the top-left and
                    // bottom-right corners.  If we were iterating through a quadrant, rather than
                    // an octant, we'd need to flip the corners we used when we hit the midpoint.
                    //
                    // Note these values will be outside the view angles for the blocks at the
                    // ends -- left value > 1, right value < 0.
                    float leftBlockSlope = (yc + 0.5f) / (xc - 0.5f);
                    float rightBlockSlope = (yc - 0.5f) / (xc + 0.5f);

                    // Check to see if the block is outside our view area.  Note that we allow
                    // a "corner hit" to make the block visible.  Changing the tests to >= / <=
                    // will reduce the number of cells visible through a corner (from a 3-wide
                    // swath to a single diagonal line), and affect how far you can see past a block
                    // as you approach it.  This is mostly a matter of personal preference.
                    if (rightBlockSlope > leftViewSlope) {
                        // Block is above the left edge of our view area; skip.
                        continue;
                    } else if (leftBlockSlope < rightViewSlope) {
                        // Block is below the right edge of our view area; we're done.
                        break;
                    }

                    // This cell is visible, given infinite vision range.  If it's also within
                    // our finite vision range, light it up.
                    //
                    // To avoid having a single lit cell poking out N/S/E/W, use a fractional
                    // viewRadius, e.g. 8.5.
                    //
                    // TODO: we're testing the middle of the cell for visibility.  If we tested
                    //  the bottom-left corner, we could say definitively that no part of the
                    //  cell is visible, and reduce the view area as if it were a wall.  This
                    //  could reduce iteration at the corners.
                    float distanceSquared = xc * xc + yc * yc;
                    if (distanceSquared <= viewRadiusSq) {
                        visible.Add(new Point(gridX, gridY));
                    }

                    bool curBlocked = grid.IsOpaque(new Point(gridX, gridY));

                    if (prevWasBlocked) {
                        if (curBlocked) {
                            // Still traversing a column of walls.
                            savedRightSlope = rightBlockSlope;
                        } else {
                            // Found the end of the column of walls.  Set the left edge of our
                            // view area to the right corner of the last wall we saw.
                            prevWasBlocked = false;
                            leftViewSlope = savedRightSlope;
                        }
                    } else {
                        if (curBlocked) {
                            // Found a wall.  Split the view area, recursively pursuing the
                            // part to the left.  The leftmost corner of the wall we just found
                            // becomes the right boundary of the view area.
                            //
                            // If this is the first block in the column, the slope of the top-left
                            // corner will be greater than the initial view slope (1.0).  Handle
                            // that here.
                            if (leftBlockSlope <= leftViewSlope) {
                                CastLight(grid, gridPosn, viewRadius, currentCol + 1,
                                    leftViewSlope, leftBlockSlope, txfrm, visible);
                            }

                            // Once that's done, we keep searching to the right (down the column),
                            // looking for another opening.
                            prevWasBlocked = true;
                            savedRightSlope = rightBlockSlope;
                        }
                    }
                }

                // Open areas are handled recursively, with the function continuing to search to
                // the right (down the column).  If we reach the bottom of the column without
                // finding an open cell, then the area defined by our view area is completely
                // obstructed, and we can stop working.
                if (prevWasBlocked) {
                    break;
                }
            }
        }
Exemple #12
0
    public void OnStateUpdate(LostGen.Board state)
    {
        var self = state.Pawns[PlayerID];

        Translation = new Vector3(self.Position.X, self.Position.Y, self.Position.Z);
    }
        public void SolidPawnsMoveIntoSharedCell()
        {
            Board board = new Board(BoardCommon.GRID_12X8);

            Point pos1 = new Point(6, 4);
            Point pos2 = new Point(8, 4);

            Point pos3 = new Point(7, 4);

            Pawn pawn1 = new Pawn("First", board, pos1, null, true, true, true);
            Pawn pawn2 = new Pawn("Second", board, pos2, null, true, true, true);

            board.AddPawn(pawn1);
            board.AddPawn(pawn2);

            Assert.IsTrue(pawn1.Offset(Point.Right),"Pawn tried to move from " + pos1 + " to " + pos3 + " but was blocked by something");
            Assert.IsFalse(pawn2.Offset(Point.Left), "Pawn moved from " + pos2 + " to " + pos3 + " when it should've been blocked");

            Assert.AreEqual(pos3, pawn1.Position, "Pawn was not able to move from " + pos1 + " to " + pos3);
            Assert.AreEqual(pos2, pawn2.Position, "Pawn was able to move from " + pos3 + " to " + pos2);
        }
        public void MoveSolidPawnIntoSolidPawn()
        {
            Board board = new Board(BoardCommon.GRID_12X8);

            Point pos1 = new Point(6, 4);
            Point pos2 = new Point(8, 4);

            Pawn pawn1 = new Pawn("First", board, pos1, null, true, true, true);
            Pawn pawn2 = new Pawn("Second", board, pos2, null, true, true, true);

            board.AddPawn(pawn1);
            board.AddPawn(pawn2);

            Point expectedPosition = new Point(7, 4);

            bool firstMove = pawn2.Offset(Point.Left);
            Point firstPosition = pawn2.Position;

            bool secondMove = pawn2.Offset(Point.Left);
            Point secondPosition = pawn2.Position;

            Assert.IsTrue(firstMove, "Pawn was not able to move left one tile");
            Assert.IsFalse(secondMove, "Pawn somehow passed through another solid pawn");

            Assert.AreEqual(expectedPosition, firstPosition, "Pawn could not move to free tile");
            Assert.AreEqual(expectedPosition, secondPosition, "Pawn somehow passed through another solid pawn");
        }
 protected virtual int Heuristic(Board.Node start, Board.Node end)
 {
     return Point.TaxicabDistance(start.Point, end.Point);
 }
 public WalkSkill(Combatant owner)
     : base(owner, "Walk", "Move across tiles within a limited range")
 {
     _board = Owner.Board;
 }
        public void SolidPawnsMoveIntoEachOther()
        {
            Board board = new Board(BoardCommon.GRID_12X8);

            Point pos1 = new Point(6, 4);
            Point pos2 = new Point(7, 4);

            Pawn pawn1 = new Pawn("First", board, pos1, null, true, true, true);
            Pawn pawn2 = new Pawn("Second", board, pos2, null, true, true, true);

            board.AddPawn(pawn1);
            board.AddPawn(pawn2);

            Assert.IsFalse(pawn1.Offset(Point.Right));
            Assert.IsFalse(pawn2.Offset(Point.Left));

            Assert.AreEqual(pos1, pawn1.Position);
            Assert.AreEqual(pos2, pawn2.Position);
        }