converting to c

This commit is contained in:
Andrew Haynes
2026-03-03 13:06:45 -05:00
parent 820c4b6dc4
commit 3d6a6142bd
9 changed files with 308 additions and 197 deletions
+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
+5 -7
View File
@@ -1,15 +1,13 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(snake)
project(snake LANGUAGES C)
set(SOURCES
src/main.cpp
src/engine.cpp
src/main.c
src/engine.c
)
add_executable(snake ${SOURCES})
target_include_directories(snake PRIVATE include)
find_package(Curses REQUIRED)
target_link_libraries(snake PRIVATE ${CURSES_LIBRARIES})
-32
View File
@@ -1,32 +0,0 @@
# Snake Game
A simple terminal-based snake game written in C++.
## Build
To build the game, run the following commands:
```bash
cmake -S . -B build && cmake --build build
```
## How to Run
After building the project, you can run the game with the following command:
```bash
./build/snake
```
## How to Play
- Use the `w`, `a`, `s`, and `d` keys to control the snake.
- `w`: move up
- `a`: move left
- `s`: move down
- `d`: move right
- The game ends if you collide with yourself.
- Press `q` to quit the game at any time.
## About
- This is a simple implementation of the classic Snake game.
- It is written in C++23.
- It uses the `ncurses` library to render the game in the terminal.
+62
View File
@@ -0,0 +1,62 @@
#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
{
unsigned char view[ROW][COL];
vec2 head;
vec2 fruit;
vec2 prev_head;
int m_score;
Direction dir;
vec2* tail[(ROW * COL) - 1];
int tail_size;
} 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
determine_index_zero_pos(Engine* engine);
void
add_tail_and_respawn_fruit(Engine* engine);
void
move_tail_indicies(vec2** tail, int tail_size, vec2* prev_head,
unsigned char (*view)[COL]);
#endif
-48
View File
@@ -1,48 +0,0 @@
#pragma once
#include <string>
#include <deque>
#include <array>
#include <ncurses.h>
constexpr int ROW = 15;
constexpr int COL = 21;
enum class Direction {
UP, DOWN, LEFT, RIGHT
};
struct vec2 {
int x, y;
vec2(int x, int y) : x(x), y(y) {}
void set(int _x, int _y) { x = _x; y = _y; }
auto operator <=> (const vec2&) const = default;
};
struct Window {
std::array<std::array<char, COL>, ROW> view;
void draw_display();
void update_display(const vec2 fruit, std::deque<vec2> &buf);
};
class Engine {
public:
Engine();
~Engine() { endwin(); }
void run();
private:
Window win;
std::deque<vec2> tail;
std::string status;
vec2 head { COL / 2, ROW / 2 };
vec2 fruit { rand() % COL, rand() % ROW };
vec2 prev_head { head.x, head.y };
Direction dir = Direction::RIGHT;
unsigned int m_score = 0;
bool process_input();
void determine_index_zero_pos();
void move_tail_indicies();
void add_tail_and_respawn_fruit();
bool exit_summary();
};
+180
View File
@@ -0,0 +1,180 @@
#include "engine.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.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, 0, sizeof(engine->view));
for (unsigned char i = 0; i < ROW; i++)
{
for (unsigned char j = 0; j < COL; j++)
{
engine->view[i][j] = '.';
}
}
}
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);
for (unsigned char i = 0; i < ROW; i++)
{
for (unsigned char j = 0; j < COL; j++)
{
write(1, &engine->view[i][j], 1);
write(1, " ", 1);
}
write(1, "\r\n", 2);
}
}
int
process_input(Direction* dir)
{
unsigned char ch;
if (read(1, &ch, 1) > 0)
{
if (ch == 'q') return false;
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 ((sizeof(engine->tail) / sizeof(engine->tail[0])) >= ROW * COL)
{
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
determine_index_zero_pos(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] = '*';
}
void
move_tail_indicies(vec2** tail, int tail_size, vec2* prev_head,
unsigned char (*view)[COL])
{
if (tail_size == 0) return;
view[tail[tail_size - 1]->y][tail[tail_size - 1]->x] = '.';
for (int i = tail_size - 1; i > 0; i--)
{
set(tail[i], tail[i - 1]->x, tail[i - 1]->y);
}
set(tail[0], prev_head->x, prev_head->y);
}
void
add_tail_and_respawn_fruit(Engine* engine)
{
if (!equals(&engine->fruit, &engine->head)) return;
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;
break;
}
if (engine->tail_size > 0)
{
for (int i = 0; i < engine->tail_size; i++)
{
if (equals(&new_pos, engine->tail[i]))
{
collision = 1;
break;
}
}
}
} while (collision);
engine->fruit = new_pos;
engine->tail[engine->tail_size] = (vec2*)malloc(sizeof(vec2));
engine->tail_size++;
engine->m_score++;
}
-103
View File
@@ -1,103 +0,0 @@
#include <engine.hpp>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <format>
#include <ranges>
#include <thread>
#include <chrono>
void Window::draw_display() {
for(int i : std::ranges::views::iota(0, ROW)) {
for(int j : std::ranges::views::iota(0, COL)) {
mvaddch(i, j * 2, view[i][j]);
}
}
}
void Window::update_display(const vec2 fruit, std::deque<vec2> &buf) {
view[fruit.y][fruit.x] = 'o';
for(const auto& segment : buf) {
if(segment.x < COL && segment.y < ROW) {
view[segment.y][segment.x] = '~';
}
}
}
Engine::Engine() {
std::srand(std::time(0));
initscr();
cbreak();
noecho();
curs_set(0);
nodelay(stdscr, true);
for(auto& row : win.view) row.fill('.');
}
void Engine::run() {
while (exit_summary() && process_input()) {
add_tail_and_respawn_fruit();
determine_index_zero_pos();
move_tail_indicies();
win.update_display(fruit, tail);
win.draw_display();
refresh();
std::this_thread::sleep_for(std::chrono::milliseconds((int) (200)));
}
nodelay(stdscr, false);
mvprintw(ROW, 0, status.c_str());
refresh();
getch();
}
bool Engine::process_input() {
int ch;
while((ch = getch()) != ERR) {
if(ch == 'q') { return status = "Quitting", false; }
else if(ch == 'w') { if(dir != Direction::DOWN) dir = Direction::UP; }
else if(ch == 's') { if(dir != Direction::UP) dir = Direction::DOWN; }
else if(ch == 'd') { if(dir != Direction::LEFT) dir = Direction::RIGHT; }
else if(ch == 'a') { if(dir != Direction::RIGHT) dir = Direction::LEFT; }
}
return true;
}
bool Engine::exit_summary() {
if(tail.size() >= ROW * COL) {
return status = std::format("You filled the whole board and won! Score: {}\n", m_score), false;
}
if(std::any_of(tail.begin(), tail.end(), [&](const vec2& segment) { return segment == head; })) {
return status = std::format("You collided with yourself and died! Score: {}\n", m_score), false;
}
return true;
}
void Engine::determine_index_zero_pos() {
prev_head.set(head.x, head.y);
if(dir == Direction::LEFT) { head.x = (head.x + COL - 1) % COL; }
else if(dir == Direction::RIGHT) { head.x = (head.x + 1) % COL; }
else if(dir == Direction::UP) { head.y = (head.y + ROW - 1) % ROW; }
else if(dir == Direction::DOWN) { head.y = (head.y + 1) % ROW; }
win.view[prev_head.y][prev_head.x] = '.';
win.view[head.y][head.x] = '*';
}
void Engine::move_tail_indicies() {
if(tail.empty()) return;
vec2 last_tail = tail.back();
tail.pop_back();
tail.push_front(prev_head);
if(last_tail.x < COL && last_tail.y < ROW) { win.view[last_tail.y][last_tail.x] = '.'; }
}
void Engine::add_tail_and_respawn_fruit() {
if(fruit != head) return;
vec2 new_pos { 0, 0 };
do { new_pos.set((rand() % ROW), (rand() % COL)); }
while (new_pos == head || std::any_of(tail.begin(), tail.end(), [&](const vec2& seg) { return seg == new_pos; }));
fruit = new_pos;
tail.push_front(vec2 { ROW, COL });
m_score++;
}
+42
View File
@@ -0,0 +1,42 @@
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <time.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))
{
add_tail_and_respawn_fruit(&engine);
determine_index_zero_pos(&engine);
move_tail_indicies(engine.tail, engine.tail_size, &engine.prev_head, engine.view);
engine_display(&engine);
nanosleep(&req, NULL);
}
for(int i = 0; i < engine.tail_size; i++) {
free(engine.tail[i]);
}
write(1, status, sizeof(status));
write(1, "\033[?25h", 6);
tcsetattr(0, TCSANOW, &oldt);
return 0;
}
-7
View File
@@ -1,7 +0,0 @@
#include <engine.hpp>
int main () {
Engine engine;
engine.run();
return 0;
}