43 Commits
Author SHA1 Message Date
DarthMooMancer dfc381bd9b CHORE: Removed redundant for loop 2026-10-12 11:23:30 -04:00
Gentoo 7ef5407684 f 2026-03-03 23:06:32 -05:00
Andrew Haynes d7faaaeaeb f 2026-03-03 15:53:21 -05:00
Andrew Haynes 3d6a6142bd converting to c 2026-03-03 13:06:45 -05:00
DarthMooMancer 820c4b6dc4 hi 2025-11-24 14:23:37 -05:00
DarthMooMancer 4a2c46af9e testing 2025-11-18 01:28:01 -05:00
DarthMooMancer 700a48798e testing 2025-11-18 01:21:53 -05:00
DarthMooMancer ff22fff701 testing 2025-11-18 01:14:06 -05:00
DarthMooMancer 8d4b3990d1 wip 2025-11-14 10:20:00 -05:00
DarthMooMancer 59c68b7bd3 wip 2025-11-13 23:15:52 -05:00
DarthMooMancer 9eb93dc691 header changes 2025-11-10 09:25:16 -05:00
DarthMooMancer 7a79d4ac1a cleaning up 2025-11-07 10:51:43 -05:00
DarthMooMancer 4f17b0c1e2 README 2025-11-07 00:55:27 -05:00
DarthMooMancer 435f7d1a49 wip 2025-11-05 09:32:05 -05:00
DarthMooMancer c5bb6e34b3 wip 2025-11-05 03:41:28 -05:00
DarthMooMancer 1160f3d147 window is no longer a raw pointer and is a unique_ptr, id no longer exists 2025-11-05 02:18:28 -05:00
DarthMooMancer e437b7b35f CMAKE changes 2025-11-04 23:59:42 -05:00
DarthMooMancer e87094ce13 removed the input thread and integrated it to the SnakeGame engine itself 2025-11-04 23:31:23 -05:00
DarthMooMancer 183be3c24a changed from raw pointer arrays to vectors 2025-11-04 20:11:57 -05:00
DarthMooMancer 1af33ba5a4 shorthand names without being to short for clarity 2025-11-03 14:02:27 -05:00
DarthMooMancer 2c38da4b79 changes from m_row & m_col to x & y 2025-11-03 13:09:37 -05:00
DarthMooMancer ab16f18137 removing magic numbers 2025-11-02 21:46:24 -05:00
DarthMooMancer 02ff154b81 optimizations 2025-11-01 18:06:21 -04:00
DarthMooMancer f9d0e9fb01 cleaning up comments 2025-10-09 12:49:25 -04:00
DarthMooMancer 6c6605fcb5 using memmove to update snake 2025-10-09 11:51:21 -04:00
DarthMooMancer baf2e21358 renaming 2025-10-08 09:18:26 -04:00
Andrew Haynes 74cb3971cc Update README.md 2025-09-27 23:14:02 +00:00
DarthMooMancer 959e539d4b wip 2025-09-27 19:10:16 -04:00
Andrew Haynes 99bcdbaf46 Delete .cache directory 2025-09-27 02:27:45 +00:00
DarthMooMancer 94006faff6 final cleanup for now as it uses a more efficient movement tech, has cleaner names and code (no magic numbers), use ptrs instead of a bunch of vectors and copies 2025-09-26 22:25:56 -04:00
DarthMooMancer 74a5d9738a snake can collide and removed some magic numbers 2025-09-26 22:14:34 -04:00
DarthMooMancer 7a6df4fbe5 game is in a working state again, now just cleaning up 2025-09-26 21:11:30 -04:00
DarthMooMancer 1f3954ac79 wip 2025-09-26 17:53:29 -04:00
DarthMooMancer abef01d7b8 wip 2025-09-25 21:11:34 -04:00
DarthMooMancer 119c1422c0 wip 2025-09-24 14:48:38 -04:00
DarthMooMancer 0d47de8a7a starting an upgrade to move everything to just use ptrs and simplify the code a little 2025-09-24 14:25:03 -04:00
DarthMooMancer 626d7261d4 refactor 2025-09-12 22:46:59 -04:00
DarthMooMancer 9d3f6b3308 refactor: removing a bunch of random bad code and fixing it with more readable code 2025-09-12 22:18:36 -04:00
DarthMooMancer e36ab27f2b fruit: fixed bug where fruit could generate under tail 2025-09-12 20:55:56 -04:00
DarthMooMancer 4bab69a3a8 refactor: removing some deprecated functions and removed array for c style array 2025-09-12 20:31:42 -04:00
DarthMooMancer cfa0e52705 final commit for the time being 2025-08-30 13:26:31 -04:00
DarthMooMancer b0393c65f2 refactor: renaming functions and removing old functions 2025-08-30 01:23:38 -04:00
DarthMooMancer 96e5eb9930 refactor: renamed snake to element and moved fruit to element. Removed a lot of unused functions and variables 2025-08-30 01:09:50 -04:00
32 changed files with 287 additions and 379 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
---
BasedOnStyle: Google
Standard: Latest
UseTab: Always
TabWidth: 8
IndentWidth: 8
BreakBeforeBraces: Allman
SpaceBeforeCpp11BracedList: true
SeparateDefinitionBlocks: Always
Cpp11BracedListStyle: false
BreakAfterReturnType: TopLevel
AlignTrailingComments:
Kind: Always
ReflowComments: Always
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
IndentAccessModifiers: false # Indent access specifiers by one level
AccessModifierOffset: -4 # Optional: adjust offset relative to the class indentation level
-2
View File
@@ -1,2 +0,0 @@
CompileFlags:
Add: [-Iinclude]
+5 -15
View File
@@ -1,23 +1,13 @@
cmake_minimum_required(VERSION 3.16)
project(snake)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
include_directories(include)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(snake LANGUAGES C)
set(SOURCES
src/main.cpp
src/snake.cpp
src/input.cpp
src/window.cpp
src/point.cpp
src/fruit.cpp
src/main.c
src/engine.c
)
add_executable(snake ${SOURCES})
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})
target_link_libraries(snake ${CURSES_LIBRARIES})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
target_include_directories(snake PRIVATE include)
-1
View File
@@ -1 +0,0 @@
# snake
-1
View File
@@ -1 +0,0 @@
build/compile_commands.json
+55
View File
@@ -0,0 +1,55 @@
#ifndef ENGINE_HPP
#define ENGINE_HPP
#include <stddef.h>
#define ROW 15
#define COL 21
typedef enum
{
UP,
DOWN,
LEFT,
RIGHT
} Direction;
typedef struct
{
int x, y;
} vec2;
int
equals(vec2* a, vec2* b);
void
set(vec2* vec2, int _x, int _y);
typedef struct
{
char view[ROW][COL];
int m_score, tail_size;
vec2 head, fruit, prev_head;
vec2 tail[(ROW * COL) - 1];
Direction dir;
} Engine;
void
engine_init(Engine* engine);
void
engine_display(Engine* engine);
int
process_input(Direction* dir);
int
is_open(char* status, size_t len, Engine* engine);
void
eat(Engine* engine);
void
move(Engine *engine);
#endif
-12
View File
@@ -1,12 +0,0 @@
#ifndef FRUIT_HPP
#define FRUIT_HPP
#include "point.hpp"
struct Fruit {
Fruit();
Point m_point;
void reset(std::array<std::array<char, COL>, ROW> &board, int row, int col, int &score);
};
#endif // FRUIT_HPP
-10
View File
@@ -1,10 +0,0 @@
#ifndef GLOBALS_HPP
#define GLOBALS_HPP
#include <cstdint>
constexpr uint16_t ROW = 11;
constexpr uint16_t COL = 11;
constexpr uint16_t FPS = 5;
#endif // GLOBALS_HPP
-10
View File
@@ -1,10 +0,0 @@
#ifndef INPUT_HPP
#define INPUT_HPP
struct Input {
int m_current_key = 0;
char m_getch = ' ';
void get_input(int &d, bool &c, bool &r);
};
#endif // INPUT_HPP
-21
View File
@@ -1,21 +0,0 @@
#ifndef POINT_HPP
#define POINT_HPP
#include <array>
#include <string>
#include "globals.hpp"
struct Point {
int m_row;
int m_col;
int m_direction;
Point() {}
Point(int x, int y) : m_row(x), m_col(y) {}
Point(int x, int y, int dir) : m_row(x), m_col(y), m_direction(dir) {}
std::string to_string() const;
void clear(std::array<std::array<char, COL>, ROW> &arr) const;
void set_new_point(int x, int y, int dir);
};
#endif // POINT_HPP
-33
View File
@@ -1,33 +0,0 @@
#ifndef SNAKE_HPP
#define SNAKE_HPP
#include <vector>
#include "point.hpp"
struct Head {
Head();
Head(int default_direction, int row, int col);
Point m_head;
Point m_previous_head;
void get_new_point(int direction);
void get_previous_point();
};
struct Tail {
Tail(int follow_direction, int row, int col);
Point m_tail_pos;
Point m_previous_tail_pos;
std::vector<Point> m_move_history;
void get_previous_point();
};
struct Body {
std::vector<Tail> m_body;
void clear(std::array<std::array<char, COL>, ROW> &arr);
void draw(std::array<std::array<char, COL>, ROW> &arr);
void move(Head &head);
void get_new_points(Head &head);
void collision(Head &head, bool &running);
};
#endif
-23
View File
@@ -1,23 +0,0 @@
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include <array>
#include "globals.hpp"
#include "fruit.hpp"
#include "snake.hpp"
struct Window {
Window();
std::array<std::array<char, COL>, ROW> m_board;
int m_score;
int m_reset;
int m_previous_reset;
void draw_window();
void tick(double frames_per_second);
void kill(bool &running);
void update_score(Fruit &fruit, Body &body);
void update_display(int fps);
void check_exit(bool &running, Body &body, bool &collision);
};
#endif // WINDOW_HPP
+169
View File
@@ -0,0 +1,169 @@
#include "engine.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
void
set(vec2* vec2, int _x, int _y)
{
vec2->x = _x;
vec2->y = _y;
}
int
equals(vec2* a, vec2* b)
{
return (a->x == b->x && a->y == b->y);
}
void
engine_init(Engine* engine)
{
srand(time(0));
set(&engine->head, COL / 2, ROW / 2);
set(&engine->prev_head, engine->head.x, engine->head.y);
set(&engine->fruit, rand() % COL, rand() % ROW);
engine->dir = RIGHT;
engine->m_score = 0;
engine->tail_size = 0;
memset(engine->view, '.', sizeof(engine->view));
}
void
engine_display(Engine* engine)
{
engine->view[engine->fruit.y][engine->fruit.x] = 'o';
if (engine->tail_size != 0)
{
for (char i = 0; i < engine->tail_size; i++)
{
if (engine->tail[i].x < COL && engine->tail[i].y < ROW)
{
engine->view[engine->tail[i].y]
[engine->tail[i].x] = '~';
}
}
}
write(1, "\033[H\033[J", sizeof("\033[H\033[J") - 1);
char str[(COL * 2 + 2) * ROW] = { 0 };
char* ptr = str;
for (char i = 0; i < ROW; i++)
{
for (char j = 0; j < COL; j++)
{
memcpy(ptr, " ", 1);
ptr++;
memcpy(ptr, &engine->view[i][j], 1);
ptr++;
}
memcpy(ptr, "\r\n", 2);
ptr += 2;
}
write(1, str, ptr - str);
}
int
process_input(Direction* dir)
{
unsigned char ch;
if (read(1, &ch, 1) > 0)
{
if (ch == 'q') return 0;
if (ch == 'w')
if (*dir != DOWN) *dir = UP;
if (ch == 's')
if (*dir != UP) *dir = DOWN;
if (ch == 'd')
if (*dir != LEFT) *dir = RIGHT;
if (ch == 'a')
if (*dir != RIGHT) *dir = LEFT;
}
return 1;
}
int
is_open(char* status, size_t len, Engine* engine)
{
if (engine->tail_size >= (ROW * COL) - 1)
{
snprintf(status, len,
"You filled the whole board and won! "
"Score: %d",
engine->m_score);
return 0;
}
for (int i = 0; i < engine->tail_size; i++)
{
if (equals(&engine->tail[i], &engine->head))
{
snprintf(status, len,
"You collided with yourself and died! "
"Score: %d",
engine->m_score);
return 0;
}
}
return 1;
}
void
move(Engine* engine)
{
set(&engine->prev_head, engine->head.x, engine->head.y);
if (engine->dir == LEFT)
engine->head.x = (engine->head.x + COL - 1) % COL;
else if (engine->dir == RIGHT)
engine->head.x = (engine->head.x + 1) % COL;
else if (engine->dir == UP)
engine->head.y = (engine->head.y + ROW - 1) % ROW;
else if (engine->dir == DOWN)
engine->head.y = (engine->head.y + 1) % ROW;
engine->view[engine->prev_head.y][engine->prev_head.x] = '.';
engine->view[engine->head.y][engine->head.x] = '*';
if (engine->tail_size == 0) return;
vec2 old_tail = engine->tail[engine->tail_size - 1];
memmove(&engine->tail[1], &engine->tail[0],
sizeof(vec2) * (engine->tail_size - 1));
engine->view[old_tail.y][old_tail.x] = '.';
set(&engine->tail[0], engine->prev_head.x, engine->prev_head.y);
}
void
eat(Engine* engine)
{
vec2 new_pos;
set(&new_pos, 0, 0);
int collision = 0;
do
{
collision = 0;
set(&new_pos, (rand() % COL), (rand() % ROW));
if (equals(&new_pos, &engine->head))
{
collision = 1;
}
if (engine->tail_size > 0)
{
for (int i = 0; i < engine->tail_size; i++)
{
if (equals(&new_pos, &engine->tail[i]))
{
collision = 1;
}
}
}
} while (collision);
engine->fruit = new_pos;
set(&engine->tail[engine->tail_size], engine->prev_head.x,
engine->prev_head.y);
engine->tail_size++;
engine->m_score++;
}
-19
View File
@@ -1,19 +0,0 @@
#include <cstdlib>
#include <ctime>
#include "fruit.hpp"
#include "globals.hpp"
Fruit::Fruit() {
std::srand(std::time(0));
m_point.m_row = (rand() % 9);
m_point.m_col = (rand() % 9);
}
void Fruit::reset(std::array<std::array<char, COL>, ROW> &board, int row, int col, int &score) {
if(m_point.m_row == row && m_point.m_col == col) {
m_point.clear(board);
m_point.m_row = (rand() % 9);
m_point.m_col = (rand() % 9);
score++;
}
}
-27
View File
@@ -1,27 +0,0 @@
#include "input.hpp"
#include <ncurses.h>
void Input::get_input(int &d, bool &c, bool &r) {
bool running = true;
while(running && !(c)) {
initscr();
cbreak();
noecho();
m_getch = getch();
endwin();
if(m_current_key != 1 && (m_getch == 'a' || m_getch == 'D')) {
d = m_current_key = -1;
} else if(m_current_key != - 1 && (m_getch == 'd' || m_getch == 'C')) {
d = m_current_key = 1;
m_current_key = 1;
} else if(m_current_key != -2 && (m_getch == 's' || m_getch == 'B')) {
d = m_current_key = 2;
} else if(m_current_key != 2 && (m_getch == 'w' || m_getch == 'A')) {
d = m_current_key = -2;
} else if(m_getch == 'q') {
r = running = false;
}
}
}
+39
View File
@@ -0,0 +1,39 @@
#include <fcntl.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include "engine.h"
int
main()
{
Engine engine;
engine_init(&engine);
char status[64] = { 0 };
static struct termios oldt, newt;
struct timespec req;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
req.tv_nsec = 1.5e8;
req.tv_sec = 0;
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
while (is_open(status, 64, &engine) && process_input(&engine.dir))
{
if (equals(&engine.fruit, &engine.head))
{
eat(&engine);
}
move(&engine);
engine_display(&engine);
nanosleep(&req, NULL);
}
write(1, status, sizeof(status));
write(1, "\033[?25h", 6);
tcsetattr(0, TCSANOW, &oldt);
return 0;
}
-42
View File
@@ -1,42 +0,0 @@
#include <thread>
#include "point.hpp"
#include "fruit.hpp"
#include "snake.hpp"
#include "window.hpp"
#include "globals.hpp"
#include "input.hpp"
int main () {
Window window;
Input input;
Fruit fruit;
Body body;
Head head;
bool running = true;
bool collision = false;
std::thread input_thread(
&Input::get_input, &input,
std::ref(head.m_head.m_direction),
std::ref(collision),
std::ref(running));
while (running) {
body.collision(head, collision);
window.update_score(fruit, body);
window.check_exit(running, body, collision);
head.get_new_point(head.m_head.m_direction);
body.get_new_points(head);
head.get_previous_point();
fruit.reset(window.m_board, head.m_head.m_row, head.m_head.m_col, window.m_score);
window.m_board[head.m_head.m_row][head.m_head.m_col] = '*';
window.m_board[fruit.m_point.m_row][fruit.m_point.m_col] = 'X';
body.draw(window.m_board);
window.update_display(FPS);
body.clear(window.m_board);
head.m_head.clear(window.m_board);
}
input_thread.join();
return 0;
}
-16
View File
@@ -1,16 +0,0 @@
#include <string>
#include "point.hpp"
std::string Point::to_string() const {
return "(" + std::to_string(m_row) + ", " + std::to_string(m_col) + "), " + std::to_string(m_direction);
}
void Point::clear(std::array<std::array<char, COL>, ROW> &arr) const {
arr[m_row][m_col] = '.';
}
void Point::set_new_point(int row, int col, int dir) {
m_row = row;
m_col = col;
m_direction = dir;
}
-96
View File
@@ -1,96 +0,0 @@
#include "snake.hpp"
Head::Head() {
m_head = Point { 5, 5, 2 };
m_previous_head.set_new_point(m_previous_head.m_row = 0, m_previous_head.m_col = 0, m_previous_head.m_direction = 2);
}
Head::Head(int default_direction, int row, int col) {
m_head = Point { row, col, default_direction };
m_previous_head.set_new_point(m_previous_head.m_row = 0, m_previous_head.m_col = 0, m_previous_head.m_direction = 2);
}
void Head::get_new_point(int direction) {
if (direction == -1) {
if (m_head.m_col <= 0) m_head.m_col = COL - 1;
else m_head.m_col--;
} else if (direction == 1) {
if (m_head.m_col >= COL - 1) m_head.m_col = 0;
else m_head.m_col++;
} else if (direction == -2) {
if (m_head.m_row <= 0) m_head.m_row = ROW - 1;
else m_head.m_row--;
} else if (direction == 2) {
if (m_head.m_row >= ROW - 1) m_head.m_row = 0;
else m_head.m_row++;
} else {
return;
}
}
void Head::get_previous_point() {
m_previous_head.m_row = m_head.m_row;
m_previous_head.m_col = m_head.m_col;
m_previous_head.m_direction = m_head.m_direction;
}
Tail::Tail(int follow_direction, int row, int col) {
m_tail_pos = Point { row, col, follow_direction };
}
void Tail::get_previous_point() {
m_tail_pos.m_row = m_previous_tail_pos.m_row;
m_tail_pos.m_col = m_previous_tail_pos.m_col;
m_tail_pos.m_direction = m_previous_tail_pos.m_direction;
}
void Body::collision(Head &head, bool &collision) {
for(int i = 0; i < m_body.size(); i++) {
if(m_body.at(i).m_tail_pos.m_row == head.m_head.m_row) {
if(m_body.at(i).m_tail_pos.m_col == head.m_head.m_col) {
collision = true;
}
}
}
}
void Body::clear(std::array<std::array<char, COL>, ROW> &arr) {
for(int i = 0; i < m_body.size(); i++) {
m_body.at(i).m_tail_pos.clear(arr);
}
}
void Body::draw(std::array<std::array<char, COL>, ROW> &arr) {
for(int i = 0; i < m_body.size(); i++) {
if(i < m_body.size()) {
arr[m_body.at(i).m_tail_pos.m_row][m_body.at(i).m_tail_pos.m_col] = '0';
}
}
}
void Body::move(Head &head) {
for (int i = 0; i < m_body.size(); i++) {
if(i == 0) {
}
}
}
void Body::get_new_points(Head &head) {
for (int i = 0; i < m_body.size(); i++) {
if(i == 0) {
m_body.at(i).m_previous_tail_pos.set_new_point(
head.m_previous_head.m_row,
head.m_previous_head.m_col,
head.m_previous_head.m_direction);
} else {
m_body.at(i).m_previous_tail_pos.set_new_point(
m_body.at(i - 1).m_tail_pos.m_row,
m_body.at(i - 1).m_tail_pos.m_col,
m_body.at(i - 1).m_tail_pos.m_direction);
}
}
for (int i = 0; i < m_body.size(); i++) {
m_body.at(i).get_previous_point();
}
}
-51
View File
@@ -1,51 +0,0 @@
#include "window.hpp"
#include <iostream>
#include <chrono>
#include <string>
#include <thread>
Window::Window() {
for(std::array<char, COL> &row : m_board) {
row.fill('.');
}
m_score = 0;
m_reset = 0;
m_previous_reset = 0;
}
void Window::check_exit(bool &running, Body &body, bool &collision) {
if(body.m_body.size() >= (ROW * COL)) {
std::cout << "You win! Score: " + std::to_string(body.m_body.size()) << std::endl;
kill(running);
} else if(collision) {
std::cout << "You died! Score: " + std::to_string(body.m_body.size()) << std::endl;
kill(running);
}
}
void Window::draw_window() {
for(std::array<char, COL> &row : m_board) {
for(char &col : row) {
std::cout << col << " ";
}
std::cout << "\r\n";
}
std::cout << std::flush;
}
void Window::update_score(Fruit &fruit, Body &body) {
if(m_score >= 1) {
m_score = 0;
body.m_body.push_back(Tail{2, 0, 0});
}
}
void Window::kill(bool &running) {
running = false;
}
void Window::update_display(int fps) {
std::cout<<"\033[H" << std::flush;
draw_window();
std::this_thread::sleep_for(std::chrono::milliseconds((int) (200))); // 1000 / fps; 200ms = 5fps
}