converting to c
This commit is contained in:
@@ -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
@@ -1,15 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_C_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
project(snake)
|
project(snake LANGUAGES C)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.c
|
||||||
src/engine.cpp
|
src/engine.c
|
||||||
)
|
)
|
||||||
add_executable(snake ${SOURCES})
|
add_executable(snake ${SOURCES})
|
||||||
|
|
||||||
target_include_directories(snake PRIVATE include)
|
target_include_directories(snake PRIVATE include)
|
||||||
find_package(Curses REQUIRED)
|
|
||||||
target_link_libraries(snake PRIVATE ${CURSES_LIBRARIES})
|
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#include <engine.hpp>
|
|
||||||
|
|
||||||
int main () {
|
|
||||||
Engine engine;
|
|
||||||
engine.run();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user