diff --git a/Bot.md b/Bot.md new file mode 100644 index 00000000..71f85f80 --- /dev/null +++ b/Bot.md @@ -0,0 +1,74 @@ +# Bot documentation + +A bot in this bomberman is using a lua script as way to choose what to do +So you can make your own with some helper functions given from C++ + +## Update function + +Each frame, the game will call the "Update" function in the script. +This is the same lua state as the last call so this means you can set global variables to keep data between frames + +Update function should take no arguments. + +## Map Blocks + +```lua +nothing = 0 +breakable = 1 +hole = 2 +bumper = 4 +unbreakable = 7 +bomb = 10 +``` + +## Registered functions + +```lua +-- getMap returns a table of the blocks of the map +-- From 1 to 17 {{0, 1, 2 ..}, ..} +function getMap(); + +-- getDanger returns a table of the danger zone on the map +-- From 1 to 17 {{0, 1, 2 ..}, ..} +-- value is number of seconds before explosion +function getDanger(); + +-- getPath returns a table of nodes of a path from A to B {{x = X, y = Y}, ...} +-- @param x1 should be int +-- @param y1 should be int +-- @param x2 should be int +-- @param y2 should be int +-- @param throughBreakable bool path is going through breakables blocks or not +function getPath(x1, y1, x2, y2, throughBreakable); + +-- getPlayer returns player pos as {x = xPlayer, y = yPlayer} +function getPlayer(); + +-- getPlayerRound returns player pos with rounded value {x = xPlayer, y = yPlayer} +function getPlayerRound(); + +-- getDangerLevel returns danger level at [xpos, ypos] +function getDangerLevel(xpos, ypos); + +-- getDangerLevelPlayer get danger level on the position of the player +function getDangerLevelPlayer(); + +-- getBlockType returns block type at [xpos, ypos] +function getBlockType(xpos, ypos); + +-- getClosestSafeSpace returns the block next to player where the player should go to to the closest safe space +-- returns player pos if no path is found +function getClosestSafeSpace(); + +-- canPutBombSafe returns true if player can put a bomb and find a path to safe space if bomb is put +function canPutBombSafe(); + +-- getRadius returns the explosion radius of the current player +function getRadius(); + +-- getEnemies returns a table with enemies position {{x = X, y = Y}, ...} +function getEnemies(); + +-- getEnemies returns a table with enemies position rounded {{x = X, y = Y}, ...} +function getEnemiesRound(); +``` \ No newline at end of file diff --git a/assets/ai_scripts/john.lua b/assets/ai_scripts/john.lua index f0a01d06..f73dff4e 100644 --- a/assets/ai_scripts/john.lua +++ b/assets/ai_scripts/john.lua @@ -41,71 +41,6 @@ function PrintMap(map, MaxX, maxZ) end end -function getNeighborsDefend(node) - local neighbors = {} - for _, dir in ipairs(Dirs) do - local neighborX = node.x + dir.x - local neighborY = node.y + dir.y - if neighborY <= MaxY and neighborX <= MaxX then - if neighborY >= 0 and neighborX >= 0 then - if Map[neighborX][neighborY] == 0 and Danger[neighborX][neighborY] ~= 1 then - table.insert(neighbors, {x = neighborX, y = neighborY}) - end - end - end - end - if #neighbors == 0 and Danger[node.x][node.y] <= 1 then - for _, dir in ipairs(Dirs) do - local neighborX = node.x + dir.x - local neighborY = node.y + dir.y - if neighborY <= MaxY and neighborX <= MaxX then - if neighborY >= 0 and neighborX >= 0 then - if Map[neighborX][neighborY] == 0 then - table.insert(neighbors, {x = neighborX, y = neighborY}) - end - end - end - end - end - return neighbors -end - -function dist(nodeA, nodeB) - return math.sqrt(math.pow(nodeB.x - nodeA.x, 2) + math.pow(nodeB.y - nodeA.y, 2)) -end - -function getNeighborAttack(node) - log("atta") - local neighbors = {} - for _, dir in ipairs(Dirs) do - local neighborX = node.x + dir.x - local neighborY = node.y + dir.y - if neighborY <= MaxY and neighborX <= MaxX then - if neighborY >= 0 and neighborX >= 0 then - if Map[neighborX][neighborY] <= 1 and Danger[neighborX][neighborY] ~= 1 then - table.insert(neighbors, {x = neighborX, y = neighborY}) - end - end - end - end - return neighbors -end - -function getPathToEnemy(player, enemies) - local minDist = 100000 - local res = {} - for _, enemy in ipairs(enemies) do - local currDist = dist(player, enemy) - if currDist < minDist and enemy.x ~= player.x and enemy.y ~= player.y then - minDist, res = currDist, enemy - end - end - local path = pathfind(player, res, getNeighborAttack) - return path -end - - - function getPathToSafeSpace(player) local res = getClosestSafeSpace() log("run to") @@ -123,7 +58,6 @@ LastTarget = nil math.randomseed(os.time()) function Update() log("NEW FRAME") - --local path = getPath(0, 0, 16, 16); local player = getPlayer() if LastTarget ~= nil then diff --git a/lib/LuaGate/sources/LuaGate.cpp b/lib/LuaGate/sources/LuaGate.cpp index 5e6a3584..04347a6d 100644 --- a/lib/LuaGate/sources/LuaGate.cpp +++ b/lib/LuaGate/sources/LuaGate.cpp @@ -70,6 +70,11 @@ namespace LuaG return res; } + bool State::getBool(int idx) + { + return lua_toboolean(_state, idx); + } + float State::getNumber(int idx) { return lua_tonumber(_state, idx); diff --git a/lib/LuaGate/sources/LuaGate.hpp b/lib/LuaGate/sources/LuaGate.hpp index 30305fe2..ee067a89 100644 --- a/lib/LuaGate/sources/LuaGate.hpp +++ b/lib/LuaGate/sources/LuaGate.hpp @@ -52,6 +52,9 @@ namespace LuaG //! @brief Get return Number bool getReturnBool(void); + //! @brief Get bool at index in the stack + bool getBool(int index); + //! @brief Get Number at index in the stack float getNumber(int index); diff --git a/sources/Component/IAControllable/IAControllableComponent.cpp b/sources/Component/IAControllable/IAControllableComponent.cpp index c91a75d9..dcfc3069 100644 --- a/sources/Component/IAControllable/IAControllableComponent.cpp +++ b/sources/Component/IAControllable/IAControllableComponent.cpp @@ -20,6 +20,12 @@ namespace BBM { if (std::filesystem::exists(scriptPath)) { _state.dofile(scriptPath); + _state.getGlobal("Update"); + if (lua_isfunction(_state.getState(), -1)) { + _state.popLast(); + } else { + throw Error("Lua script doesn't have Update function"); + } } else { throw Error("Couldn't load lua script: " + scriptPath); diff --git a/sources/Map/LuaMap.cpp b/sources/Map/LuaMap.cpp index 33ffdc3b..cd3967cd 100644 --- a/sources/Map/LuaMap.cpp +++ b/sources/Map/LuaMap.cpp @@ -56,7 +56,7 @@ namespace BBM return path; } - std::vector LuaMap::getNeighbors(Vector2f node) const + std::vector LuaMap::getNeighbors(Vector2f node, bool throughBreakable) const { std::vector neighbors; for (auto &dir : _dirs) { @@ -65,7 +65,8 @@ namespace BBM continue; if (neighbor.y >= 17 || neighbor.x >= 17) continue; - if (_map[neighbor.y][neighbor.x] == 0 && + if ((_map[neighbor.y][neighbor.x] == 0 || + _map[neighbor.y][neighbor.x] == throughBreakable) && _danger[neighbor.y][neighbor.x] != 1) neighbors.push_back(neighbor); } @@ -83,7 +84,7 @@ namespace BBM return neighbors; } - std::vector LuaMap::pathfind(Vector2f root, Vector2f target) const + std::vector LuaMap::pathfind(Vector2f root, Vector2f target, bool throughBreakable) const { std::vector closed; std::vector open; @@ -117,7 +118,7 @@ namespace BBM } open.erase(std::remove(open.begin(), open.end(), current), open.end()); closed.push_back(current); - auto neighbors = getNeighbors(current); + auto neighbors = getNeighbors(current, throughBreakable); for (auto &neighbor : neighbors) { if (std::find(closed.begin(), closed.end(), neighbor) != closed.end()) continue; @@ -224,14 +225,16 @@ namespace BBM int LuaMap::getPath(lua_State *L) { LuaG::State state(L); - auto y2 = state.getNumber(-1); - auto x2 = state.getNumber(-2); - auto y1 = state.getNumber(-3); - auto x1 = state.getNumber(-4); - const LuaMap *map = reinterpret_cast(state.getPointer(state.getFirstUpValueIdx())); + auto throughBreakable = state.getBool(-1); + auto y2 = state.getNumber(-2); + auto x2 = state.getNumber(-3); + auto y1 = state.getNumber(-4); + auto x1 = state.getNumber(-5); + + const LuaMap *map = reinterpret_cast(state.getPointer(state.getFirstUpValueIdx())); Vector2f fst(x1, y1); Vector2f snd(x2, y2); - auto path = map->pathfind(fst, snd); + auto path = map->pathfind(fst, snd, throughBreakable); int index = 1; state.newTable(); for (auto &r : path) { @@ -319,6 +322,54 @@ namespace BBM return 1; } + int LuaMap::getRadius(lua_State *L) + { + LuaG::State state(L); + const LuaMap *map = reinterpret_cast(state.getPointer(state.getFirstUpValueIdx())); + state.push(map->currRadius); + return 1; + } + + int LuaMap::getEnemies(lua_State *L) + { + LuaG::State state(L); + const LuaMap *map = reinterpret_cast(state.getPointer(state.getFirstUpValueIdx())); + int index = 1; + state.newTable(); + for (auto &r : map->_enemies) { + state.push(index++); + state.newTable(); + state.push("x"); + state.push(r.x); + state.setTable(); + state.push("y"); + state.push(r.y); + state.setTable(); + state.setTable(); + } + return 1; + } + + int LuaMap::getEnemiesRound(lua_State *L) + { + LuaG::State state(L); + const LuaMap *map = reinterpret_cast(state.getPointer(state.getFirstUpValueIdx())); + int index = 1; + state.newTable(); + for (auto &r : map->_enemies) { + state.push(index++); + state.newTable(); + state.push("x"); + state.push(std::round(r.x)); + state.setTable(); + state.push("y"); + state.push(std::round(r.y)); + state.setTable(); + state.setTable(); + } + return 1; + } + int LuaMap::canPutBomb(lua_State *L) { LuaG::State state(L); diff --git a/sources/Map/LuaMap.hpp b/sources/Map/LuaMap.hpp index 43719e39..ee53c3a0 100644 --- a/sources/Map/LuaMap.hpp +++ b/sources/Map/LuaMap.hpp @@ -28,7 +28,7 @@ namespace BBM void setPlayer(Vector3f pos); //! @brief A star pathfinding between two points - std::vector pathfind(Vector2f, Vector2f) const; + std::vector pathfind(Vector2f root, Vector2f target, bool throughBreakable) const; //! @brief find a safe space for current player Vector2f findSafeSpace(const std::vector> &dangerMap) const; @@ -63,6 +63,15 @@ namespace BBM //! @brief Check if current player can put a bomb with an escape static int canPutBomb(lua_State *L); + //! @brief Get current explosion radius of the player + static int getRadius(lua_State *L); + + //! @brief Get enemies position + static int getEnemies(lua_State *L); + + //! @brief Get enemies position rounded + static int getEnemiesRound(lua_State *L); + //! @brief map blocks in 2D grid std::vector> _map; @@ -72,6 +81,9 @@ namespace BBM //! @brief player position Vector2f _player; + //! @brief other players position + std::vector _enemies; + //! @brief rounded player position Vector2f _roundedPlayer; @@ -83,7 +95,7 @@ namespace BBM std::unordered_map &cameFrom, Vector2f node) const; //! @brief get neighbors of node for a_star - std::vector getNeighbors(Vector2f node) const; + std::vector getNeighbors(Vector2f node, bool throughBreakable) const; std::vector _dirs = { Vector2f(1, 0), Vector2f(-1, 0), Vector2f(0, 1), Vector2f(0, -1) diff --git a/sources/System/IAControllable/IAControllableSystem.cpp b/sources/System/IAControllable/IAControllableSystem.cpp index 6d67c3bb..55056b3e 100644 --- a/sources/System/IAControllable/IAControllableSystem.cpp +++ b/sources/System/IAControllable/IAControllableSystem.cpp @@ -19,11 +19,13 @@ namespace BBM void IAControllableSystem::UpdateMapInfos(WAL::ViewEntity &entity) { - _players.clear(); + _luamap._enemies.clear(); if (!_wal.getScene()) return; for (auto &[other, pos, _] : _wal.getScene()->view()) { - _players.push_back(MapInfo(pos.position, MapGenerator::NOTHING)); + if (other == entity) + continue; + _luamap._enemies.push_back(Vector2f(pos.position.x, pos.position.z)); } if (_cached) return; @@ -86,6 +88,9 @@ namespace BBM state.registerClosure(&_luamap, "getBlockType", LuaMap::getBlockType); state.registerClosure(&_luamap, "getClosestSafeSpace", LuaMap::getClosestSafeSpace); state.registerClosure(&_luamap, "canPutBombSafe", LuaMap::canPutBomb); + state.registerClosure(&_luamap, "getRadius", LuaMap::getRadius); + state.registerClosure(&_luamap, "getEnemies", LuaMap::getEnemies); + state.registerClosure(&_luamap, "getEnemiesRound", LuaMap::getEnemiesRound); } void IAControllableSystem::onFixedUpdate(WAL::ViewEntity &entity)