mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Rework of MineCube.cs and further improvements to CalculatePath() (#2014)
* Add function to determine if the client is executing a walking process
* Add comments
* Remove test bot entry
* Add funtion to approach a block as close as possible
* Add funtion to approach a block as close as possible
* Add funtion to approach a block as close as possible
* Add comment to function in McClient.cs
* Improve concurrency and reduce potential calculation power
* Apply code suggestions
* Apply code suggestions
* Improve CalculatePath() function to allow approaching
* Fix typo in MinecraftClient/ChatBot.cs
* Add comments to Chatbot fucntion
* Add break to for loop to exit quicker
* Allow to give a maxOffset to the goal
* Comment the sample bot again.
* Add parameter for calculation timeout
* Remove TestBot again
* Implement timeout in Chatbot class
* Remove test commands
* Update comment in Chatbot.cs
* Set timeout to default 5 sec
* Change order of parameters back
* Add suggested improvements
* Move task and fix missing methods in .NET 4.0
* Create switch for tool handling
* Remove unused function
* Improve movement
* Improve performance of CalculatePath()
- Replace Hashset OpenSet with a Binary Heap
- Temporary remove maxOffset / minOffset features
- Round start location for easier calculation
- Add 0.5 to each location in reconstruct path to avoid getting stuck
on edges
* Add diagonal movement
* Remove direct block movement
- causes kick for invalid packet movement if moving on the block you are
currently standing on
* Floor start in A* and improve diagonal walking check
* Add helperfunctions to McClient.cs
* Prevent client from falling into danger
* Add comment to function and remove dependencies
* Add comments
* Remove debug settings
Co-authored-by: ORelio <ORelio@users.noreply.github.com>
This commit is contained in:
parent
ea6788278d
commit
aa1f54d0d8
5 changed files with 787 additions and 502 deletions
|
|
@ -1,165 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// A row of blocks that will be mined
|
||||
/// </summary>
|
||||
public class Row
|
||||
{
|
||||
public readonly List<Location> BlocksInRow;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a row of blocks
|
||||
/// </summary>
|
||||
/// <param name="bIL"> Enter a list of blocks </param>
|
||||
public Row(List<Location> blocksInRow = null)
|
||||
{
|
||||
BlocksInRow = blocksInRow ?? new List<Location>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Several rows are summarized in a layer
|
||||
/// </summary>
|
||||
public class Layer
|
||||
{
|
||||
public readonly List<Row> RowsInLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Add a new row to this layer
|
||||
/// </summary>
|
||||
/// <param name="givenRow"> enter a row that should be added </param>
|
||||
/// <returns> Index of the last row </returns>
|
||||
public void AddRow(Row givenRow = null)
|
||||
{
|
||||
RowsInLayer.Add(givenRow ?? new Row());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a layer
|
||||
/// </summary>
|
||||
/// <param name="rTM"> Enter a list of rows </param>
|
||||
public Layer(List<Row> rowInLayer = null)
|
||||
{
|
||||
RowsInLayer = rowInLayer ?? new List<Row>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Several layers result in a cube
|
||||
/// </summary>
|
||||
public class Cube
|
||||
{
|
||||
public readonly List<Layer> LayersInCube;
|
||||
|
||||
/// <summary>
|
||||
/// Add a new layer to the cube
|
||||
/// </summary>
|
||||
/// <param name="givenLayer"> Enter a layer that should be added </param>
|
||||
/// <returns> Index of the last layer </returns>
|
||||
public void AddLayer(Layer givenLayer = null)
|
||||
{
|
||||
LayersInCube.Add(givenLayer ?? new Layer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a cube
|
||||
/// </summary>
|
||||
/// <param name="lTM"> Enter a list of layers </param>
|
||||
public Cube(List<Layer> layerInCube = null)
|
||||
{
|
||||
LayersInCube = layerInCube ?? new List<Layer>();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CubeFromWorld
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a cube of blocks out of two coordinates.
|
||||
/// </summary>
|
||||
/// <param name="startBlock">Start Location</param>
|
||||
/// <param name="stopBlock">Stop Location</param>
|
||||
/// <returns>A cube of blocks consisting of Layers, Rows and single blocks</returns>
|
||||
public static Cube GetBlocksAsCube(World currentWorld, Location startBlock, Location stopBlock, List<Material> materialList = null, bool isBlacklist = true)
|
||||
{
|
||||
// Initialize cube to mine.
|
||||
Cube cubeToMine = new Cube();
|
||||
|
||||
// Get the distance between start and finish as Vector.
|
||||
Location vectorToStopPosition = stopBlock - startBlock;
|
||||
|
||||
// Initialize Iteration process
|
||||
int[] iterateX = GetNumbersFromTo(0, Convert.ToInt32(Math.Round(vectorToStopPosition.X))).ToArray();
|
||||
int[] iterateY = GetNumbersFromTo(0, Convert.ToInt32(Math.Round(vectorToStopPosition.Y))).ToArray();
|
||||
int[] iterateZ = GetNumbersFromTo(0, Convert.ToInt32(Math.Round(vectorToStopPosition.Z))).ToArray();
|
||||
|
||||
// Iterate through all coordinates relative to the start block.
|
||||
foreach (int y in iterateY)
|
||||
{
|
||||
Layer tempLayer = new Layer();
|
||||
foreach (int x in iterateX)
|
||||
{
|
||||
Row tempRow = new Row();
|
||||
foreach (int z in iterateZ)
|
||||
{
|
||||
if (materialList != null && materialList.Count > 0)
|
||||
{
|
||||
Location tempLocation = new Location(Math.Round(startBlock.X + x), Math.Round(startBlock.Y + y), Math.Round(startBlock.Z + z));
|
||||
Material tempLocationMaterial = currentWorld.GetBlock(tempLocation).Type;
|
||||
|
||||
// XOR
|
||||
// If blacklist == true and it does not contain the material (false); Add it.
|
||||
// If blacklist == false (whitelist) and it contains the item (true); Add it.
|
||||
if (isBlacklist ^ materialList.Contains(tempLocationMaterial))
|
||||
{
|
||||
tempRow.BlocksInRow.Add(tempLocation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tempRow.BlocksInRow.Add(new Location(Math.Round(startBlock.X + x), Math.Round(startBlock.Y + y), Math.Round(startBlock.Z + z)));
|
||||
}
|
||||
}
|
||||
if (tempRow.BlocksInRow.Count > 0)
|
||||
{
|
||||
tempLayer.AddRow(tempRow);
|
||||
}
|
||||
}
|
||||
if (tempLayer.RowsInLayer.Count > 0)
|
||||
{
|
||||
cubeToMine.AddLayer(tempLayer);
|
||||
}
|
||||
}
|
||||
|
||||
return cubeToMine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all numbers between from and to.
|
||||
/// </summary>
|
||||
/// <param name="start">Number to start</param>
|
||||
/// <param name="end">Number to stop</param>
|
||||
/// <returns>All numbers between the start and stop number, including the stop number</returns>
|
||||
private static List<int> GetNumbersFromTo(int start, int stop)
|
||||
{
|
||||
List<int> tempList = new List<int>();
|
||||
if (start <= stop)
|
||||
{
|
||||
for (int i = start; i <= stop; i++)
|
||||
{
|
||||
tempList.Add(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = start; i >= stop; i--)
|
||||
{
|
||||
tempList.Add(i);
|
||||
}
|
||||
}
|
||||
return tempList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,13 @@ namespace MinecraftClient.Mapping
|
|||
West = 1,
|
||||
North = 2,
|
||||
East = 3,
|
||||
|
||||
Up = 4,
|
||||
Down = 5
|
||||
Down = 5,
|
||||
|
||||
NorthEast = 6,
|
||||
SouthEast = 7,
|
||||
SouthWest = 8,
|
||||
NorthWest = 9,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -61,7 +60,7 @@ namespace MinecraftClient.Mapping
|
|||
}
|
||||
else
|
||||
{
|
||||
foreach (Direction dir in new []{ Direction.East, Direction.West, Direction.North, Direction.South })
|
||||
foreach (Direction dir in new[] { Direction.East, Direction.West, Direction.North, Direction.South })
|
||||
if (CanMove(world, location, dir) && IsOnGround(world, Move(location, dir)) && (allowUnsafe || IsSafe(world, Move(location, dir))))
|
||||
availableMoves.Add(Move(location, dir));
|
||||
availableMoves.Add(Move(location, Direction.Down));
|
||||
|
|
@ -164,72 +163,77 @@ namespace MinecraftClient.Mapping
|
|||
/// <returns>A list of locations, or null if calculation failed</returns>
|
||||
public static Queue<Location> CalculatePath(World world, Location start, Location goal, bool allowUnsafe, int maxOffset, int minOffset, CancellationToken ct)
|
||||
{
|
||||
|
||||
// This is a bad configuration
|
||||
if (minOffset > maxOffset)
|
||||
throw new ArgumentException("minOffset must be lower or equal to maxOffset", "minOffset");
|
||||
|
||||
|
||||
// Round start coordinates for easier calculation
|
||||
start = new Location(Math.Floor(start.X), Math.Floor(start.Y), Math.Floor(start.Z));
|
||||
|
||||
// We always use distance squared so our limits must also be squared.
|
||||
minOffset *= minOffset;
|
||||
maxOffset *= maxOffset;
|
||||
|
||||
Location current = new Location(); // Location that is currently processed
|
||||
Location closestGoal = new Location(); // Closest Location to the goal. Used for approaching if goal can not be reached or was not found.
|
||||
HashSet<Location> ClosedSet = new HashSet<Location>(); // The set of locations already evaluated.
|
||||
HashSet<Location> OpenSet = new HashSet<Location>(new[] { start }); // The set of tentative nodes to be evaluated, initially containing the start node
|
||||
Dictionary<Location, Location> Came_From = new Dictionary<Location, Location>(); // The map of navigated nodes.
|
||||
///---///
|
||||
// Prepare variables and datastructures for A*
|
||||
///---///
|
||||
|
||||
// Dictionary that contains the relation between all coordinates and resolves the final path
|
||||
Dictionary<Location, Location> CameFrom = new Dictionary<Location, Location>();
|
||||
// Create a Binary Heap for all open positions => Allows fast access to Nodes with lowest scores
|
||||
BinaryHeap openSet = new BinaryHeap();
|
||||
// Dictionary to keep track of the G-Score of every location
|
||||
Dictionary<Location, int> gScoreDict = new Dictionary<Location, int>();
|
||||
|
||||
Dictionary<Location, int> g_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||
g_score[start] = 0; // Cost from start along best known path.
|
||||
// Estimated total cost from start to goal through y.
|
||||
Dictionary<Location, int> f_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||
f_score[start] = (int)start.DistanceSquared(goal); //heuristic_cost_estimate(start, goal)
|
||||
// Set start values for variables
|
||||
openSet.Insert(0, (int)start.DistanceSquared(goal), start);
|
||||
gScoreDict[start] = 0;
|
||||
BinaryHeap.Node current = null;
|
||||
|
||||
while (OpenSet.Count > 0)
|
||||
///---///
|
||||
// Start of A*
|
||||
///---///
|
||||
|
||||
// Execute while we have nodes to process and we are not cancelled
|
||||
while (openSet.Count() > 0 && !ct.IsCancellationRequested)
|
||||
{
|
||||
current = //the node in OpenSet having the lowest f_score[] value
|
||||
OpenSet.Select(location => f_score.ContainsKey(location)
|
||||
? new KeyValuePair<Location, int>(location, f_score[location])
|
||||
: new KeyValuePair<Location, int>(location, int.MaxValue))
|
||||
.OrderBy(pair => pair.Value).
|
||||
// Sort for h-score (f-score - g-score) to get smallest distance to goal if f-scores are equal
|
||||
ThenBy(pair => f_score[pair.Key]-g_score[pair.Key]).First().Key;
|
||||
|
||||
// Only assert a value if it is of actual use later
|
||||
if (maxOffset > 0 && ClosedSet.Count > 0)
|
||||
// Get the block that currently is closest to the goal
|
||||
closestGoal = ClosedSet.OrderBy(checkedLocation => checkedLocation.DistanceSquared(goal)).First();
|
||||
// Get the root node of the Binary Heap
|
||||
// Node with the lowest F-Score or lowest H-Score on tie
|
||||
current = openSet.GetRootLocation();
|
||||
|
||||
// Stop when goal is reached or we are close enough
|
||||
if (current == goal || (minOffset > 0 && current.DistanceSquared(goal) <= minOffset))
|
||||
return ReconstructPath(Came_From, current);
|
||||
else if (ct.IsCancellationRequested)
|
||||
break; // Return if we are cancelled
|
||||
|
||||
OpenSet.Remove(current);
|
||||
ClosedSet.Add(current);
|
||||
|
||||
foreach (Location neighbor in GetAvailableMoves(world, current, allowUnsafe))
|
||||
// Return if goal found and no maxOffset was given OR current node is between minOffset and maxOffset
|
||||
if ((current.Location == goal && maxOffset <= 0) || (maxOffset > 0 && current.H_score >= minOffset && current.H_score <= maxOffset))
|
||||
{
|
||||
if (ct.IsCancellationRequested)
|
||||
break; // Stop searching for blocks if we are cancelled.
|
||||
if (ClosedSet.Contains(neighbor))
|
||||
continue; // Ignore the neighbor which is already evaluated.
|
||||
int tentative_g_score = g_score[current] + (int)current.DistanceSquared(neighbor); //dist_between(current,neighbor) // length of this path.
|
||||
if (!OpenSet.Contains(neighbor)) // Discover a new node
|
||||
OpenSet.Add(neighbor);
|
||||
else if (tentative_g_score >= g_score[neighbor])
|
||||
continue; // This is not a better path.
|
||||
return ReconstructPath(CameFrom, current.Location);
|
||||
}
|
||||
|
||||
// This path is the best until now. Record it!
|
||||
Came_From[neighbor] = current;
|
||||
g_score[neighbor] = tentative_g_score;
|
||||
f_score[neighbor] = g_score[neighbor] + (int)neighbor.DistanceSquared(goal); //heuristic_cost_estimate(neighbor, goal)
|
||||
// Discover neighbored blocks
|
||||
foreach (Location neighbor in GetAvailableMoves(world, current.Location, allowUnsafe))
|
||||
{
|
||||
// If we are cancelled: break
|
||||
if (ct.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
// tentative_gScore is the distance from start to the neighbor through current
|
||||
int tentativeGScore = current.G_score + (int)current.Location.DistanceSquared(neighbor);
|
||||
|
||||
// If the neighbor is not in the gScoreDict OR its current tentativeGScore is lower than the previously saved one:
|
||||
if (!gScoreDict.ContainsKey(neighbor) || (gScoreDict.ContainsKey(neighbor) && tentativeGScore < gScoreDict[neighbor]))
|
||||
{
|
||||
// Save the new relation between the neighbored block and the current one
|
||||
CameFrom[neighbor] = current.Location;
|
||||
gScoreDict[neighbor] = tentativeGScore;
|
||||
|
||||
// If this location is not already included in the Binary Heap: save it
|
||||
if (!openSet.ContainsLocation(neighbor))
|
||||
openSet.Insert(tentativeGScore, (int)neighbor.DistanceSquared(goal), neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Goal could not be reached. Set the path to the closest location if close enough
|
||||
if (maxOffset == int.MaxValue || goal.DistanceSquared(closestGoal) <= maxOffset)
|
||||
return ReconstructPath(Came_From, closestGoal);
|
||||
//// Goal could not be reached. Set the path to the closest location if close enough
|
||||
if (current != null && (maxOffset == int.MaxValue || openSet.MinH_ScoreNode.H_score <= maxOffset))
|
||||
return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
|
@ -242,16 +246,196 @@ namespace MinecraftClient.Mapping
|
|||
/// <returns>the path that leads to current from the start position</returns>
|
||||
private static Queue<Location> ReconstructPath(Dictionary<Location, Location> Came_From, Location current)
|
||||
{
|
||||
List<Location> total_path = new List<Location>(new[] { current });
|
||||
// Add 0.5 to walk over the middle of a block and avoid collisions
|
||||
List<Location> total_path = new List<Location>(new[] { current + new Location(0.5, 0, 0.5) });
|
||||
while (Came_From.ContainsKey(current))
|
||||
{
|
||||
current = Came_From[current];
|
||||
total_path.Add(current);
|
||||
total_path.Add(current + new Location(0.5, 0, 0.5));
|
||||
}
|
||||
total_path.Reverse();
|
||||
return new Queue<Location>(total_path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A datastructure to store Locations as Nodes and provide them in sorted and queued order.
|
||||
/// !!!
|
||||
/// CAN BE REPLACED WITH PriorityQueue IN .NET-6
|
||||
/// https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.priorityqueue-2?view=net-6.0
|
||||
/// !!!
|
||||
/// </summary>
|
||||
public class BinaryHeap
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a location and its attributes
|
||||
/// </summary>
|
||||
public class Node
|
||||
{
|
||||
// Distance to start
|
||||
public int G_score;
|
||||
// Distance to Goal
|
||||
public int H_score;
|
||||
public int F_score { get { return H_score + G_score; } }
|
||||
|
||||
public Location Location;
|
||||
|
||||
public Node(int g_score, int h_score, Location loc)
|
||||
{
|
||||
this.G_score = g_score;
|
||||
this.H_score = h_score;
|
||||
Location = loc;
|
||||
}
|
||||
}
|
||||
|
||||
// List which contains all nodes in form of a Binary Heap
|
||||
private List<Node> heapList;
|
||||
// Hashset for quick checks of locations included in the heap
|
||||
private HashSet<Location> locationList;
|
||||
public Node MinH_ScoreNode;
|
||||
|
||||
public BinaryHeap()
|
||||
{
|
||||
heapList = new List<Node>();
|
||||
locationList = new HashSet<Location>();
|
||||
MinH_ScoreNode = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a new location in the heap
|
||||
/// </summary>
|
||||
/// <param name="newG_Score">G-Score of the location</param>
|
||||
/// <param name="newH_Score">H-Score of the location</param>
|
||||
/// <param name="loc">The location</param>
|
||||
public void Insert(int newG_Score, int newH_Score, Location loc)
|
||||
{
|
||||
// Begin at the end of the list
|
||||
int i = heapList.Count;
|
||||
|
||||
// Temporarily save the node created with the parameters to allow comparisons
|
||||
Node newNode = new Node(newG_Score, newH_Score, loc);
|
||||
|
||||
// Add new note to the end of the list
|
||||
heapList.Add(newNode);
|
||||
locationList.Add(loc);
|
||||
|
||||
// Save node with the smallest H-Score => Distance to goal
|
||||
if (MinH_ScoreNode == null || newNode.H_score < MinH_ScoreNode.H_score)
|
||||
MinH_ScoreNode = newNode;
|
||||
|
||||
// There is no need of sorting for one node.
|
||||
if (i > 0)
|
||||
{
|
||||
/// Go up the heap from child to parent and move parent down...
|
||||
// while we are not looking at the root node AND the new node has better attributes than the parent node ((i - 1) / 2)
|
||||
while (i > 0 && FirstNodeBetter(newNode /* Current Child */, heapList[(i - 1) / 2] /* Coresponding Parent */))
|
||||
{
|
||||
// Move parent down and replace current child -> New free space is created
|
||||
heapList[i] = heapList[(i - 1) / 2];
|
||||
// Select the next parent to check
|
||||
i = (i - 1) / 2;
|
||||
}
|
||||
|
||||
/// Nodes were moved down at position I there is now a free space at the correct position for our new node:
|
||||
// Insert new node in position
|
||||
heapList[i] = newNode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtain the root which represents the node the the best attributes currently
|
||||
/// </summary>
|
||||
/// <returns>node with the best attributes currently</returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public Node GetRootLocation()
|
||||
{
|
||||
// The heap is empty. There is nothing to return.
|
||||
if (heapList.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The heap is empty.");
|
||||
}
|
||||
|
||||
// Save the root node
|
||||
Node rootNode = heapList[0];
|
||||
locationList.Remove(rootNode.Location);
|
||||
|
||||
// Temporarirly store the last item's value.
|
||||
Node lastNode = heapList[heapList.Count - 1];
|
||||
|
||||
// Remove the last value.
|
||||
heapList.RemoveAt(heapList.Count - 1);
|
||||
|
||||
if (heapList.Count > 0)
|
||||
{
|
||||
// Start at the first index.
|
||||
int currentParentPos = 0;
|
||||
|
||||
/// Go through the heap from root to bottom...
|
||||
// Continue until the halfway point of the heap.
|
||||
while (currentParentPos < heapList.Count / 2)
|
||||
{
|
||||
// Select the left child of the current parent
|
||||
int currentChildPos = (2 * currentParentPos) + 1;
|
||||
|
||||
// If the currently selected child is not the last entry of the list AND right child has better attributes
|
||||
if ((currentChildPos < heapList.Count - 1) && FirstNodeBetter(heapList[currentChildPos + 1], heapList[currentChildPos]))
|
||||
{
|
||||
// Select the right child
|
||||
currentChildPos++;
|
||||
}
|
||||
|
||||
// If the last item is smaller than both siblings at the
|
||||
// current height, break.
|
||||
if (FirstNodeBetter(lastNode, heapList[currentChildPos]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Move the item at index j up one level.
|
||||
heapList[currentParentPos] = heapList[currentChildPos];
|
||||
// Move index i to the appropriate branch.
|
||||
currentParentPos = currentChildPos;
|
||||
}
|
||||
// Insert the last node into the currently free position
|
||||
heapList[currentParentPos] = lastNode;
|
||||
}
|
||||
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two nodes and evaluates their position to the goal.
|
||||
/// </summary>
|
||||
/// <param name="firstNode">First node to compare</param>
|
||||
/// <param name="secondNode">Second node to compare</param>
|
||||
/// <returns>True if the first node has a more promissing position to the goal than the second</returns>
|
||||
private static bool FirstNodeBetter(Node firstNode, Node secondNode)
|
||||
{
|
||||
// Is the F_score smaller?
|
||||
return (firstNode.F_score < secondNode.F_score) ||
|
||||
// If F_score is equal, evaluate the h-score
|
||||
(firstNode.F_score == secondNode.F_score && firstNode.H_score < secondNode.H_score);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of the heap
|
||||
/// </summary>
|
||||
/// <returns>size of the heap</returns>
|
||||
public int Count()
|
||||
{
|
||||
return heapList.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the heap contains a node with a certain location
|
||||
/// </summary>
|
||||
/// <param name="loc">Location to check</param>
|
||||
/// <returns>true if a node with the given location is in the heap</returns>
|
||||
public bool ContainsLocation(Location loc)
|
||||
{
|
||||
return locationList.Contains(loc);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= LOCATION PROPERTIES ========= */
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -313,22 +497,47 @@ namespace MinecraftClient.Mapping
|
|||
{
|
||||
switch (direction)
|
||||
{
|
||||
// Move vertical
|
||||
case Direction.Down:
|
||||
return !IsOnGround(world, location);
|
||||
case Direction.Up:
|
||||
return (IsOnGround(world, location) || IsSwimming(world, location))
|
||||
&& !world.GetBlock(Move(Move(location, Direction.Up), Direction.Up)).Type.IsSolid();
|
||||
|
||||
// Move horizontal
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
case Direction.South:
|
||||
case Direction.North:
|
||||
return !world.GetBlock(Move(location, direction)).Type.IsSolid()
|
||||
&& !world.GetBlock(Move(Move(location, direction), Direction.Up)).Type.IsSolid();
|
||||
return PlayerFitsHere(world, Move(location, direction));
|
||||
|
||||
// Move diagonal
|
||||
case Direction.NorthEast:
|
||||
return PlayerFitsHere(world, Move(location, Direction.North)) && PlayerFitsHere(world, Move(location, Direction.East)) && PlayerFitsHere(world, Move(location, direction));
|
||||
case Direction.SouthEast:
|
||||
return PlayerFitsHere(world, Move(location, Direction.South)) && PlayerFitsHere(world, Move(location, Direction.East)) && PlayerFitsHere(world, Move(location, direction));
|
||||
case Direction.SouthWest:
|
||||
return PlayerFitsHere(world, Move(location, Direction.South)) && PlayerFitsHere(world, Move(location, Direction.West)) && PlayerFitsHere(world, Move(location, direction));
|
||||
case Direction.NorthWest:
|
||||
return PlayerFitsHere(world, Move(location, Direction.North)) && PlayerFitsHere(world, Move(location, Direction.West)) && PlayerFitsHere(world, Move(location, direction));
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown direction", "direction");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates if a player fits in this location
|
||||
/// </summary>
|
||||
/// <param name="world">Current world</param>
|
||||
/// <param name="location">Location to check</param>
|
||||
/// <returns>True if a player is able to stand in this location</returns>
|
||||
public static bool PlayerFitsHere(World world, Location location)
|
||||
{
|
||||
return !world.GetBlock(location).Type.IsSolid()
|
||||
&& !world.GetBlock(Move(location, Direction.Up)).Type.IsSolid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an updated location for moving in the specified direction
|
||||
/// </summary>
|
||||
|
|
@ -350,10 +559,13 @@ namespace MinecraftClient.Mapping
|
|||
{
|
||||
switch (direction)
|
||||
{
|
||||
// Move vertical
|
||||
case Direction.Down:
|
||||
return new Location(0, -1, 0);
|
||||
case Direction.Up:
|
||||
return new Location(0, 1, 0);
|
||||
|
||||
// Move horizontal straight
|
||||
case Direction.East:
|
||||
return new Location(1, 0, 0);
|
||||
case Direction.West:
|
||||
|
|
@ -362,6 +574,17 @@ namespace MinecraftClient.Mapping
|
|||
return new Location(0, 0, 1);
|
||||
case Direction.North:
|
||||
return new Location(0, 0, -1);
|
||||
|
||||
// Move horizontal diagonal
|
||||
case Direction.NorthEast:
|
||||
return Move(Direction.North) + Move(Direction.East);
|
||||
case Direction.SouthEast:
|
||||
return Move(Direction.South) + Move(Direction.East);
|
||||
case Direction.SouthWest:
|
||||
return Move(Direction.South) + Move(Direction.West);
|
||||
case Direction.NorthWest:
|
||||
return Move(Direction.North) + Move(Direction.West);
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unknown direction", "direction");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue