Skip to content

Commit

Permalink
feat(nibbler): handle snake movement
Browse files Browse the repository at this point in the history
  • Loading branch information
drawbu committed Apr 7, 2024
1 parent e9fd9a3 commit 1340464
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 80 deletions.
11 changes: 1 addition & 10 deletions Games/nibbler/Fruit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,14 @@ Fruit::Fruit(ass::IEngine &engine) : _sprite(engine.create_sprite())
},
.path = "assets/nibbler/fruit.png",
});
move(engine);
_sprite->move({9, 8});
}

void Fruit::move(pos_t pos)
{
_sprite->move(pos);
}

void Fruit::move(ass::IEngine &engine)
{
auto [width, height] = engine.get_renderer().get_window_size();
move({
static_cast<float>(rand() % width),
static_cast<float>(rand() % height),
});
}

pos_t Fruit::position()
{
return _sprite->position();
Expand Down
20 changes: 7 additions & 13 deletions Games/nibbler/Nibbler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ ass::RunStatus Nibbler::run(ass::IEngine &engine)
Map map{engine};

while (true) {
// Avoid that the fruit can disapear
const auto &fruit_pos = fruit.position();
const auto &[w, h] = engine.get_renderer().get_window_size();
if (fruit_pos.x >= w || fruit_pos.y >= h)
fruit.move(engine);

// Move the snake
for (auto &event : engine.events()) {
if (event.state != ass::EventState::KeyPressed)
Expand All @@ -53,35 +47,35 @@ ass::RunStatus Nibbler::run(ass::IEngine &engine)
engine.next_renderer();
break;
case ass::EventKey::KeyUp:
snake.set_direction(Direction::Up);
snake.set_direction(Direction::Up, map);
break;
case ass::EventKey::KeyDown:
snake.set_direction(Direction::Down);
snake.set_direction(Direction::Down, map);
break;
case ass::EventKey::KeyLeft:
snake.set_direction(Direction::Left);
snake.set_direction(Direction::Left, map);
break;
case ass::EventKey::KeyRight:
snake.set_direction(Direction::Right);
snake.set_direction(Direction::Right, map);
break;
case ass::EventKey::KeySpace:
for (size_t i = 0; i < 3; i++)
snake.move(engine, fruit);
snake.move(fruit, map);
break;
default:
break;
}
}
snake.move(engine, fruit);
snake.move(fruit, map);

// Check if the player is dead
if (snake.is_dead(engine))
return ass::RunStatus::Exit;

// Redraw screen
engine.clear(ass::TermColor::Black);
snake.draw(engine);
fruit.draw(engine);
snake.draw(engine);
map.draw(engine);
engine.refresh();
engine.wait_frame(10 + snake.get_size() / 4);
Expand Down
49 changes: 25 additions & 24 deletions Games/nibbler/Nibbler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,56 @@ class Fruit
~Fruit() = default;

void draw(ass::IEngine &engine);
void move(ass::IEngine &engine);
void move(pos_t pos);
pos_t position();

private:
std::unique_ptr<ass::ISprite> _sprite;
};

class Map
{
public:
Map(ass::IEngine &engine);
~Map();

enum class MapPart : char
{
Void,
Wall,
};

MapPart get_tile(pos_t pos) const;
void draw(ass::IEngine &engine);

private:
std::vector<std::vector<MapPart>> _map;
std::unique_ptr<ass::ISprite> _sprite;
};

class Player
{
public:
Player(ass::IEngine &engine);
~Player() = default;

void draw(ass::IEngine &engine);
void move(ass::IEngine &engine, Fruit &fruit);
void move(Fruit &fruit, Map &map);
bool is_dead(ass::IEngine &engine);
void grow();
void set_direction(Direction direction);
void set_direction(Direction direction, Map &map);

pos_t &get_head();
pos_t &get_tail();
size_t get_size() const;

private:
std::unique_ptr<ass::ISprite> _sprite;
std::queue<Direction> _directions;
Direction _current_direction;
Direction _last_direction;
std::vector<pos_t> _body;

bool is_safe(Direction dir, Map &map);
};

static const std::vector<std::string> MAP{
Expand All @@ -91,23 +112,3 @@ static const std::vector<std::string> MAP{
"O O",
"OOOOOOOOOOOOOOOOOOO",
};

class Map
{
public:
Map(ass::IEngine &engine);
~Map();

enum class MapPart : char
{
Void,
Wall,
};

MapPart get_tile(pos_t pos) const;
void draw(ass::IEngine &engine);

private:
std::vector<std::vector<MapPart>> _map;
std::unique_ptr<ass::ISprite> _sprite;
};
83 changes: 51 additions & 32 deletions Games/nibbler/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
#include <ASS/ISprite.hpp>
#include <ASS/Vector2.hpp>
#include <cmath>
#include <iostream>
#include <vector>

#include "Nibbler.hpp"

Player::Player(ass::IEngine &engine)
: _sprite(engine.create_sprite()), _current_direction(Direction::Right)
: _sprite(engine.create_sprite()),
_current_direction(Direction::Right),
_last_direction(_current_direction)
{
_sprite->set_asset({
.sprite =
Expand All @@ -30,10 +33,7 @@ Player::Player(ass::IEngine &engine)

const auto [w, h] = engine.get_renderer().get_window_size();
for (size_t i = 0; i < 4; i++)
_body.push_back({
std::round(static_cast<float>(w) / 2) - i,
std::round(static_cast<float>(h) / 2),
});
_body.push_back({static_cast<float>(4 - i), 1});
}

void Player::grow()
Expand All @@ -43,37 +43,58 @@ void Player::grow()
_body.push_back(_body.back());
}

void Player::move(ass::IEngine &engine, Fruit &fruit)
bool Player::is_safe(Direction dir, Map &map)
{
// Move the tail
for (size_t i = _body.size() - 1; i > 0; i--)
_body.at(i) = _body.at(i - 1);

if (!_directions.empty()) {
auto direction = _directions.front();
_directions.pop();

// Check that the snake doesn't do a 360 no scope
if (int(direction) / 2 != int(_current_direction) / 2)
_current_direction = direction;
}

// Move the head
auto &head = get_head();
switch (_current_direction) {
pos_t pos{get_head()};
switch (dir) {
case Direction::Up:
head.y -= 1;
pos.y -= 1;
break;
case Direction::Down:
head.y += 1;
pos.y += 1;
break;
case Direction::Right:
head.x += 1;
pos.x += 1;
break;
case Direction::Left:
head.x -= 1;
pos.x -= 1;
break;
}
return map.get_tile(pos) == Map::MapPart::Void;
}

void Player::move(Fruit &fruit, Map &map)
{

// Check that the snake doesn't do a 360 no scope
if (is_safe(_current_direction, map) && int(_last_direction) / 2 != int(_current_direction) / 2)
_last_direction = _current_direction;
else
_current_direction = _last_direction;

// Move the head
auto &head = get_head();
if (is_safe(_current_direction, map)) {
// Move the tail
for (size_t i = _body.size() - 1; i > 0; i--)
_body.at(i) = _body.at(i - 1);

// Move the head
switch (_current_direction) {
case Direction::Up:
head.y -= 1;
break;
case Direction::Down:
head.y += 1;
break;
case Direction::Right:
head.x += 1;
break;
case Direction::Left:
head.x -= 1;
break;
}
}

// The snake touches the fruit
auto fruit_pos = fruit.position();
Expand All @@ -91,15 +112,13 @@ bool Player::is_dead(ass::IEngine &engine)
for (size_t i = 2; i < _body.size(); i++)
if (head.x == _body.at(i).x && head.y == _body.at(i).y)
return true;

// Out of bounds
auto [width, height] = engine.get_renderer().get_window_size();
return head.x < 0 || head.x >= width || head.y < 0 || head.y >= height;
return false;
}

void Player::set_direction(Direction direction)
void Player::set_direction(Direction direction, Map &map)
{
_directions.push(direction);
if (int(direction) / 2 != int(_current_direction) / 2)
_current_direction = direction;
}

pos_t &Player::get_head()
Expand Down
2 changes: 1 addition & 1 deletion Games/snake/Snake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ ass::RunStatus Snake::run(ass::IEngine &engine)

// Redraw screen
engine.clear(ass::TermColor::Black);
snake.draw(engine);
fruit.draw(engine);
snake.draw(engine);
engine.refresh();
engine.wait_frame(10 + snake.get_size() / 4);
}
Expand Down

0 comments on commit 1340464

Please sign in to comment.