/// <summary>
    /// Sets all the tiles in a specified rectangle on this grid to the specified type.
    /// Note that lowerLeft and upperRight MUST have this relationship
    /// (for example, upperRight.y cannot be lower than lowerLeft.y),
    /// or I'm going to crash your program.
    /// If prioritize is false,
    /// this function will only mark tiles which are currently empty.
    /// Also, this function technically can mark single lines,
    /// but it's recommended that you use setTypeLine() instead for that.
    /// </summary>
    /// <param name="lowerLeft">Lower leftmost point of the desired rectangle</param>
    /// <param name="upperRight">Upper rightmost point of the desired rectangle</param>
    /// <param name="type">Tile type to set this rectangle to</param>
    /// <param name="prioritize">Whether this action should override existing non-empty tiles</param>
    public void setTypeRect(Coord2DObject lowerLeft, Coord2DObject upperRight,
                            TileObject.TileType type, bool prioritize)
    {
        assertBounds(lowerLeft); assertBounds(upperRight);

        Assert.IsTrue(lowerLeft.x <= upperRight.x && lowerLeft.y <= upperRight.y,
                      "Invalid argument order. "
                      + "lowerLeft should be " + upperRight.ToString()
                      + "and upperRight should be " + lowerLeft.ToString());

        // Just setTypeLine if they gave us a line instead of a rectangle
        if (lowerLeft.x == upperRight.x || lowerLeft.y == upperRight.y)
        {
            setTypeLine(lowerLeft, upperRight, type, prioritize);
            return;
        }

        // If we're here, then we're marking a non-line rectangle,
        // and the arguments were provided in correct order
        for (int thisY = lowerLeft.y; thisY <= upperRight.y; thisY++)
        {
            // Go row by row
            Coord2DObject thisRowLeft  = new Coord2DObject(lowerLeft.x, thisY);
            Coord2DObject thisRowRight = new Coord2DObject(upperRight.x, thisY);

            setTypeLine(thisRowLeft, thisRowRight, type, prioritize);
        }
    }
Beispiel #2
0
    //public override bool Equals(object obj) {

    //    if (!this.GetType().IsAssignableFrom(obj.GetType()))
    //        return false;

    //    TileObject otherTile = (TileObject)obj;

    //    return type == otherTile.type
    //        && distance == otherTile.distance
    //        && location.Equals(otherTile.location);
    //}

    //public override int GetHashCode() {

    //    //if (location != null)
    //    //    return location.GetHashCode() + (distance % 29);

    //    //else
    //    //    return type.GetHashCode() + (distance % 17);

    //    return ToString().GetHashCode();
    //}

    private void init(TileType t, int dist, TileObject prev, Coord2DObject location)
    {
        this.type     = t;
        this.distance = dist;
        this.prev     = prev;
        this.location = location;
    }
