├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── preview.png ├── snake.png └── source ├── colors.h └── snake.c /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://github.com/DosX-dev/DosX-dev/blob/main/donate.md'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 DosX 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐍 TinySnake 2 | Little game "Snake" for Windows in C (.EXE size is 3.5 kilobytes) 3 | 4 | ### [Download compiled as .EXE](https://github.com/DosX-dev/TinySnake-game/releases/tag/Builds) 5 | 6 | ![](snake.png) 7 | 8 | | Requirement | Minimum | 9 | |---|---| 10 | | Operating system | Windows 95 11 | | Processor | Any 1 GHz CPU 12 | | Memory | 1 mb 13 | | Hard drive space | 3.5 kb free 14 | 15 | ### A little about the gameplay 16 | * The borders do not kill the snake, but teleport to the other side of the map 17 | * 10 points for each apple 18 | * Use the English keyboard layout to control 19 | * Don't resize the window during gameplay if you don't want to break the renderer 20 | * You have to enjoy the game 21 | 22 | 23 | 24 | Enjoy! ;) 25 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DosX-dev/TinySnake-game/550468903f4234daec5480734a8a08679bb16529/preview.png -------------------------------------------------------------------------------- /snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DosX-dev/TinySnake-game/550468903f4234daec5480734a8a08679bb16529/snake.png -------------------------------------------------------------------------------- /source/colors.h: -------------------------------------------------------------------------------- 1 | // Coded by DosX 2 | // GitHub: https://github.com/DosX-dev 3 | // Windows console colors module for TinySnake 4 | 5 | // Foreground text colors 6 | #define FOREGROUND_BLACK 0x00 7 | #define FOREGROUND_BLUE 0x01 8 | #define FOREGROUND_GREEN 0x02 9 | #define FOREGROUND_RED 0x04 10 | #define FOREGROUND_GRAY 0x08 11 | #define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE) 12 | #define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE) 13 | #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN) 14 | #define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) 15 | #define FOREGROUND_LIGHT_BLUE (FOREGROUND_BLUE | FOREGROUND_INTENSITY) 16 | #define FOREGROUND_LIGHT_GREEN (FOREGROUND_GREEN | FOREGROUND_INTENSITY) 17 | #define FOREGROUND_LIGHT_CYAN (FOREGROUND_CYAN | FOREGROUND_INTENSITY) 18 | #define FOREGROUND_LIGHT_RED (FOREGROUND_RED | FOREGROUND_INTENSITY) 19 | #define FOREGROUND_LIGHT_MAGENTA (FOREGROUND_MAGENTA | FOREGROUND_INTENSITY) 20 | #define FOREGROUND_LIGHT_YELLOW (FOREGROUND_YELLOW | FOREGROUND_INTENSITY) 21 | #define FOREGROUND_BRIGHT_WHITE (FOREGROUND_WHITE | FOREGROUND_INTENSITY) 22 | 23 | // Background colors 24 | #define BACKGROUND_BLACK 0x00 25 | #define BACKGROUND_BLUE 0x10 26 | #define BACKGROUND_GREEN 0x20 27 | #define BACKGROUND_RED 0x40 28 | #define BACKGROUND_GRAY 0x80 29 | #define BACKGROUND_CYAN (BACKGROUND_GREEN | BACKGROUND_BLUE) 30 | #define BACKGROUND_MAGENTA (BACKGROUND_RED | BACKGROUND_BLUE) 31 | #define BACKGROUND_YELLOW (BACKGROUND_RED | BACKGROUND_GREEN) 32 | #define BACKGROUND_WHITE (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) 33 | #define BACKGROUND_LIGHT_BLUE (BACKGROUND_BLUE | BACKGROUND_INTENSITY) 34 | #define BACKGROUND_LIGHT_GREEN (BACKGROUND_GREEN | BACKGROUND_INTENSITY) 35 | #define BACKGROUND_LIGHT_CYAN (BACKGROUND_CYAN | BACKGROUND_INTENSITY) 36 | #define BACKGROUND_LIGHT_RED (BACKGROUND_RED | BACKGROUND_INTENSITY) 37 | #define BACKGROUND_LIGHT_MAGENTA (BACKGROUND_MAGENTA | BACKGROUND_INTENSITY) 38 | #define BACKGROUND_LIGHT_YELLOW (BACKGROUND_YELLOW | BACKGROUND_INTENSITY) 39 | #define BACKGROUND_BRIGHT_WHITE (BACKGROUND_WHITE | BACKGROUND_INTENSITY) 40 | -------------------------------------------------------------------------------- /source/snake.c: -------------------------------------------------------------------------------- 1 | // Coded by DosX 2 | // GitHub: https://github.com/DosX-dev 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "colors.h" 11 | 12 | #define SIZE 25 13 | 14 | #define WIDTH SIZE 15 | #define HEIGHT SIZE 16 | 17 | #define SPEED_DELAY 100 18 | 19 | #define true 0b1 // 20 | #define false 0b0 // I know there are predefined values ​​like TRUE, FALSE, NULL etc 21 | #define short 32768 // But I prefer using these values ​​in small case. 22 | #define null (void *)0b0 // 23 | 24 | #define main _start 25 | 26 | int gameover = false, 27 | score = 0, 28 | snakeX[short], snakeY[short], 29 | snake_length = 1, 30 | foodX = 0, foodY = 0, 31 | dirX = 0, dirY = 0; 32 | 33 | HANDLE hConsole = null; 34 | CONSOLE_SCREEN_BUFFER_INFO consoleInfo; 35 | 36 | void setup() { 37 | snakeX[0] = WIDTH / 2; 38 | snakeY[0] = HEIGHT / 2; // Set snake head at center of game zone 39 | 40 | srand(time(0x00)); // Initialize the random number generator with the current time 41 | foodX = 1 + rand() % (WIDTH - 2); 42 | foodY = 1 + rand() % (HEIGHT - 2); 43 | 44 | dirX = 0; 45 | dirY = 1; // Move down 46 | } 47 | 48 | void refillRenderZone() { 49 | SetConsoleCursorPosition(hConsole, (COORD){0, 2}); // Skip 2 lines 50 | } 51 | 52 | void draw(char *text, int color) { 53 | GetConsoleScreenBufferInfo(hConsole, &consoleInfo); 54 | SetConsoleTextAttribute(hConsole, color); 55 | printf(text); 56 | SetConsoleTextAttribute(hConsole, consoleInfo.wAttributes); 57 | } 58 | 59 | // Function to render the game screen 60 | void render() { 61 | refillRenderZone(); 62 | 63 | for (int i = 0; i < HEIGHT; i++) // Loop through the game board rows 64 | { 65 | for (int j = 0; j < WIDTH; j++) // Loop through the game board columns 66 | { 67 | if (i == 0 || i == HEIGHT - 1 || j == 0 || j == WIDTH - 1) // Check if it's a border cell 68 | draw(" ", !gameover ? BACKGROUND_CYAN : BACKGROUND_RED); // Draw a cyan-colored border 69 | else if (i == snakeY[0] && j == snakeX[0]) // Check if it's the snake's head cell 70 | draw(!gameover ? "^^" : "oO", BACKGROUND_WHITE | FOREGROUND_BLACK); // Draw the snake's head with eyes 71 | else if (i == foodY && j == foodX) // Check if it's the food cell 72 | draw(" ", BACKGROUND_LIGHT_RED); // Draw an apple (food) 73 | else { 74 | int isBody = false; 75 | for (int k = 1; k < snake_length; k++) // Loop through the snake's body cells 76 | { 77 | if (snakeX[k] == j && snakeY[k] == i) // Check if it's a cell in the snake's body 78 | { 79 | draw("##", BACKGROUND_GRAY | FOREGROUND_GRAY); // Draw the snake's 80 | // body segment 81 | isBody = true; 82 | break; 83 | } 84 | } 85 | if (!isBody) { 86 | printf(" "); // Draw two empty spaces for an empty cell 87 | } 88 | } 89 | } 90 | printf("\n"); // Move to the next row 91 | } 92 | 93 | draw("SCORE", BACKGROUND_YELLOW); // Draw the "SCORE" label in yellow 94 | printf(": %d\n", score); // Display the current score value 95 | } 96 | 97 | // Function to handle user input 98 | void input() { 99 | if (_kbhit()) { 100 | switch (tolower(_getch())) { 101 | case 'a': 102 | // Ignore if the current direction is right (moving to the 103 | // right) to avoid going backward 104 | if (dirX != 1) { 105 | dirX = -1; // Move left 106 | dirY = 0; 107 | } 108 | break; 109 | case 'd': 110 | // Ignore if the current direction is left (moving to the left) 111 | // to avoid going backward 112 | if (dirX != -1) { 113 | dirX = 1; // Move right 114 | dirY = 0; 115 | } 116 | break; 117 | case 'w': 118 | // Ignore if the current direction is down (moving downwards) to 119 | // avoid going backward 120 | if (dirY != 1) { 121 | dirX = 0; 122 | dirY = -1; // Move up 123 | } 124 | break; 125 | case 's': 126 | // Ignore if the current direction is up (moving upwards) to 127 | // avoid going backward 128 | if (dirY != -1) { 129 | dirX = 0; 130 | dirY = 1; // Move down 131 | } 132 | break; 133 | } 134 | } 135 | } 136 | 137 | // Function to update the game logic 138 | void logic() { 139 | int prevX = snakeX[0], 140 | prevY = snakeY[0], 141 | prev2X, prev2Y; 142 | snakeX[0] += dirX; 143 | snakeY[0] += dirY; 144 | 145 | // Teleport the snake to the opposite side of the map if it hits a wall 146 | if (snakeX[0] == 0) 147 | snakeX[0] = WIDTH - 2; 148 | else if (snakeX[0] == WIDTH - 1) 149 | snakeX[0] = 1; 150 | if (snakeY[0] == 0) 151 | snakeY[0] = HEIGHT - 2; 152 | else if (snakeY[0] == HEIGHT - 1) 153 | snakeY[0] = 1; 154 | 155 | for (int i = 1; i < snake_length; i++) { 156 | prev2X = snakeX[i]; 157 | prev2Y = snakeY[i]; 158 | snakeX[i] = prevX; 159 | snakeY[i] = prevY; 160 | prevX = prev2X; 161 | prevY = prev2Y; 162 | } 163 | 164 | // Check if the snake eats the food 165 | if (snakeX[0] == foodX && snakeY[0] == foodY) { 166 | // Generate new random coordinates for food within the game field (excluding the borders) 167 | do { 168 | foodX = 1 + rand() % (WIDTH - 2); 169 | foodY = 1 + rand() % (HEIGHT - 2); 170 | } while (foodX == snakeX[0] && foodY == snakeY[0]); 171 | 172 | // Check if the food spawns on the snake's body 173 | int spawnOnBody = true; 174 | for (int i = 1; i < snake_length; i++) { 175 | if (foodX == snakeX[i] && foodY == snakeY[i]) { 176 | spawnOnBody = true; 177 | break; 178 | } 179 | } 180 | 181 | // If the food spawns on the snake's body, try generating new coordinates again 182 | while (spawnOnBody) { 183 | foodX = 1 + rand() % (WIDTH - 2); 184 | foodY = 1 + rand() % (HEIGHT - 2); 185 | 186 | spawnOnBody = false; 187 | for (int i = 0; i < snake_length; i++) { 188 | // If the coordinates of the Apple are on the body of the snake, then the cycle continues 189 | if (foodX == snakeX[i] && foodY == snakeY[i]) { 190 | spawnOnBody = true; 191 | break; 192 | } 193 | } 194 | } 195 | 196 | score += 10; // Increase the score 197 | snake_length++; // Increase the length of the snake 198 | } 199 | 200 | // Check for collision with itself 201 | for (int i = 1; i < snake_length; i++) { 202 | if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) { 203 | gameover = true; // Game over if the snake collides with itself 204 | break; 205 | } 206 | } 207 | 208 | /* 209 | // Check for collision with the wall 210 | if (snakeX[0] == 0 || snakeX[0] == WIDTH - 1 || snakeY[0] == 0 || snakeY[0] 211 | == HEIGHT - 1) 212 | { 213 | gameover = true; // Game over if the snake hits the border 214 | } 215 | */ 216 | } 217 | 218 | // Oh ok, legacy code. Really bad code 219 | // int getBitness() { return sizeof(void *) == 0x4 ? 32 : 64; } 220 | 221 | #if defined(__i386__) // i386 (32-bit) 222 | #define bitness 32 223 | #elif defined(__x86_64__) // x86-64 (AMD64) 224 | #define bitness 64 225 | #else 226 | #define bitness 32 227 | #endif 228 | 229 | void main() { 230 | char title[short]; 231 | 232 | sprintf(title, "Tiny Snake (x%d)", bitness); 233 | 234 | SetConsoleTitleA(title); 235 | 236 | int buffer[] = {(WIDTH * 2) + 1, HEIGHT + 8}; 237 | 238 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 239 | 240 | SMALL_RECT windowSize = {0, 0, buffer[0] - 1, buffer[1] - 1}; 241 | SetConsoleWindowInfo(hConsole, true, &windowSize); 242 | 243 | SetConsoleScreenBufferSize(hConsole, (COORD){buffer[0], buffer[1]}); 244 | 245 | CONSOLE_CURSOR_INFO cursorinfo = {0}; 246 | cursorinfo.dwSize = 1; 247 | cursorinfo.bVisible = FALSE; // Turns off the blinking cursor (Thank to https://github.com/R32GTT) 248 | SetConsoleCursorInfo(hConsole, &cursorinfo); 249 | 250 | printf("Coded by DosX-dev (GitHub)\nUSE ONLY ENGLISH KEYBOARD LAYOUT! (WASD)\n"); 251 | 252 | setup(); // Initialize the game 253 | 254 | while (!gameover) { 255 | render(); // Draw the current state of the game 256 | input(); // Handle user input 257 | logic(); // Update the game logic 258 | 259 | Sleep(SPEED_DELAY); // Add a delay to control the snake's speed 260 | } 261 | 262 | dirX = 0; 263 | dirY = 0; 264 | 265 | render(); // Run the renderer for the last time to draw the desired colors (lose) 266 | 267 | printf("\n"); 268 | 269 | draw("==============\n=", BACKGROUND_RED | FOREGROUND_RED); 270 | draw(" GAME OVER! ", BACKGROUND_WHITE | FOREGROUND_RED); 271 | draw("=\n==============", BACKGROUND_RED | FOREGROUND_RED); 272 | 273 | printf("\nPress X to exit"); 274 | 275 | while (true) { 276 | if (tolower(_getch()) == 'x') { 277 | return; 278 | } 279 | } 280 | } 281 | 282 | // I like coffee. Preferably with milk 283 | --------------------------------------------------------------------------------