summaryrefslogtreecommitdiff
path: root/Games/Snake
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-04-20 03:24:50 +0200
committerAndreas Kling <awesomekling@gmail.com>2019-04-20 03:24:50 +0200
commite24e4867142d1ae2a495060fa07629f941e8e4ee (patch)
tree789d7ef6bd703a65efcd61a912ad2fca5704df34 /Games/Snake
parenteca9494adfcdd0acb6a27172053195b0c4c8ec09 (diff)
downloadserenity-e24e4867142d1ae2a495060fa07629f941e8e4ee.zip
Snake: Flesh out a basic snake game :^)
Diffstat (limited to 'Games/Snake')
-rw-r--r--Games/Snake/SnakeGame.cpp151
-rw-r--r--Games/Snake/SnakeGame.h37
-rw-r--r--Games/Snake/main.cpp9
3 files changed, 190 insertions, 7 deletions
diff --git a/Games/Snake/SnakeGame.cpp b/Games/Snake/SnakeGame.cpp
index e69de29bb2..a0ed4175ad 100644
--- a/Games/Snake/SnakeGame.cpp
+++ b/Games/Snake/SnakeGame.cpp
@@ -0,0 +1,151 @@
+#include "SnakeGame.h"
+#include <LibGUI/GPainter.h>
+#include <stdlib.h>
+#include <time.h>
+
+SnakeGame::SnakeGame(GWidget* parent)
+ : GWidget(parent)
+{
+ srand(time(nullptr));
+ reset();
+}
+
+SnakeGame::~SnakeGame()
+{
+}
+
+void SnakeGame::reset()
+{
+ m_head = { m_rows / 2, m_columns / 2 };
+ m_tail.clear_with_capacity();
+ m_length = 2;
+ stop_timer();
+ start_timer(120);
+ spawn_fruit();
+}
+
+bool SnakeGame::is_available(const Coordinate& coord)
+{
+ for (int i = 0; i < m_tail.size(); ++i) {
+ if (m_tail[i] == coord)
+ return false;
+ }
+ if (m_head == coord)
+ return false;
+ if (m_fruit == coord)
+ return false;
+ return true;
+}
+
+void SnakeGame::spawn_fruit()
+{
+ Coordinate coord;
+ for (;;) {
+ coord.row = rand() % m_rows;
+ coord.column = rand() % m_columns;
+ if (is_available(coord))
+ break;
+ }
+ m_fruit = coord;
+}
+
+void SnakeGame::timer_event(CTimerEvent&)
+{
+ m_tail.prepend(m_head);
+
+ if (m_tail.size() > m_length)
+ m_tail.take_last();
+
+ m_head.row += m_vertical_velocity;
+ m_head.column += m_horizontal_velocity;
+
+ m_last_vertical_velocity = m_vertical_velocity;
+ m_last_horizontal_velocity = m_horizontal_velocity;
+
+ if (m_head.row >= m_rows)
+ m_head.row = 0;
+ if (m_head.row < 0)
+ m_head.row = m_rows - 1;
+ if (m_head.column >= m_columns)
+ m_head.column = 0;
+ if (m_head.column < 0)
+ m_head.column = m_columns - 1;
+
+ for (int i = 0; i < m_tail.size(); ++i) {
+ if (m_head == m_tail[i]) {
+ game_over();
+ return;
+ }
+ }
+
+ if (m_head == m_fruit) {
+ ++m_length;
+ spawn_fruit();
+ }
+ update();
+}
+
+void SnakeGame::keydown_event(GKeyEvent& event)
+{
+ switch (event.key()) {
+ case KeyCode::Key_A:
+ case KeyCode::Key_Left:
+ if (m_last_horizontal_velocity == 1)
+ break;
+ m_vertical_velocity = 0;
+ m_horizontal_velocity = -1;
+ break;
+ case KeyCode::Key_D:
+ case KeyCode::Key_Right:
+ if (m_last_horizontal_velocity == -1)
+ break;
+ m_vertical_velocity = 0;
+ m_horizontal_velocity = 1;
+ break;
+ case KeyCode::Key_W:
+ case KeyCode::Key_Up:
+ if (m_last_vertical_velocity == 1)
+ break;
+ m_vertical_velocity = -1;
+ m_horizontal_velocity = 0;
+ break;
+ case KeyCode::Key_S:
+ case KeyCode::Key_Down:
+ if (m_last_vertical_velocity == -1)
+ break;
+ m_vertical_velocity = 1;
+ m_horizontal_velocity = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+void SnakeGame::paint_event(GPaintEvent& event)
+{
+ GPainter painter(*this);
+ painter.fill_rect(event.rect(), Color::Black);
+
+ auto game_rect = rect();
+ auto cell_size = Size(game_rect.width() / m_columns, game_rect.height() / m_rows);
+
+ auto cell_rect = [&] (const Coordinate& coord) -> Rect {
+ return {
+ coord.column * cell_size.width(),
+ coord.row * cell_size.height(),
+ cell_size.width(),
+ cell_size.height()
+ };
+ };
+
+ painter.fill_rect(cell_rect(m_head), Color::Yellow);
+ for (auto& coord : m_tail)
+ painter.fill_rect(cell_rect(coord), Color::from_rgb(0xaaaa00));
+
+ painter.fill_rect(cell_rect(m_fruit), Color::Red);
+}
+
+void SnakeGame::game_over()
+{
+ reset();
+}
diff --git a/Games/Snake/SnakeGame.h b/Games/Snake/SnakeGame.h
index 01e13aabbf..2e8d04c00d 100644
--- a/Games/Snake/SnakeGame.h
+++ b/Games/Snake/SnakeGame.h
@@ -4,8 +4,43 @@
class SnakeGame : public GWidget {
public:
- explicit SnakeGame(GWidget* parent);
+ explicit SnakeGame(GWidget* parent = nullptr);
+ virtual ~SnakeGame() override;
+
+ void reset();
private:
virtual void paint_event(GPaintEvent&) override;
+ virtual void keydown_event(GKeyEvent&) override;
+ virtual void timer_event(CTimerEvent&) override;
+
+ struct Coordinate {
+ int row { 0 };
+ int column { 0 };
+
+ bool operator==(const Coordinate& other) const
+ {
+ return row == other.row && column == other.column;
+ }
+ };
+
+ void game_over();
+ void spawn_fruit();
+ bool is_available(const Coordinate&);
+
+ int m_rows { 20 };
+ int m_columns { 20 };
+
+ int m_horizontal_velocity { 1 };
+ int m_vertical_velocity { 0 };
+
+ int m_last_horizontal_velocity { 1 };
+ int m_last_vertical_velocity { 0 };
+
+ Coordinate m_head;
+ Vector<Coordinate> m_tail;
+
+ Coordinate m_fruit;
+
+ int m_length { 0 };
};
diff --git a/Games/Snake/main.cpp b/Games/Snake/main.cpp
index 6443761bfa..7cf72b64ff 100644
--- a/Games/Snake/main.cpp
+++ b/Games/Snake/main.cpp
@@ -13,13 +13,10 @@ int main(int argc, char** argv)
auto* window = new GWindow;
window->set_title("Snake");
- window->set_rect(100, 100, 139, 175);
+ window->set_rect(100, 100, 300, 300);
- auto* widget = new GWidget;
- window->set_main_widget(widget);
- widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
-
- auto* game = new SnakeGame(widget);
+ auto* game = new SnakeGame;
+ window->set_main_widget(game);
auto menubar = make<GMenuBar>();