Skip to content
Snippets Groups Projects
Commit ad9330f9 authored by Susan Yuen's avatar Susan Yuen
Browse files

Changed path finding to use BFS

parent 6a60bc23
No related branches found
No related tags found
No related merge requests found
......@@ -100,13 +100,13 @@ namespace Controller
//initializes players and units
private void initializeGame()
{
graph = new Graph(50,32);
graph = new Graph(50, 32);
player1 = new Player();
player2 = new Player();
// load character sprite and set position
player1.addUnit(getNewUnit(UnitType.Warrior, new Vector2(4*32f, 6*32f),1));
player2.addUnit(getNewUnit(UnitType.Warrior, new Vector2(15*32f, 6*32f),2));
player1.addUnit(getNewUnit(UnitType.Warrior, new Vector2(4 * 32f, 6 * 32f), 1));
player2.addUnit(getNewUnit(UnitType.Warrior, new Vector2(15 * 32f, 6 * 32f), 2));
GameState.currentPlayer = (player1);
GameState.enemyPlayer = (player2);
......@@ -139,7 +139,7 @@ namespace Controller
{
mMenu.Close(); //close Main Menu screen
tut.Close(); //close How To Play Menu
Form GameForm = (Form)Form.FromHandle(Window.Handle);
Form GameForm = (Form)Form.FromHandle(Window.Handle);
GameForm.Opacity = 100; // make screen show
currentGameState = GameMenuState.Playing; //set game state to Playing
break;
......@@ -225,7 +225,7 @@ namespace Controller
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null); // begin spriteBatch
//only draw objects relevent to current game state
switch (currentGameState)
{
......@@ -242,31 +242,30 @@ namespace Controller
#endregion
#region When Unit is selected
//if unit is currently clicked on
if (GameState.playableUnitSelected)
{
Unit unit = GameState.selectedUnit;
//if unit is currently clicked on
if (GameState.playableUnitSelected)
{
Unit unit = GameState.selectedUnit;
if (!GameState.isAnimating)
{
if (GameState.beforeMove) // if unit has yet to move, display the overall move and attack range of unit
{
#region Highlight nodes
// Highlight movable nodes in blue
LinkedList<Node> moveableNodes = GameFunction.getMovableNodes(graph, unit);
foreach (Node move in moveableNodes)
{
spriteBatch.Draw(moveableNode, move.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
}
#region Highlight nodes
// Highlight movable nodes in blue
foreach (Node move in GameState.moveableNodes)
{
spriteBatch.Draw(moveableNode, move.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
}
// Highlight attackable nodes in red
LinkedList<Node> attackableNodes = GameFunction.getAttackableNodes(graph, unit);
foreach (Node attack in attackableNodes)
{
if (!moveableNodes.Contains(attack))
// Highlight attackable nodes in red
LinkedList<Node> attackableNodes = GameFunction.getAttackableNodes(graph, unit);
foreach (Node attack in attackableNodes)
{
spriteBatch.Draw(attackableNode, attack.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
if (!GameState.moveableNodes.Contains(attack))
{
spriteBatch.Draw(attackableNode, attack.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
}
}
}
#endregion
}
else // else if unit has already moved, only display the attack range
......@@ -274,7 +273,7 @@ namespace Controller
LinkedList<Node> attackableNodes = GameFunction.getAttackRangeAfterMoving(graph, unit);
foreach (Node attack in attackableNodes)
{
spriteBatch.Draw(attackableNode, attack.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
spriteBatch.Draw(attackableNode, attack.getPosition(), null, Color.White * 0.2f, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.9f);
}
}
......@@ -286,7 +285,7 @@ namespace Controller
{
if (button.getActive())
{
spriteBatch.Draw(button.getImage(), button.getPixelCoordinates(), null, Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0f);
spriteBatch.Draw(button.getImage(), button.getPixelCoordinates(), null, Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}
}
}
......@@ -295,9 +294,9 @@ namespace Controller
#region Character Info Screen player1
if(GameState.currentPlayer == player1){ //if player 1, prints info screen for player 1
//if player 1, prints info screen for player 1
if (GameState.currentPlayer == player1)
{
Vector2 statLocation = new Vector2(180, 533); //starting location for first stat
Vector2 increment = new Vector2(0, 11.5f); //increment downwards for each stat
Vector2 infoLocation = new Vector2(20, 513);
......@@ -311,7 +310,8 @@ namespace Controller
spriteBatch.Draw(unit.getCharInfo(), infoLocation, null, Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.7f); //draw charInfoBackground texture
}
else //else, info screen for player 2
//else, info screen for player 2
else
{
Vector2 statLocation = new Vector2(795, 533); //starting location for first stat
Vector2 increment = new Vector2(0, 11.5f); //increment downwards for each stat
......@@ -324,12 +324,12 @@ namespace Controller
}
spriteBatch.DrawString(largeFont, unit.Hp.ToString(), new Vector2(861, 512), Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.6f); //draws each stat
spriteBatch.Draw(unit.getCharInfo(), infoLocation, null, Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.7f); //draw charInfoBackground texture
}
#endregion
}
#endregion
}
#endregion
#region Player 2 Units
// draws units for player 2
for (int i = 0; i < player2.getNumOfUnits(); i++)
......@@ -339,9 +339,9 @@ namespace Controller
unit.getCurrentFrame(), Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 0.8f);
}
#endregion
spriteBatch.Draw(backGround, Vector2.Zero, null, Color.White, 0, Vector2.Zero, 1f, SpriteEffects.None, 1);
break;
}
......@@ -381,11 +381,10 @@ namespace Controller
return null;
}
// manually sets obstacle nodes for graph
private void setObstacles(Graph graph)
{
// manually sets obstacle nodes for graph
for (int y=5; y<=14; y++)
for (int y = 5; y <= 14; y++)
{
graph.getNode(2, y).setIsObstacle(true);
}
......@@ -404,12 +403,12 @@ namespace Controller
graph.getNode(7, 4).setIsObstacle(true);
graph.getNode(7, 14).setIsObstacle(true);
graph.getNode(8, 4).setIsObstacle(true);
for (int y=14; y<=17; y++)
for (int y = 14; y <= 17; y++)
{
graph.getNode(8, y).setIsObstacle(true);
}
graph.getNode(9, 4).setIsObstacle(true);
for(int y=17; y<=20; y++)
for (int y = 17; y <= 20; y++)
{
graph.getNode(9, y).setIsObstacle(true);
}
......
......@@ -24,7 +24,7 @@ namespace Controller
foreach (Unit unit in player.getUnits())
{
MenuButton[] menuButtons = unit.getMenuButtons();
for (int i=0; i<menuButtons.Count(); i++)
for (int i = 0; i < menuButtons.Count(); i++)
{
menuButtons[i].setActive(true);
}
......@@ -38,11 +38,12 @@ namespace Controller
int range = unit.getEquippedWeapon().getRange(); // use this to determine all hitable squares
return null;
}
// checks if specified unit can perform actions
public static bool hasUnitFinishedActions(Unit unit)
{
if (!unit.isButtonActive(MenuButtonType.Attack)) {
if (!unit.isButtonActive(MenuButtonType.Attack))
{
return true;
}
if (!unit.isButtonActive(MenuButtonType.Wait))
......@@ -70,7 +71,7 @@ namespace Controller
// TODO
return false;
}
// checks if the node is attackable based on the unit's class and position
// I THINK THIS WOULD WORK MUCH SIMPLER BASED ON EQUIPPED WEAPON NOT UNIT TYPE!!
// IF logic < equippedweapon.range or IF logic < weapon with highest range.
......@@ -108,22 +109,24 @@ namespace Controller
}
// returns a list of nodes that the unit can move onto
public static LinkedList<Node> getMovableNodes(Graph graph, Unit unit)
public static LinkedList<Node> setMovableNodes(Graph graph, Unit unit)
{
LinkedList<Node> moveableNodes = new LinkedList<Node>();
Node currentNode = graph.getNode(unit.Position);
// iterate through all nodes in the graph
for (int x=0; x<graph.getWidth(); x++)
for (int x = 0; x < graph.getWidth(); x++)
{
for (int y=0; y<graph.getHeight(); y++)
for (int y = 0; y < graph.getHeight(); y++)
{
// if a path exists to that node, add it to list of moveable nodes
if (pathFinder(graph, unit, currentNode, graph.getNode(x, y)) != null && !graph.getNode(x, y).isOccupied()) {
if (pathFinder(graph, unit, currentNode, graph.getNode(x, y)) != null && !graph.getNode(x, y).isOccupied())
{
moveableNodes.AddLast(graph.getNode(x, y));
}
}
}
GameState.moveableNodes = moveableNodes;
return moveableNodes;
}
......@@ -131,10 +134,9 @@ namespace Controller
public static LinkedList<Node> getAttackableNodes(Graph graph, Unit unit)
{
LinkedList<Node> attackableNodes = new LinkedList<Node>();
LinkedList<Node> moveableNodes = getMovableNodes(graph, unit);
// determine attackable nodes for each moveable node
foreach(Node moveableNode in moveableNodes)
foreach (Node moveableNode in GameState.moveableNodes)
{
// iterate through entire grid to determine attackable nodes for moveable node
for (int x = 0; x < graph.getWidth(); x++)
......@@ -181,78 +183,79 @@ namespace Controller
return null;
}
int numOfMovementsLeft = unit.getMovability();
int endNodeX = end.getPositionX();
int endNodeY = end.getPositionY();
bool nodeAdded = false;
// uses a variation of breadth-first search
Queue<LinkedList<Node>> paths = new Queue<LinkedList<Node>>(); // queue of paths
LinkedList<Node> path = new LinkedList<Node>();
path.AddFirst(start);
// sets up queue of paths
LinkedList<Node> startPath = new LinkedList<Node>();
startPath.AddFirst(start); // adds the starting node to starting path
paths.Enqueue(startPath);
while (numOfMovementsLeft > 0)
while (paths.Count != 0) // while queue isn't empty
{
int currentNodeX = path.Last().getPositionX();
int currentNodeY = path.Last().getPositionY();
// if it is already at the end node, break
if (currentNodeX == endNodeX && currentNodeY == endNodeY)
{
break;
}
// check if left gets closer to end node
if (!nodeAdded && Math.Abs(currentNodeX - 1 - endNodeX) < Math.Abs(currentNodeX - endNodeX)
&& !graph.getNode(currentNodeX - 1, currentNodeY).isOccupied()
&& !graph.getNode(currentNodeX - 1, currentNodeY).getIsObstacle())
LinkedList<Node> path = paths.Dequeue(); // get the first path from the queue
Node lastNode = path.Last(); // get the last node from the path
if (lastNode == end) // if last node is desired end node, return path
{
path.AddLast(graph.getNode(currentNodeX - 1, currentNodeY));
nodeAdded = true;
return path;
}
// check if right gets closer to end node
if (!nodeAdded && Math.Abs(currentNodeX + 1 - endNodeX) < Math.Abs(currentNodeX - endNodeX)
&& !graph.getNode(currentNodeX + 1, currentNodeY).isOccupied()
&& !graph.getNode(currentNodeX + 1, currentNodeY).getIsObstacle())
if (path.Count <= unit.getMovability()) // only consider paths within the unit's movability
{
path.AddLast(graph.getNode(currentNodeX + 1, currentNodeY));
nodeAdded = true;
// add paths with adjacent nodes of last node to queue
foreach (Node adjacentNode in getAdjacentNodes(graph, lastNode))
{
if (!path.Contains(adjacentNode)) // if adjacent node doesn't already exist in the path
{
LinkedList<Node> newPath = new LinkedList<Node>();
// copies path to new path
foreach (Node node in path)
{
newPath.AddLast(node);
}
// add adjacent node to new path that will be enqueued
newPath.AddLast(adjacentNode);
paths.Enqueue(newPath);
}
}
}
}
return null;
}
// check if up gets closer to end node
if (!nodeAdded && Math.Abs(currentNodeY + 1 - endNodeY) < Math.Abs(currentNodeY - endNodeY)
&& !graph.getNode(currentNodeX, currentNodeY + 1).isOccupied()
&& !graph.getNode(currentNodeX, currentNodeY + 1).getIsObstacle())
{
path.AddLast(graph.getNode(currentNodeX, currentNodeY + 1));
nodeAdded = true;
}
// returns movable adjacent nodes; used for BFS path finding
private static LinkedList<Node> getAdjacentNodes(Graph graph, Node node)
{
LinkedList<Node> adjacentNodes = new LinkedList<Node>();
Node upNode = graph.getNode(node.getPositionX(), node.getPositionY() + 1);
Node downNode = graph.getNode(node.getPositionX(), node.getPositionY() - 1);
Node rightNode = graph.getNode(node.getPositionX() + 1, node.getPositionY());
Node leftNode = graph.getNode(node.getPositionX() - 1, node.getPositionY());
// check if down gets closer to end node
if (!nodeAdded && Math.Abs(currentNodeY - 1 - endNodeY) < Math.Abs(currentNodeY - endNodeY)
&& !graph.getNode(currentNodeX, currentNodeY - 1).isOccupied()
&& !graph.getNode(currentNodeX, currentNodeY - 1).getIsObstacle())
{
path.AddLast(graph.getNode(currentNodeX, currentNodeY - 1));
nodeAdded = true;
}
// up node
if (!upNode.isOccupied() && !upNode.getIsObstacle())
{
adjacentNodes.AddLast(upNode);
}
// if no movement gets closer to the end node, there is no valid movement
if (!nodeAdded)
{
break;
}
// down node
if (!downNode.isOccupied() && !downNode.getIsObstacle())
{
adjacentNodes.AddLast(downNode);
}
numOfMovementsLeft--;
nodeAdded = false;
// left node
if (!leftNode.isOccupied() && !leftNode.getIsObstacle())
{
adjacentNodes.AddLast(leftNode);
}
// if the node the algorithm ends on isn't the same as the end node, there is no valid path
if (!(path.Last() == end))
// right node
if (!rightNode.isOccupied() && !rightNode.getIsObstacle())
{
return null;
adjacentNodes.AddLast(rightNode);
}
return path;
return adjacentNodes;
}
public static void firstTurn()
......
......@@ -10,13 +10,15 @@ namespace Model
{
public static readonly int SCREEN_HEIGHT = 640;
public static readonly int SCREEN_WIDTH = 960;
public static bool playableUnitSelected { get; set; } //indicates playable selected unit
public static Unit selectedUnit { get; set; } // indicates current selected unit
public static Player currentPlayer { get; set; } // indicates player 1
public static Player enemyPlayer { get; set; } // indicates enemy player (player 2)
public static bool dropDownMenuOpen { get; set; } // indicates whether drop down menu should be open
public static bool beforeMove { get; set; } // true before unit moves, false after it moves. Used to determine what tiles are highlighted
public static bool isAnimating { get; set; } // indicates whether an animation sequence is on screen
public static bool isAnimating { get; set; } // indicates whether an animation sequence is on screen
public static LinkedList<Node> moveableNodes { get; set; } // holds moveable nodes that can be retreived without calling path finding
}
enum GameMenuState // enumerated list for different possible Game States
......
......@@ -66,5 +66,16 @@ namespace Model
{
return height;
}
public void setAllNodesUnmarked()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
nodes[i, j].setIsMarked(false);
}
}
}
}
}
......@@ -47,7 +47,6 @@ namespace Controller
}
Vector2 mouseClickCoordinates = new Vector2(currentMouseState.X, currentMouseState.Y); // mouse click coordinates
Node nodeClickedOn = graph.getNode(mouseClickCoordinates);
#region Check if a unit is clicked
......@@ -82,6 +81,7 @@ namespace Controller
turnState = TurnState.Wait;
}
}
// if player clicks when a unit is not selected ...
else
{
......@@ -90,6 +90,7 @@ namespace Controller
if (unit != null)
{
setSelectedUnit(unit, true);
GameFunction.setMovableNodes(graph, unit);
GameState.dropDownMenuOpen = (true);
GameState.beforeMove = (true);
}
......@@ -102,7 +103,6 @@ namespace Controller
// sets selection of unit state inside GameFunction and the unit itself
private static void setSelectedUnit(Unit unit, bool selected)
{
GameState.playableUnitSelected = selected;
GameState.selectedUnit = unit;
}
......@@ -134,6 +134,8 @@ namespace Controller
foreach (Node node in path)
{
animateUnitPosition(graph, unit, node);
unit.Position = (new Tuple<int, int>(node.getPositionX(), node.getPositionY()));
node.setUnitOnNode(unit);
GameState.dropDownMenuOpen = (true);
}
......@@ -147,7 +149,7 @@ namespace Controller
{
int nodePixelX = node.getPositionX() * 32;
int nodePixelY = node.getPositionY() * 32;
int pixelDifference = 4;
graph.getNode(unit.Position).setUnitOnNode(null);
while (unit.PixelCoordinates.X != nodePixelX)
......@@ -155,15 +157,14 @@ namespace Controller
if (unit.PixelCoordinates.X < nodePixelX)
{
unit.animate(2);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X + 4, unit.PixelCoordinates.Y);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X + pixelDifference, unit.PixelCoordinates.Y);
Game.Instance.Tick();
Thread.Sleep(40);
}
else
{
unit.animate(1);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X - 4, unit.PixelCoordinates.Y);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X - pixelDifference, unit.PixelCoordinates.Y);
Game.Instance.Tick();
Thread.Sleep(40);
}
......@@ -174,20 +175,18 @@ namespace Controller
if (unit.PixelCoordinates.Y < nodePixelY)
{
unit.animate(0);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X, unit.PixelCoordinates.Y + 4);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X, unit.PixelCoordinates.Y + pixelDifference);
Game.Instance.Tick();
Thread.Sleep(40);
}
else
{
unit.animate(3);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X, unit.PixelCoordinates.Y - 4);
unit.PixelCoordinates = new Vector2(unit.PixelCoordinates.X, unit.PixelCoordinates.Y - pixelDifference);
Game.Instance.Tick();
Thread.Sleep(40);
}
}
node.setUnitOnNode(unit);
}
// returns a menu button that was clicked; if no menu button was clicked, returns null
......
......@@ -13,6 +13,7 @@ namespace Model
private Unit unitOnNode; // holds the unit that is on the node
private int positionX; // position x on the grid
private int positionY; // position y on the grid
private bool isMarked; // used for BFS path finding
public Node(int x, int y)
{
......@@ -77,5 +78,15 @@ namespace Model
}
return false;
}
public void setIsMarked(bool b)
{
isMarked = b;
}
public bool getIsMarked()
{
return isMarked;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment