Implement ghost movement and scatter behavior
Each ghost is trying to reach a point beyond the corner of the board, which make them run in circle
This commit is contained in:
parent
93186dc8b9
commit
c2cfb8c734
5 changed files with 121 additions and 12 deletions
|
@ -23,9 +23,9 @@ static const uint8_t board[ROWS][COLUMNS] = {
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 10
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 10
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 11
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 11
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 5, 5, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 12
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 5, 5, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 12
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 13
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 13
|
||||||
{ 3, 2, 2, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 3 }, // 14
|
{ 3, 2, 2, 2, 2, 2, 1, 2, 2, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 3 }, // 14
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 15
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 15
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 16
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 16
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 17
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 17
|
||||||
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 18
|
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 18
|
||||||
|
@ -57,6 +57,19 @@ bool Board::isWalkableForGhost(Position point, float d, Direction direction) con
|
||||||
return isWalkable(point, d, direction, false);
|
return isWalkable(point, d, direction, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Board::isWalkable(Position point) const {
|
||||||
|
return board_state[int(point.y)][int(point.x)] != uint8_t(Cell::wall);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Board::isWalkableForGost(Position point, Position origin) const
|
||||||
|
{
|
||||||
|
return isWalkable(point) && (isInPen(origin) || !isInPen(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Board::isInPen(Position point) const {
|
||||||
|
return board_state[int(point.y)][int(point.x)] == uint8_t(Cell::pen);
|
||||||
|
}
|
||||||
|
|
||||||
bool Board::isWalkable(Position point, float position_delta, Direction direction, bool pacman) const {
|
bool Board::isWalkable(Position point, float position_delta, Direction direction, bool pacman) const {
|
||||||
if (point.x <= 0 || point.x >= COLUMNS - 1)
|
if (point.x <= 0 || point.x >= COLUMNS - 1)
|
||||||
return true;
|
return true;
|
||||||
|
@ -77,7 +90,7 @@ bool Board::isWalkable(Position point, float position_delta, Direction direction
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Cell cell = Cell(cellAtPosition(point, position_delta, direction));
|
Cell cell = Cell(cellAtPosition(point, position_delta, direction));
|
||||||
return pacman ? cell != Cell::wall : cell != Cell::wall && cell != Cell::pen_door;
|
return pacman ? cell != Cell::wall : cell != Cell::wall && cell != Cell::pen;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PositionInt> Board::initialPelletPositions() const {
|
std::vector<PositionInt> Board::initialPelletPositions() const {
|
||||||
|
|
|
@ -18,7 +18,7 @@ public:
|
||||||
nothing = 2,
|
nothing = 2,
|
||||||
door = 3,
|
door = 3,
|
||||||
power_pellet = 4,
|
power_pellet = 4,
|
||||||
pen_door = 5,
|
pen = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
Board();
|
Board();
|
||||||
|
@ -26,6 +26,10 @@ public:
|
||||||
[[nodiscard]] bool isWalkableForPacMan(Position point, float d, Direction direction) const;
|
[[nodiscard]] bool isWalkableForPacMan(Position point, float d, Direction direction) const;
|
||||||
[[nodiscard]] bool isWalkableForGhost(Position point, float d, Direction direction) const;
|
[[nodiscard]] bool isWalkableForGhost(Position point, float d, Direction direction) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isWalkableForGost(Position point, Position origin) const;
|
||||||
|
[[nodiscard]] bool isWalkable(Position point) const;
|
||||||
|
[[nodiscard]] bool isInPen(Position point) const;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<PositionInt> initialPelletPositions() const;
|
[[nodiscard]] std::vector<PositionInt> initialPelletPositions() const;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<PositionInt> initialSuperPelletPositions() const;
|
[[nodiscard]] std::vector<PositionInt> initialSuperPelletPositions() const;
|
||||||
|
@ -33,7 +37,9 @@ public:
|
||||||
static Position initialPacManPosition() { return { 13.5, 23 }; }
|
static Position initialPacManPosition() { return { 13.5, 23 }; }
|
||||||
|
|
||||||
static Position initialBlinkyPosition() { return { 13.5, 11 }; }
|
static Position initialBlinkyPosition() { return { 13.5, 11 }; }
|
||||||
static Position blinkyScatterTarget() { return { 25, -2 }; }
|
static Position penDoorPosition() { return { 13, 11 }; }
|
||||||
|
|
||||||
|
static Position blinkyScatterTarget() { return { 25, -3 }; }
|
||||||
|
|
||||||
static Position initialSpeedyPosition() { return { 11.5, 14 }; }
|
static Position initialSpeedyPosition() { return { 11.5, 14 }; }
|
||||||
static Position speedyScatterTarget() { return { 3, -2 }; }
|
static Position speedyScatterTarget() { return { 3, -2 }; }
|
||||||
|
|
|
@ -7,3 +7,15 @@ enum class Direction {
|
||||||
UP,
|
UP,
|
||||||
DOWN
|
DOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline Direction oppositeDirection(Direction d)
|
||||||
|
{
|
||||||
|
switch (d) {
|
||||||
|
case Direction::LEFT: return Direction::RIGHT;
|
||||||
|
case Direction::RIGHT: return Direction::LEFT;
|
||||||
|
case Direction::UP: return Direction::DOWN;
|
||||||
|
case Direction::DOWN: return Direction::UP;
|
||||||
|
case Direction::NONE: return d;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
|
@ -17,15 +17,86 @@ Position Ghost::position() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Ghost::positionInGrid() const {
|
Position Ghost::positionInGrid() const {
|
||||||
return { std::round(pos.x), std::round(pos.y) };
|
return { std::round(pos.x), std::round(pos.y) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::update(std::chrono::milliseconds time_delta, const Board & board) {
|
void Ghost::update(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
time += time_delta.count();
|
updateAnimation(time_delta);
|
||||||
if (time >= 250) {
|
updatePosition(time_delta, board);
|
||||||
time = 0;
|
}
|
||||||
alternate_animation = !alternate_animation;
|
|
||||||
}
|
void Ghost::updatePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
|
updateDirection(board);
|
||||||
|
|
||||||
|
float position_delta = (0.004 * time_delta.count()) * 0.75;
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::NONE:
|
||||||
|
break;
|
||||||
|
case Direction::LEFT:
|
||||||
|
pos.x -= position_delta;
|
||||||
|
pos.y = round(pos.y);
|
||||||
|
break;
|
||||||
|
case Direction::RIGHT:
|
||||||
|
pos.x += position_delta;
|
||||||
|
pos.y = round(pos.y);
|
||||||
|
break;
|
||||||
|
case Direction::UP:
|
||||||
|
pos.x = round(pos.x);
|
||||||
|
pos.y -= position_delta;
|
||||||
|
break;
|
||||||
|
case Direction::DOWN:
|
||||||
|
pos.x = round(pos.x);
|
||||||
|
pos.y += position_delta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ghost::updateDirection(const Board & board) {
|
||||||
|
auto cell = positionInGrid();
|
||||||
|
if(cell == lastIntersection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using P = std::tuple<Direction, Position, double>;
|
||||||
|
|
||||||
|
auto [x , y] = cell;
|
||||||
|
std::array<P, 4> positions = {{
|
||||||
|
P{Direction::UP, {x, y-1}, 0},
|
||||||
|
P{Direction::LEFT, {x-1, y}, 0},
|
||||||
|
P{Direction::DOWN, {x, y+1}, 0},
|
||||||
|
P{Direction::RIGHT, {x+1, y}, 0}
|
||||||
|
}};
|
||||||
|
const Position target = this->target(board);
|
||||||
|
|
||||||
|
std::for_each(positions.begin(), positions.end(), [&](P & p) {
|
||||||
|
get<2>(p) = (get<0>(p) != oppositeDirection(direction) && board.isWalkableForGost(get<1>(p), cell)) ?
|
||||||
|
std::hypot(get<1>(p).x - target.x, get<1>(p).y - target.y)
|
||||||
|
: std::numeric_limits<double>::infinity();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto it = std::min_element(positions.begin(), positions.end(), [](const auto & a, const auto &b)
|
||||||
|
{
|
||||||
|
return get<2>(a) < get<2>(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
lastIntersection = cell;
|
||||||
|
direction = std::get<0>(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position Ghost::target(const Board & board) const {
|
||||||
|
if(board.isInPen(positionInGrid()))
|
||||||
|
return board.penDoorPosition();
|
||||||
|
|
||||||
|
return scatterTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ghost::updateAnimation(std::chrono::milliseconds time_delta)
|
||||||
|
{
|
||||||
|
time += time_delta.count();
|
||||||
|
if (time >= 250) {
|
||||||
|
time = 0;
|
||||||
|
alternate_animation = !alternate_animation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Blinky::Blinky(const Board & board)
|
Blinky::Blinky(const Board & board)
|
||||||
|
|
|
@ -25,6 +25,12 @@ public:
|
||||||
|
|
||||||
void update(std::chrono::milliseconds time_delta, const Board & board);
|
void update(std::chrono::milliseconds time_delta, const Board & board);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateAnimation(std::chrono::milliseconds time_delta);
|
||||||
|
void updatePosition(std::chrono::milliseconds time_delta, const Board & board);
|
||||||
|
void updateDirection(const Board & board);
|
||||||
|
Position target(const Board & board) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Atlas::Ghost spritesSet;
|
Atlas::Ghost spritesSet;
|
||||||
Direction direction = Direction::NONE;
|
Direction direction = Direction::NONE;
|
||||||
|
@ -34,6 +40,7 @@ protected:
|
||||||
Position pos;
|
Position pos;
|
||||||
Position startingPosition;
|
Position startingPosition;
|
||||||
Position scatterTarget;
|
Position scatterTarget;
|
||||||
|
Position lastIntersection = {-1, -1};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Blinky : public Ghost {
|
class Blinky : public Ghost {
|
||||||
|
|
Loading…
Reference in a new issue