Merge pull request #262 from AnonymusRaccoon/ai_inprovements

Ai inprovements
This commit is contained in:
Clément Le Bihan
2021-06-20 22:22:08 +02:00
committed by GitHub
8 changed files with 170 additions and 80 deletions

74
Bot.md Normal file
View File

@@ -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();
```

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -56,7 +56,7 @@ namespace BBM
return path;
}
std::vector<Vector2f> LuaMap::getNeighbors(Vector2f node) const
std::vector<Vector2f> LuaMap::getNeighbors(Vector2f node, bool throughBreakable) const
{
std::vector<Vector2f> 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<Vector2f> LuaMap::pathfind(Vector2f root, Vector2f target) const
std::vector<Vector2f> LuaMap::pathfind(Vector2f root, Vector2f target, bool throughBreakable) const
{
std::vector<Vector2f> closed;
std::vector<Vector2f> 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<const LuaMap *>(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<const LuaMap *>(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<const LuaMap *>(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<const LuaMap *>(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<const LuaMap *>(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);

View File

@@ -28,7 +28,7 @@ namespace BBM
void setPlayer(Vector3f pos);
//! @brief A star pathfinding between two points
std::vector<Vector2f> pathfind(Vector2f, Vector2f) const;
std::vector<Vector2f> pathfind(Vector2f root, Vector2f target, bool throughBreakable) const;
//! @brief find a safe space for current player
Vector2f findSafeSpace(const std::vector<std::vector<int>> &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<std::vector<int>> _map;
@@ -72,6 +81,9 @@ namespace BBM
//! @brief player position
Vector2f _player;
//! @brief other players position
std::vector<Vector2f> _enemies;
//! @brief rounded player position
Vector2f _roundedPlayer;
@@ -83,7 +95,7 @@ namespace BBM
std::unordered_map<Vector2f, Vector2f> &cameFrom, Vector2f node) const;
//! @brief get neighbors of node for a_star
std::vector<Vector2f> getNeighbors(Vector2f node) const;
std::vector<Vector2f> getNeighbors(Vector2f node, bool throughBreakable) const;
std::vector<Vector2f> _dirs = {
Vector2f(1, 0), Vector2f(-1, 0), Vector2f(0, 1), Vector2f(0, -1)

View File

@@ -19,11 +19,13 @@ namespace BBM
void IAControllableSystem::UpdateMapInfos(WAL::ViewEntity<PositionComponent, ControllableComponent, IAControllableComponent, BombHolderComponent> &entity)
{
_players.clear();
_luamap._enemies.clear();
if (!_wal.getScene())
return;
for (auto &[other, pos, _] : _wal.getScene()->view<PositionComponent, ScoreComponent>()) {
_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<PositionComponent, ControllableComponent, IAControllableComponent, BombHolderComponent> &entity)