diff --git a/example/pyramid.cpp b/example/pyramid.cpp index b25c609..78d61c0 100644 --- a/example/pyramid.cpp +++ b/example/pyramid.cpp @@ -29,8 +29,7 @@ int main() { int min_height = *std::min_element(heights.begin(), heights.end()); // Build rings, diminishing up to pyramid height - mcpp::Coordinate base_pt = heights.base_pt(); - base_pt.y = min_height; + mcpp::Coordinate base_pt = heights.base_pt().with_height(min_height); int side_len = pyramid_base_len; for (int i = 0; i < PYRAMID_HEIGHT; i++) { make_ring(base_pt + mcpp::Coordinate(i, i, i), side_len - (i * 2)); diff --git a/include/mcpp/coordinate.h b/include/mcpp/coordinate.h index 3bd4917..3ab1186 100644 --- a/include/mcpp/coordinate.h +++ b/include/mcpp/coordinate.h @@ -7,6 +7,9 @@ * */ namespace mcpp { + +struct Coordinate2D; + /** * Represented using integers since sub-unit coordinates are not of particular * relevance. Allows for operations such as addition between coordinates. @@ -49,6 +52,8 @@ struct Coordinate { */ Coordinate operator+(const Coordinate& obj) const; + Coordinate operator+(const Coordinate2D& obj) const; + /** * @brief Checks if two Coordinate objects are equal. * @@ -93,6 +98,106 @@ struct Coordinate { friend std::ostream& operator<<(std::ostream& out, const Coordinate& coord); }; +/** + * @brief Height-agnostic coordinate class. + * + * Represented using integers since sub-unit coordinates are not of particular + * relevance. Allows for operations such as addition between flat coordinates. + */ +struct Coordinate2D { + /** + * @brief Constructs a Coordinate2D object with integer values. + * + * @param x The x-coordinate. + * @param z The z-coordinate. + */ + constexpr Coordinate2D(int x, int z) : x(x), z(z) {} + + /** + * @brief Constructs a Coordinate2D object with zero values. + */ + constexpr Coordinate2D() : x(0), z(0) {} + + /** + * @brief Constructs a Coordinate2D object with double values. + * + * @param x The x-coordinate as a double. + * @param z The z-coordinate as a double. + */ + constexpr Coordinate2D(double x, double z) : x(static_cast(x)), z(static_cast(z)) {} + + /** + * @brief Constructs a Coordinate2D object from a Coordinate object. + * + * @param coord The Coordinate object. + */ + constexpr Coordinate2D(const Coordinate& coord) : x(coord.x), z(coord.z) {} + + /** + * @brief Constructs a Coordinate object from a Coordinate2D object and a + * y value. + * + * @param coord The Coordinate2D object. + * @param y The y value. + */ + Coordinate with_height(int y) const; + + /** + * @brief Adds two Coordinate2D objects. + * + * @param obj The Coordinate2D object to add. + * @return A new Coordinate2D object representing the sum of the two + * coordinates. + */ + Coordinate2D operator+(const Coordinate2D& obj) const; + + /** + * @brief Checks if two Coordinate2D objects are equal. + * + * @param obj The Coordinate2D object to compare with. + * @return True if the flat coordinates are equal, false otherwise. + */ + bool operator==(const Coordinate2D& obj) const; + + /** + * @brief Checks if two Coordinate2D objects are not equal. + * + * @param obj The Coordinate2D object to compare with. + * @return True if the flat coordinates are not equal, false otherwise. + */ + bool operator!=(const Coordinate2D& obj) const; + + /** + * @brief Subtracts one Coordinate2D object from another. + * + * @param obj The Coordinate2D object to subtract. + * @return A new Coordinate2D object representing the difference between + * the two coordinates. + */ + Coordinate2D operator-(const Coordinate2D& obj) const; + + /** + * @brief Implements hash algorithm for Coordinate2D object using non-negative + * mapping and weighted coordinate values. + * + * @param obj The Coordinate2D object to hash. + * @return Hash of Coordinate2D object. + */ + std::size_t operator()(const Coordinate2D& obj) const; + + /** + * @brief Outputs the Coordinate2D object to an ostream. + * + * @param out The output stream. + * @param coord The Coordinate2D object to output. + * @return The output stream with the Coordinate object's values. + */ + friend std::ostream& operator<<(std::ostream& out, const Coordinate2D& coord); + + int x; + int z; +}; + /** * @brief Convert coordinate to string representation. * diff --git a/include/mcpp/heightmap.h b/include/mcpp/heightmap.h index f477cd0..54b864a 100644 --- a/include/mcpp/heightmap.h +++ b/include/mcpp/heightmap.h @@ -12,14 +12,15 @@ namespace mcpp { */ struct HeightMap { private: - Coordinate _base_pt; + Coordinate2D _base_pt; uint16_t _x_len; uint16_t _z_len; std::unique_ptr _raw_heights; public: // Constructors and assignment - HeightMap(const Coordinate& loc1, const Coordinate& loc2, const std::vector& heights); + HeightMap(const Coordinate2D& loc1, const Coordinate2D& loc2, + const std::vector& heights); ~HeightMap() = default; HeightMap(const HeightMap& other) @@ -45,10 +46,10 @@ struct HeightMap { /** * Get the height at a Minecraft coordinate if saved inside the height map - * @param loc: Coordinate in Minecraft world to access in the map + * @param loc: Coordinate2D in Minecraft world to access in the map * @return: height at specified coordinate */ - int16_t get_worldspace(const Coordinate& loc) const; + int16_t get_worldspace(const Coordinate2D& loc) const; /** * Fill a coordinate inplace with the highest y coordinate at the `loc`'s x @@ -73,7 +74,7 @@ struct HeightMap { * Gets the minimum coordinate in the HeightMap. * @return the minimum coordinate in the HeightMap. */ - Coordinate base_pt() const; + Coordinate2D base_pt() const; /** * @brief An iterator for the HeightMap structure. diff --git a/include/mcpp/mcpp.h b/include/mcpp/mcpp.h index 528e92c..918c158 100644 --- a/include/mcpp/mcpp.h +++ b/include/mcpp/mcpp.h @@ -134,19 +134,33 @@ class MinecraftConnection { [[nodiscard]] Chunk getBlocks(const Coordinate& loc1, const Coordinate& loc2) const; /** - * @brief Returns the height of the specific provided x and z coordinate + * @brief Returns the height of the specific provided 2D coordinate * * ***IMPORTANT:*** * DO NOT USE FOR LARGE AREAS, IT WILL BE VERY SLOW * USE getHeights() INSTEAD * - * Gets the y-value of the highest non-air block at the specified (x, z) + * Gets the y-value of the highest non-air block at the specified 2D * coordinate. - * @param x - * @param z + * @param loc 2D coordinate * @return Returns the integer y-height at the requested coordinate. */ - [[nodiscard]] int32_t getHeight(int x, int z) const; + [[nodiscard]] int32_t getHeight(Coordinate2D loc) const; + + /** + * @brief Returns the coordinate with the x, z, and in-world height of the + * specific provided 2D coordinate + * + * ***IMPORTANT:*** + * DO NOT USE FOR LARGE AREAS, IT WILL BE VERY SLOW + * USE getHeights() INSTEAD + * + * Gets the y-value of the highest non-air block at the specified 2D + * coordinate, and creates a new 3D coordinate. + * @param loc 2D coordinate + * @return Returns the coordinate with the filled-in height. + */ + Coordinate fillHeight(Coordinate2D loc) const; /** * @brief Provides a scaled option of the getHeight call to allow for @@ -154,11 +168,11 @@ class MinecraftConnection { * * \par USE THIS instead of getHeight in a for loop. * - * @param loc1 - * @param loc2 + * @param loc1 1st corner of rectangle + * @param loc2 2nd corner of rectangle * @return Returns a vector of integers representing the 2D area of heights. */ - [[nodiscard]] HeightMap getHeights(const Coordinate& loc1, const Coordinate& loc2) const; + [[nodiscard]] HeightMap getHeights(const Coordinate2D& loc1, const Coordinate2D& loc2) const; // NOLINTEND(readability-identifier-naming) }; diff --git a/src/coordinate.cpp b/src/coordinate.cpp index 37f6630..74a10f3 100644 --- a/src/coordinate.cpp +++ b/src/coordinate.cpp @@ -10,6 +10,14 @@ Coordinate Coordinate::operator+(const Coordinate& obj) const { return result; } +Coordinate Coordinate::operator+(const Coordinate2D& obj) const { + Coordinate result; + result.x = this->x + obj.x; + result.y = this->y; + result.z = this->z + obj.z; + return result; +} + bool Coordinate::operator==(const Coordinate& obj) const { return (this->x == obj.x) && (this->y == obj.y) && (this->z == obj.z); } @@ -44,4 +52,46 @@ std::ostream& operator<<(std::ostream& out, const Coordinate& coord) { out << to_string(coord); return out; } + +Coordinate Coordinate2D::with_height(int y) const { return Coordinate(this->x, y, this->z); } + +Coordinate2D Coordinate2D::operator+(const Coordinate2D& obj) const { + Coordinate2D result; + result.x = this->x + obj.x; + result.z = this->z + obj.z; + return result; +} + +bool Coordinate2D::operator==(const Coordinate2D& obj) const { + return (this->x == obj.x) && (this->z == obj.z); +} + +bool Coordinate2D::operator!=(const Coordinate2D& obj) const { return !(*this == obj); } + +Coordinate2D Coordinate2D::operator-(const Coordinate2D& obj) const { + Coordinate2D result; + result.x = this->x - obj.x; + result.z = this->z - obj.z; + return result; +} + +std::size_t Coordinate2D::operator()(const mcpp::Coordinate2D& obj) const { + int lower = -3e7, upper = 3e7; + size_t base = upper - lower + 1; + + size_t nx = obj.x - lower; + size_t nz = obj.z - lower; + + return nx * base + nz; +} + +std::string to_string(const Coordinate2D& coord) { + using std::to_string; + return "(" + to_string(coord.x) + "," + to_string(coord.z) + ")"; +} + +std::ostream& operator<<(std::ostream& out, const Coordinate2D& coord) { + out << to_string(coord); + return out; +} } // namespace mcpp diff --git a/src/heightmap.cpp b/src/heightmap.cpp index 2c47e92..d043b04 100644 --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -2,7 +2,7 @@ #include namespace mcpp { -HeightMap::HeightMap(const Coordinate& loc1, const Coordinate& loc2, +HeightMap::HeightMap(const Coordinate2D& loc1, const Coordinate2D& loc2, const std::vector& heights) { _base_pt = Coordinate{ std::min(loc1.x, loc2.x), @@ -37,7 +37,7 @@ int16_t HeightMap::get(int x, int z) const { return _raw_heights[(x * _z_len) + z]; } -int16_t HeightMap::get_worldspace(const Coordinate& loc) const { +int16_t HeightMap::get_worldspace(const Coordinate2D& loc) const { return get(loc.x - _base_pt.x, loc.z - _base_pt.z); } @@ -47,5 +47,5 @@ uint16_t HeightMap::x_len() const { return _x_len; } uint16_t HeightMap::z_len() const { return _z_len; } -Coordinate HeightMap::base_pt() const { return _base_pt; } +Coordinate2D HeightMap::base_pt() const { return _base_pt; } } // namespace mcpp diff --git a/src/mcpp.cpp b/src/mcpp.cpp index 786d078..dbec165 100644 --- a/src/mcpp.cpp +++ b/src/mcpp.cpp @@ -103,12 +103,18 @@ Chunk MinecraftConnection::getBlocks(const Coordinate& loc1, const Coordinate& l return Chunk{loc1, loc2, result}; } -int MinecraftConnection::getHeight(int x, int z) const { - std::string response = _conn->send_receive_command("world.getHeight", x, z); +int MinecraftConnection::getHeight(Coordinate2D loc) const { + std::string response = _conn->send_receive_command("world.getHeight", loc.x, loc.z); return stoi(response); } -HeightMap MinecraftConnection::getHeights(const Coordinate& loc1, const Coordinate& loc2) const { +Coordinate MinecraftConnection::fillHeight(Coordinate2D loc) const { + int y = this->getHeight(loc); + return Coordinate(loc.x, y, loc.z); +} + +HeightMap MinecraftConnection::getHeights(const Coordinate2D& loc1, + const Coordinate2D& loc2) const { std::string response = _conn->send_receive_command("world.getHeights", loc1.x, loc1.z, loc2.x, loc2.z); @@ -116,7 +122,7 @@ HeightMap MinecraftConnection::getHeights(const Coordinate& loc1, const Coordina std::vector parsed; split_response(response, parsed); - return {loc1, loc2, parsed}; + return HeightMap{loc1, loc2, parsed}; } } // namespace mcpp diff --git a/test/minecraft_tests.cpp b/test/minecraft_tests.cpp index cd3ce46..177383c 100644 --- a/test/minecraft_tests.cpp +++ b/test/minecraft_tests.cpp @@ -115,7 +115,7 @@ TEST_CASE("Test the main mcpp class") { SUBCASE("getHeight") { Coordinate heightTestLoc(300, 200, 300); mc.setBlock(heightTestLoc, Blocks::DIRT); - auto height = mc.getHeight(heightTestLoc.x, heightTestLoc.z); + auto height = mc.getHeight(heightTestLoc); CHECK_EQ(height, heightTestLoc.y); // Clean up @@ -384,14 +384,14 @@ TEST_CASE("HeightMap functionality") { // Copy assignment mc.setBlocks({10, 310, 10}, {20, 320, 20}, Blocks::AIR); mc.setBlocks({10, 310, 10}, {20, 310, 20}, Blocks::STONE); - auto map = mc.getHeights({10, 10, 10}, {20, 20, 20}); + auto map = mc.getHeights({10, 10}, {20, 20}); HeightMap map_copy = map; // Contains 310 CHECK_EQ(map.get(0, 0), map_copy.get(0, 0)); CHECK_EQ(map.get(0, 0), 310); // Reassignment mc.setBlock({10, 311, 10}, Blocks::STONE); - map = mc.getHeights({10, 10, 10}, {20, 20, 20}); // Now contains 311 + map = mc.getHeights({10, 10}, {20, 20}); // Now contains 311 CHECK_NE(map.get(0, 0), map_copy.get(0, 0)); CHECK_EQ(map.get(0, 0), 311); @@ -402,7 +402,7 @@ TEST_CASE("HeightMap functionality") { // Copy constructor auto map_copy2 = HeightMap(map); // Contains 310 mc.setBlock({10, 312, 10}, Blocks::STONE); - map = mc.getHeights({10, 10, 10}, {20, 20, 20}); // Now contains 312 + map = mc.getHeights({10, 10}, {20, 20}); // Now contains 312 CHECK_NE(map_copy2.get(0, 0), 312); CHECK_EQ(map.get(0, 0), 312);