Beispiel #3
0
    /// <summary>
    /// Add a joint to this path at the specified index.
    /// Adjacent points are required to be compatible,
    /// i.e. they lie on the same line as their adjacent joint.
    /// If you try to use this function and add a joint which isn't compatible with its neighbors,
    /// this function returns false and does NOT add the joint.
    /// Else, it adds the joint to this path and returns true.
    /// Don't worry if this Path isn't yet complete (i.e. doesn't have 2 or more joints);
    /// you can add joints with this method and still return true safely.
    /// </summary>
    /// <param name="newJoint">Joint to be added</param>
    /// <param name="index">Index in this list of joints</param>
    /// <returns>True if this joint was compatible and added successfully, false otherwise</returns>
    public bool addJoint(Coord2DObject newJoint, int index)
    {
        joints.Insert(index, newJoint);



        // Check compatibility with joint before, if it exists
        if (index > 0)
        {
            Coord2DObject leftNeighbor = joints[index - 1];

            if (!areCompatibleJoints(leftNeighbor, newJoint))
            {
                joints.RemoveAt(index);
                return(false);
            }
        }

        // Check compatibility with joint after, if it exists
        if (index < joints.Count - 1)
        {
            Coord2DObject rightNeighbor = joints[index + 1];

            if (!areCompatibleJoints(newJoint, rightNeighbor))
            {
                joints.RemoveAt(index);
                return(false);
            }
        }

        return(true);
    }
    /// <summary>
    /// Sets all the tiles in a specified line on this grid to the specified type.
    /// Note that point1 and point2 MUST lie on the same line
    /// (i.e. either their x coordinates are equal or their y coordinates are equal),
    /// or I'm going to crash your program.
    /// If prioritize is false,
    /// this function will only mark tiles which are currently empty.
    /// </summary>
    /// <param name="point1">An endpoint</param>
    /// <param name="point2">Another endpoint</param>
    /// <param name="type">Tile type to set this line to</param>
    /// <param name="prioritize">Whether this method should override existing non-empty tiles</param>
    public void setTypeLine(Coord2DObject point1, Coord2DObject point2,
                            TileObject.TileType type, bool prioritize)
    {
        assertBounds(point1); assertBounds(point2);

        Assert.raiseExceptions = true;
        Assert.IsTrue(point1.x == point2.x || point1.y == point2.y,
                      "point1 " + point1.ToString() + " and point2 " + point2.ToString() + " must lie on a straight line");

        if (point1.Equals(point2) &&
            (prioritize || getTile(point1).type == TileObject.TileType.EMPTY))
        {
            getTile(point1).type = type;
            return;
        }

        // If on the same row
        if (point1.y == point2.y)
        {
            // Iterate from least x to greater x,
            // whichever is which
            int biggerX  = (point1.x > point2.x ? point1.x : point2.x);
            int smallerX = (point1.x < point2.x ? point1.x : point2.x);

            for (int i = smallerX; i <= biggerX; i++)
            {
                TileObject thisTile = getTile(new Coord2DObject(i, point1.y));

                if (thisTile == null)
                {
                    Debug.Log("i is " + i + ", point1.y is " + point1.y);
                }

                if (prioritize || thisTile.type == TileObject.TileType.EMPTY)
                {
                    thisTile.type = type;
                }
            }
        }

        // Else, they're on the same column
        else
        {
            // Iterate from least y to greatest y,
            // whichever is which
            int biggerY  = (point1.y > point2.y ? point1.y : point2.y);
            int smallerY = (point1.y < point2.y ? point1.y : point2.y);

            for (int i = smallerY; i <= biggerY; i++)
            {
                TileObject thisTile = getTile(new Coord2DObject(point1.x, i));

                if (prioritize || thisTile.type == TileObject.TileType.EMPTY)
                {
                    thisTile.type = type;
                }
            }
        }
    }
Beispiel #5
0
    public PathObject(Grid2DObject grid, Coord2DObject point1, Coord2DObject point2, int thickness)
    {
        this.grid      = grid;
        joints         = new List <Coord2DObject>();
        this.thickness = thickness;

        populateBestPath(point1, point2);
    }
    public bool checkBounds(Coord2DObject location)
    {
        if (location.x < 0 || location.y < 0)
        {
            return(false);
        }

        return(location.x < dimensions.x && location.y < dimensions.y);
    }
    public TileObject getRight(Coord2DObject fromHere)
    {
        if (!canGoRight(fromHere))
        {
            return(null);
        }

        return(getTile(new Coord2DObject(fromHere.x + 1, fromHere.y)));
    }
    public TileObject getDown(Coord2DObject fromHere)
    {
        if (!canGoDown(fromHere))
        {
            return(null);
        }

        return(getTile(new Coord2DObject(fromHere.x, fromHere.y - 1)));
    }
    public TileObject getUp(Coord2DObject fromHere)
    {
        if (!canGoUp(fromHere))
        {
            return(null);
        }

        return(getTile(new Coord2DObject(fromHere.x, fromHere.y + 1)));
    }
    /// <summary>
    /// Sets all the tiles in a specified line on this grid to the specified type.
    /// This overload adds the "thickness" parameter.
    /// A single straight line of Tiles is considered a row with layers = 0 (total thickness 1).
    /// layers = 1 surrounds the line on each side with additional rows, for a total thickness of 3.
    /// All other parameters remain the same as the other overload for this method,
    /// including that part about me crashing your program
    /// if point1 and point2 aren't on the same line.
    /// </summary>
    /// <param name="point1">An endpoint</param>
    /// <param name="point2">Another endpoint</param>
    /// <param name="type">Tile type to set this line to</param>
    /// <param name="layers">Number of layers on each side of this line</param>
    /// <param name="prioritize">Whether this method should override existing non-empty tiles</param>
    public void setTypeLine(Coord2DObject point1, Coord2DObject point2,
                            TileObject.TileType type, int layers, bool prioritize)
    {
        for (int thisLayer = 0; thisLayer <= layers; thisLayer++)
        {
            // Rows (horizontal)
            if (point1.y == point2.y)
            {
                // Do row on top, offset by thisLevel
                Coord2DObject point1Layered = new Coord2DObject(point1.x, point1.y + thisLayer);
                Coord2DObject point2Layered = new Coord2DObject(point2.x, point2.y + thisLayer);

                if (checkBounds(point1Layered) && checkBounds(point2Layered))
                {
                    setTypeLine(point1Layered, point2Layered, type, prioritize);
                }



                // Do row on bot, offset by thisLevel
                point1Layered = new Coord2DObject(point1.x, point1.y - thisLayer);
                point2Layered = new Coord2DObject(point2.x, point2.y - thisLayer);

                if (checkBounds(point1Layered) && checkBounds(point2Layered))
                {
                    setTypeLine(point1Layered, point2Layered, type, prioritize);
                }
            }

            // Columns (vertical)
            else if (point1.x == point2.x)
            {
                // Do column on left, offset by thisLevel
                Coord2DObject point1Layered = new Coord2DObject(point1.x - thisLayer, point1.y);
                Coord2DObject point2Layered = new Coord2DObject(point2.x - thisLayer, point2.y);

                if (checkBounds(point1Layered) && checkBounds(point2Layered))
                {
                    setTypeLine(point1Layered, point2Layered, type, prioritize);
                }

                // Do column on right, offset by thisLevel
                point1Layered = new Coord2DObject(point1.x + thisLayer, point1.y);
                point2Layered = new Coord2DObject(point2.x + thisLayer, point2.y);

                if (checkBounds(point1Layered) && checkBounds(point2Layered))
                {
                    setTypeLine(point1Layered, point2Layered, type, prioritize);
                }
            }

            else
            {
                Assert.IsTrue(false, "point1" + point1.ToString() + " and point2 " + point2.ToString() + " not on a line");
            }
        }
    }
    public override bool Equals(object other)
    {
        if (!this.GetType().IsAssignableFrom(other.GetType()))
        {
            return(false);
        }

        Coord2DObject otherCoord = (Coord2DObject)other;

        return(x == otherCoord.x && y == otherCoord.y);
    }
    public Grid2DObject(Coord2DObject dimensions)
    {
        this.dimensions = new Coord2DObject(dimensions);

        grid = new TileObject[dimensions.x, dimensions.y];

        for (int i = 0; i < dimensions.x; i++)
        {
            for (int j = 0; j < dimensions.y; j++)
            {
                setTile(TileObject.TileType.EMPTY, new Coord2DObject(i, j));
            }
        }
    }
    /// <summary>
    /// Constructs a new Path (with the desired thickness) between each adjacent landmark given,
    /// and returns that series of Path objects.
    /// </summary>
    /// <param name="landmarks">Landmarks to be connected. Note that these don't necessarily have to be on the same line.</param>
    /// <param name="thickness">Thickness of the Path objects to be created.</param>
    /// <returns></returns>
    private List <PathObject> getFullPath(List <Coord2DObject> landmarks, int thickness)
    {
        List <PathObject> paths = new List <PathObject>(landmarks.Count);

        for (int i = 0; i < landmarks.Count - 1; i++)
        {
            Coord2DObject landmark1 = landmarks[i];
            Coord2DObject landmark2 = landmarks[i + 1];

            PathObject p = new PathObject(this, landmark1, landmark2, thickness);
            paths.Add(p);
        }

        return(paths);
    }
    /// <summary>
    /// Get the set of tiles immediately adjacent (up, down, left, right) to the specified coordinate.
    /// Note that in cases where the given location is on an edge or corner,
    /// the returned set will a size less than the usual four.
    /// Also note that this only returns neighbors which aren't non-traversable,
    /// so the returned tiles may be of type EMPTY or TRAVERSABLE.
    /// </summary>
    /// <param name="location">Location of tile whose neighbors should be returned</param>
    /// <returns>HashSet of TileObject references to this location's eligible neighbors</returns>
    public HashSet <TileObject> getTraversableNeighbors(Coord2DObject location)
    {
        assertBounds(location);

        HashSet <TileObject> neighbors = new HashSet <TileObject>();

        if (canGoUp(location))
        {
            TileObject upNeighbor = getUp(location);

            if (upNeighbor.type != TileObject.TileType.NON_TRAVERSABLE)
            {
                neighbors.Add(upNeighbor);
            }
        }

        if (canGoDown(location))
        {
            TileObject downNeighbor = getDown(location);

            if (downNeighbor.type != TileObject.TileType.NON_TRAVERSABLE)
            {
                neighbors.Add(downNeighbor);
            }
        }

        if (canGoLeft(location))
        {
            TileObject leftNeighbor = getLeft(location);

            if (leftNeighbor.type != TileObject.TileType.NON_TRAVERSABLE)
            {
                neighbors.Add(leftNeighbor);
            }
        }

        if (canGoRight(location))
        {
            TileObject rightNeighbor = getRight(location);

            if (rightNeighbor.type != TileObject.TileType.NON_TRAVERSABLE)
            {
                neighbors.Add(rightNeighbor);
            }
        }

        return(neighbors);
    }
    public Grid2DObject(Grid2DObject other)
    {
        this.dimensions = new Coord2DObject(other.dimensions);

        grid = new TileObject[dimensions.x, dimensions.y];

        for (int i = 0; i < dimensions.x; i++)
        {
            for (int j = 0; j < dimensions.y; j++)
            {
                Coord2DObject thisCoord2D   = new Coord2DObject(i, j);
                TileObject    thisOtherTile = other.getTile(thisCoord2D);
                setTile(thisOtherTile.type, thisCoord2D);
            }
        }
    }
    /// <summary>
    /// Sets types for bases, which are BASE_WIDTH x BASE_WIDTH corners in super.grid.
    /// Also initializes p1UpRight and p2LowLeft, which specify these rectangles.
    /// (I know it's bad practice to make those coordinates member variables instead of local variables,
    /// but I don't feel like passing them through every function that needs them
    /// and dude it's like 3AM rn)
    /// </summary>
    private void drawBases()
    {
        int gridX = base.dimensions.x;
        int gridY = base.dimensions.y;

        Coord2DObject p1LowLeft = new Coord2DObject(0, 0);

        p1UpRight = new Coord2DObject(p1LowLeft.x + BASE_WIDTH,
                                      p1LowLeft.y + BASE_WIDTH);
        Coord2DObject p2UpRight = new Coord2DObject(gridX - 1, gridY - 1);

        p2LowLeft = new Coord2DObject(p2UpRight.x - BASE_WIDTH + 1,
                                      p2UpRight.y - BASE_WIDTH + 1);

        base.setTypeRect(p1LowLeft, p1UpRight, TileObject.TileType.TRAVERSABLE, true);
        base.setTypeRect(p2LowLeft, p2UpRight, TileObject.TileType.TRAVERSABLE, true);
    }
Beispiel #17
0
    /// <summary>
    /// Sets the type of tiles on this path.
    /// Should be used to either set a path as traversable,
    /// or to "erase" by setting them to empty (or something else!).
    /// Note that you must first set this object's path by giving it 2 or more joints,
    /// otherwise you're trying to draw an ill-defined path
    /// and I'm going to abort your program.
    /// </summary>
    /// <param name="type">Type that all tiles on this path should be set to</param>
    /// <param name="prioritize">True if should override non-empty tiles, false otherwise</param>
    public void setPathType(TileObject.TileType type, bool prioritize)
    {
        Assert.raiseExceptions = true;
        Assert.IsTrue(joints.Count >= 2,
                      "Not enough joints in this path, here are all joints: " + joints.ToString());

        Coord2DObject firstJoint = joints[0];

        IEnumerator <Coord2DObject> e = joints.GetEnumerator();

        while (e.MoveNext())
        {
            Coord2DObject secondJoint = e.Current;

            grid.setTypeLine(firstJoint, secondJoint, type, thickness, prioritize);

            // Slide first joint
            // (second joint is slid using the while-loop condition
            firstJoint = secondJoint;
        }
    }
    /// <summary>
    /// Returns a list of random Coord2DObjects of specified size.
    /// As long as p1UpRight and p2LowLeft are initialized,
    /// you are guaranteed that this list will have no duplicate values,
    /// and will also not contain the values p1UpRight or p2LowLeft.
    /// Additionally, none of these points shall fall within the bases.
    /// </summary>
    /// <param name="amount">Number of random points to generate</param>
    /// <returns>List of distinct Coord2DObject's</returns>
    private List <Coord2DObject> getDistinctRandomPoints(int amount)
    {
        HashSet <Coord2DObject> pointsSet = new HashSet <Coord2DObject>();

        // Use a while loop instead of a for loop
        // because there's a small chance
        // that we could accidentally generate duplicate Coord2D's
        while (pointsSet.Count < amount)
        {
            Coord2DObject randCoord = getRandomNonBase();

            // These two will populate pointsSet later,
            // so check for duplicates now
            if (!randCoord.Equals(p1UpRight) && !randCoord.Equals(p2LowLeft))
            {
                pointsSet.Add(randCoord);
            }
        }

        // As far as this function is concerned,
        // order does not matter,
        // so we can clumsily return a list from our set
        return(new List <Coord2DObject>(pointsSet));
    }
    public void setTile(TileObject.TileType type, Coord2DObject location)
    {
        assertBounds(location);

        grid[location.x, location.y] = new TileObject(type, new Coord2DObject(location));
    }
Beispiel #20
0
 /// <summary>
 /// Checks if two coordinates are eligible to be a straight path.
 /// If they are diagonal relative to each other,
 /// this will return false.
 /// Otherwise, if they're on the same horizontal row or vertical column,
 /// this will return true.
 /// </summary>
 /// <param name="joint1">an endpoint</param>
 /// <param name="joint2">another endpoint</param>
 /// <returns>True if the points are compatible, false otherwise</returns>
 public bool areCompatibleJoints(Coord2DObject joint1, Coord2DObject joint2)
 {
     return(joint1.x == joint2.x || joint1.y == joint2.y);
 }
Beispiel #21
0
    /// <summary>
    /// Populates this Path's "joints" list with the best path between point1 and point2.
    /// This is accomplished by implementing Dijkstra's algorithm.
    /// This path's grid and joints MUST be initialized before this method is called.
    /// Algorithm adopted from the pseudocode found here:
    /// https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Pseudocode
    /// </summary>
    /// <param name="src">The coordinate that the path should start from</param>
    /// <param name="dest">The coordinate that the path should start from</param>
    private void populateBestPath(Coord2DObject src, Coord2DObject dest)
    {
        Grid2DObject tempGrid = new Grid2DObject(grid); // generate copy, maintaining tile types

        TileObject srcTile  = tempGrid.getTile(src);
        TileObject destTile = tempGrid.getTile(dest);

        srcTile.distance = 0;

        // Do some error checking
        Assert.raiseExceptions = true;
        Assert.IsTrue(!src.Equals(dest), "Attempted autopath to the same tile " + src.ToString());
        Assert.IsTrue(srcTile.type != TileObject.TileType.NON_TRAVERSABLE,
                      "Path attempted on srcTile  non-traversable tile " + srcTile.ToString() + " to destTile " + destTile.ToString());
        Assert.IsTrue(destTile.type != TileObject.TileType.NON_TRAVERSABLE,
                      "Path attempted on destTile non-traversable tile " + destTile.ToString() + " from srcTile " + srcTile.ToString());

        // Populate set Q
        HashSet <TileObject> setQ = new HashSet <TileObject>();

        foreach (TileObject t in tempGrid)
        {
            if (t.type != TileObject.TileType.NON_TRAVERSABLE)
            {
                setQ.Add(t);
            }
        }
        List <TileObject> listQ = new List <TileObject>(setQ);

        shuffleList <TileObject>(listQ);

        TileObject uTile = null;

        while (listQ.Count > 0)
        {
            // Get tile with minimum distance from setQ
            int runningMin = System.Int32.MaxValue;
            foreach (TileObject t in listQ)
            {
                if (t.distance < runningMin)
                {
                    runningMin = t.distance;
                    uTile      = t;
                }
            }

            // Make sure uTile is properly set,
            // then remove it from setQ
            Assert.IsTrue(uTile != null, "Minimum distance tile uTile not properly set");
            Assert.IsTrue(listQ.Contains(uTile), "setQ doesn't contain uTile " + uTile.ToString());
            listQ.Remove(uTile);

            // Break out if we've reached the destination,
            // we now need to construct the path via reverse iteration
            if (uTile == destTile)  // check for identity, not just equivalence
            {
                break;
            }

            // Update distances of all uTile's current neighbors
            HashSet <TileObject> uNeighbors = tempGrid.getTraversableNeighbors(uTile.location);

            foreach (TileObject thisNeighbor in uNeighbors)
            {
                int currentDist = uTile.distance + 1;

                if (currentDist < thisNeighbor.distance)
                {
                    thisNeighbor.distance = currentDist;
                    thisNeighbor.prev     = uTile;
                }
            }
        }



        // Ensure that uTile is actually usable
        Assert.IsTrue(uTile.prev != null || uTile == srcTile,
                      "Condition specified by Dijkstra's not met");

        // Populate joints by backtracing
        while (uTile != null)
        {
            joints.Add(uTile.location);
            uTile = uTile.prev;

            // Make sure if we're about to break out,
            // that we have enough joints to do so
            // (i.e. that we have at least 2 joints)
            Assert.IsTrue(!(uTile == null && joints.Count < 2),
                          "Not enough prev's? For sure not enough joints\n"
                          + "Perhaps src and dest are the same?\n"
                          + "src:  " + srcTile.ToString() + '\n'
                          + "dest: " + destTile.ToString() + '\n'
                          + "src.equals(dest)? " + src.Equals(dest));
        }
    }
 public bool canGoDown(Coord2DObject location)
 {
     return(checkBounds(new Coord2DObject(location.x, location.y - 1)));
 }
 public bool canGoRight(Coord2DObject location)
 {
     return(checkBounds(new Coord2DObject(location.x + 1, location.y)));
 }
Beispiel #24
0
 public TileObject(TileType type, Coord2DObject location)
 {
     init(type, System.Int32.MaxValue, null, location);
 }
 public GameGrid2DObject(Coord2DObject dimensions, int thickness, int landmarks, int baseWidth)
     : base(dimensions)
 {
     init(thickness, landmarks, baseWidth);
 }
 public char getChar(Coord2DObject location)
 {
     return(getTile(location).getChar());
 }
 public Coord2DObject(Coord2DObject other)
 {
     this.x = other.x;
     this.y = other.y;
 }
 public TileObject getTile(Coord2DObject location)
 {
     return(grid[location.x, location.y]);
 }
 public void assertBounds(Coord2DObject location)
 {
     Assert.raiseExceptions = true;
     Assert.IsTrue(checkBounds(location), "Invalid coordinate " + location.ToString());
 }