snake.c 21.69 KiB
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<locale.h>
#include<ctype.h>
#include "structs.h"
#include "multiplatformLib.h"
//debugmalloc from infoc
#include "debugmalloc.h"
#define green "\e[0;32m"
#define blue "\e[0;34m"
//There are lots of hints on https://infoc.eet.bme.hu/
//#define windows 1 with #ifndef __linux__ solved...
//typedef unsigned long long int pointer; //Not optimal and not required. sometimes even the worst idea...
//-----------methods--------------
int isUnicodeEncoding(int set){
static int bl = 0;
if(set){
bl = 1;
}
return bl;
}
/**
* Only the first byte is required
*/
int checkUnicharLen(char c){
int i = 0;
if(!isUnicodeEncoding(0)){
return 1; //int windows-xyzw every char has 1 len;
}
if(!(c & 0x80)){
return 1;
}
while(c & 0x80){
i++;
c = c << 1;
if(i > 8){
return EOF;
}
}
return i;
}
void printChar(unichar c){
int len;
len = checkUnicharLen(c.bytes.c[0]);
for(int i = 0; i < len; i++){
printf("%c", c.bytes.c[i]);
}
}
//fueoetoeoecsoeoe *next //what to NOT do
int readFile(FILE *file, Matrix *matrix){
int c, len, maxLineLen = 0, lineCount = 1, lineLen = 0; //lineCount = (3,1) ??? why?... i was just bored
struct Vec2i pos;
pos.x = 0;
pos.y = 0;
linkedString *linkedStr = 0, *current = 0, *next;
//linkedStr = malloc(sizeof(linkedString));
while(c = fgetc(file), c != EOF){
next = malloc(sizeof(linkedString));
if(next == NULL){
return EOF;
}
memset(next, 0, sizeof(linkedString)); //TODO remove after debugging
next->next = 0;
while (c == '\r')
{
c = fgetc(file);
if(c == -1){
break;
}
}
next->value.bytes.c[0] = c;
len = checkUnicharLen(c);
if(isUnicodeEncoding(0) && len -1){
for (int i = 1; i < len; i++){
c = fgetc(file);
if(c == -1){
return EOF;
}
next->value.bytes.c[i] = c;
}
}
else if(c == '\n'){ //checking newline (unichar can't be a newline char (\n))
lineCount++;
if(lineLen > maxLineLen){
maxLineLen = lineLen;
}
lineLen = -1;
}
lineLen++;
//next.value = chars;
if(current != 0){
current->next = next;
}
else
{
linkedStr = next;
//current = next;
}
current = next;
}
maxLineLen = maxLineLen / 2 + maxLineLen % 2;
matrix->height = lineCount;
matrix->width = maxLineLen;
matrix->matrix = calloc(maxLineLen, sizeof(chunk*));
if(matrix->matrix == NULL){
printf("failed to allocate memory for the matrix");
return EOF-1;
}
for (int i = 0; i < maxLineLen; i++){
matrix->matrix[i] = calloc(lineCount, sizeof(chunk));
if(matrix->matrix[i] == NULL){
printf("failed to allocate memory for the matrix");
return EOF-1;
}
for(int n = 0; n < lineCount; n++){
for(int a = 0; a < 2; a++){
matrix->matrix[i][n].chars[a].bytes.c[0] = ' ';
}
}
}
current = linkedStr;
while(current != 0){
//tmp = current;
//current = current->next;
//free(tmp);
if(current->value.bytes.c[0] == '\n'){
pos.x = 0;
pos.y++;
//break;
}
else{
for(int charPos = 0; charPos < 4; charPos++){
matrix->matrix[pos.x/2][pos.y].chars[pos.x%2].bytes.c[charPos] = current->value.bytes.c[charPos];
}
pos.x++;
}
next = current;
current = current->next;
free(next);
}
return 0;
}
void rmMatrix(Matrix *map){
for(int i = 0; i < map->width; i++){
free(map->matrix[i]);
}
free(map->matrix);
}
//Use what chunk,
//witch screen coordinates?
//just the screen data
void printChunk(chunk ch, Pos pos, screenData *scrDat){
pos.x -= scrDat->pos.x;
pos.y -= scrDat->pos.y;
if(pos.x < 0 || pos.y < 0 || pos.x >= scrDat->pos.x || pos.y >= scrDat->pos.y){
return; //if pos is not on the screen, just do nothing.
}
printf("\e[%d;%dH", pos.x * 2, pos.y);
for(int i = 0; i < 2; i++){
printChar(ch.chars[i]);
}
}
void print(chunk ch, Pos pos, screenData *scrDat, int width, int height){
pos.x = pos.x - scrDat->pos.x;
if(pos.x < 0){
if(scrDat->isXRepeat){
pos.x += width;
}
else{
return;
}
}
pos.y = pos.y - scrDat->pos.y;
if(pos.y < 0){
if(scrDat->isYRepeat){
pos.y += height;
}
else{
return;
}
}
printChunk(ch, pos, scrDat);
}
int updateScreenSize(Matrix *map, screenData *scrDat){
struct Vec2i size = getWindowSize();
size.x = size.x / 2;
size.y = size.y / 2;
if(size.x == scrDat->size.x && size.y == scrDat->size.y){
return 0; //no change happened
}
scrDat->size.x = size.x;
scrDat->size.y = size.y;
if(scrDat->repeatMap){
scrDat->isXRepeat = size.x < map->width;
scrDat->isYRepeat = size.y < map->height;
}
return 1;
}
//Update screen if required
void updateScreen(Matrix *map, screenData *scrDat, snakeChain *head, Direction d, Food *food){
int do_update;
do_update = updateScreenSize(map, scrDat);
if(do_update){
if(scrDat->size.x < map->width){
scrDat->pos.x = mod((head->pos.x + scrDat->size.x) / 2 ,map->width, scrDat->repeatMap);
}else{
scrDat->pos.x = 0;
}
if(scrDat->size.y < map->height){
scrDat->pos.y = mod((head->pos.y + scrDat->size.y) / 2 ,map->height, scrDat->repeatMap);
}else{
scrDat->pos.y = 0;
}
}
else{
int min_x = scrDat->pos.x;
int max_x = scrDat->pos.x + scrDat->size.x;
int min_y = scrDat->pos.y;
int max_y = scrDat->pos.y + scrDat->size.y;
switch (d)
{
case LEFT:
if(scrDat->size.x < map->width && mod(head->pos.x - min_x, map->width, 1) < 6){
scrDat->pos.x = mod(min_x - 1, map->width, scrDat->isXRepeat);
do_update = 1;
}
break;
case RIGHT:
if(scrDat->size.x < map->width && mod(max_x - head->pos.x, map->width, 1) < 6){
scrDat->pos.x = mod(mod(max_x + 1, map->width, scrDat->isXRepeat) - scrDat->size.x, map->width, true);
do_update = 1; //1 equal true :D
}
break;
case UP:
if(scrDat->size.y < map->height && mod(head->pos.y - min_y, map->height, 1) < 6){
scrDat->pos.y = mod(min_y - 1, map->height, scrDat->isYRepeat);
do_update = 1;
}
break;
case DOWN:
if(scrDat->size.y < map->height && mod(max_y - head->pos.y, map->height, 1) < 6){
scrDat->pos.y = mod(mod(max_y + 1, map->height, scrDat->isYRepeat) - scrDat->size.y, map->height, true);
do_update = 1; //1 equal true :D
}
break;
default:
//printf("Something went wrong. Direction value is %d.\n", d);
break;
}
}
if(do_update){
Pos pos;
for (pos.y = scrDat->pos.y; pos.y < scrDat->pos.y + scrDat->size.y; pos.y++){
printf("\e[%d;0H\e[ K", pos.y); //Clear the screen line
for (pos.x = scrDat->pos.x; pos.x < scrDat->pos.x + scrDat->size.x; pos.x += 2){
if(pos.x < scrDat->size.x + map->width || scrDat->isXRepeat){
printChunk(map->matrix[pos.x%map->width][pos.y&map->height], pos, scrDat);
}
}
if(pos.y >= scrDat->size.y && !scrDat->isYRepeat){
break;
}
}
while(food->next->next != NULL){
chunk c;
food = food->next;
//c.chars[0].bytes.c[0] = 'X';
c.chars[0].bytes.c[0] = 'X';
c.chars[0].bytes.c[0] = 'X';
printf(green);
printChunk(c, food->pos, scrDat);
printf("\e[0m");
}
}
}
void getControl(screenData *scrDat, Direction *direction){
int i;
while(i = getNextChar(), i != EOF){
Direction next = NONE;
switch (i)
{
case 'a':
next = LEFT;
break;
case 's':
next = DOWN;
break;
case 'w':
next = UP;
break;
case 'd':
next = RIGHT;
break;
default:
next = NONE;
continue; //we don't have to registrane a non command...
}
if(next == *direction){
scrDat->commands[0] = NONE;
scrDat->commands[1] = NONE;
continue;
}
// [a && b || c] = [c || a && b] = [(a && b) || c]
if(((*direction == UP || *direction == DOWN) && (next == RIGHT || next == LEFT))
|| ((*direction == RIGHT || *direction == LEFT) && (next == UP || next == DOWN))){
scrDat->commands[0] = next;
}
else
{
if(scrDat->commands[0] != NONE){
scrDat->commands[1] = next;
}
}
}
}
int isAir(chunk c){
return c.chars[0].bytes.c[0] == ' ' && c.chars[1].bytes.c[0] == ' ';
}
void updateFood(Matrix *map, int *foodTick, int feedAmount, Food *firstFood, snakeChain *firstSnake, screenData *scrDat){
if((*foodTick)++ > feedAmount){
*foodTick = 0;
for(int i = 0; i < 128; i++){
Pos pos;
int isFree = 1;
Food *food = firstFood;
snakeChain *snake = firstSnake;
pos.x = rand()%map->width;
pos.y = rand()%map->height;
if(!isAir(map->matrix[pos.x][pos.y])){
continue;
}
while (food->next->next != NULL)
{
food = food->next;
if(food->pos.x == pos.x && food->pos.y == pos.y){
isFree = false;
break;
}
}
if(!isFree){
continue;
}
while(snake != NULL){
if(snake->pos.x == pos.x && snake->pos.y == pos.y){
isFree = false;
break;
}
snake = snake->next;
}
if(!isFree){
continue;
}
//pos is available
{
Food *new = malloc(sizeof(Food));
if(new != NULL){
chunk c;
new->next = food->next;
food->next = new;
new->pos = pos;
c.chars[0].bytes.c[0] = 'X';
c.chars[0].bytes.c[0] = 'X';
printf(green);
print(c, pos, scrDat, map->width, map->height);
printf("\e[0m");
}
break;
}
}
}
}
//EOF fatal error, 1 game over
int updateSnake(Matrix *map, screenData *scrDat, Direction d, snakeChain *head, Food *food, int canBite){
Pos pos = head->pos;
switch (d)
{
case UP:
pos.y++;
break;
case DOWN:
pos.y--;
break;
case LEFT:
pos.x--;
break;
case RIGHT:
pos.x++;
break;
default:
return EOF;
break;
}
if(pos.x < 0 || pos.x >= map->width){
if(scrDat->repeatMap){
pos.x = mod(pos.x, map->width, true);
}
else
{
return 1; //GAME OVER reached map end;
}
}
if(pos.y < 0 || pos.y >= map->height){
if(scrDat->repeatMap){
pos.y = mod(pos.y, map->height, true);
}
else
{
return 1; //GAME OVER reached map end;
}
}
if(isAir(map->matrix[pos.x][pos.y])){
int isFood = 0;
snakeChain *snake = head;
Pos tmp_pos1 = head->pos, tmp_pos2;
while(food->next->next != NULL){
if(food->next->pos.x == pos.x && food->next->pos.y == pos.y){
Food *current = food->next;
isFood = true;
food->next = food->next->next;
free(current); //Snake ate this food. It is safe to free;
break;
}
food = food->next;
}
while (snake->next != 0)
{
chunk c;
if(snake != head && snake->pos.x == head->pos.x && snake->pos.y == head->pos.y){
if(snake->next != NULL){
if(canBite){
snakeChain *current = snake;
snake = snake->next;
current->next = NULL;
while (snake != NULL)
{
current = snake;
snake = snake->next;
free(current);
}
}
else
{
return 1; //GAME OVER snake hit itself;
}
}else
{
isFood = false;
}
}
snake = snake->next;
tmp_pos2 = snake->pos;
snake->pos = tmp_pos1;
tmp_pos1 = tmp_pos2;
c.chars[0].bytes.c[0] = '>';
c.chars[0].bytes.c[0] = '<';
if(snake->next == NULL && isFood){
isFood = false;
snake->next = malloc(sizeof(snake));
if(snake->next == NULL){
return EOF;
}
snake->next->next = NULL;
snake->pos.x = -1;
snake->pos.y = -1;
}
if(snake->pos.x == -1){
continue; //don't render snake part with initial position
}
printf(blue);
print(c, snake->pos, scrDat, map->width, map->height); //TODO direction snake
printf("\e[0m");
}
if(tmp_pos2.x != -1){
chunk c;
c.chars[0].bytes.c[0] = ' ';
c.chars[0].bytes.c[0] = ' ';
print(c, tmp_pos2, scrDat, map->width, map->height); //set this to air.
}
chunk c;
c.chars[0].bytes.c[0] = '(';
c.chars[0].bytes.c[0] = ')';
printf(blue);
print(c, head->pos, scrDat, map->width, map->height); //TODO direction snake
printf("\e[0m");
}
else
{
return 1; //GAME OVER hit the wall;
}
return 0;
}
//------------config loader------------
int loadConfig(int *tickSpeed, int *repeatMap, int *feedAmount, int *canBite){
FILE *config;
config = fopen("config.cfg", "r");
//int stillFile = 1; //boolean to show file is ended...
if(config == NULL){
return -1;
}
while(1){
char name[32] = {0}, c; //it have to be enough;
while(c = fgetc(config), c != -1 && isspace(c));
if(c == -1){
break;
}
//name[0] = c;
for(int i = 0; i < 32 && !isspace(c) && c != '='; i++, c = fgetc(config)){
name[i] = c;
}
//c = fgetc(config);
while(c != '='){
c = fgetc(config);
if(c == -1){
printf("I can't understand the config file: %s", name);
return EOF;
}
}
if(strncmp(name, "use_utf8", 9) == 0){
int bl;
fscanf(config, " %d", &bl);
isUnicodeEncoding(bl);
}
else if(strncmp(name, "tickspeed", 10) == 0){
fscanf(config, " %d", tickSpeed);
}
else if(strncmp(name, "repeatMap", 10) == 0){
fscanf(config, " %d", repeatMap);
}
else if(strncmp(name, "feed_amount", 12) == 0){
fscanf(config, " %d", feedAmount);
}
else if(strncmp(name, "can_bite", 9) == 0){
fscanf(config, " %d", canBite);
}
else{
printf("Unknown keyword: %s", name);
}
}
return 0;
}
//------------LOOP METHODs--------------
//Tick the game, step the snake, collect and place food.
//check new direction
//update display
//update food state
//step snake
int tick(Matrix *map, screenData *scrDat, snakeChain *snake, Direction *d, Food *food, int feedAmount, int canBite){
static int foodTick = 0;
getControl(scrDat, d);
if(scrDat->commands[0] != NONE){
*d = scrDat->commands[0];
}
updateScreen(map, scrDat, snake, *d, NULL); // TODO non-null object
if(d == NONE){
chunk c;
c.chars[0].bytes.c[0] = '(';
c.chars[0].bytes.c[0] = ')';
printf(blue);
print(c, snake->pos, scrDat, map->width, map->height);
printf("\e[0m");
return 0; // waiting for user input.
}
updateFood(map, &foodTick, feedAmount, food, snake, scrDat);
updateSnake(map, scrDat, *d, snake, food, canBite);
return 0;
}
int loop(Matrix *matrix, int tickspeed, int repeatMap, int feedAmount, int canBite){
Direction d = DOWN;
screenData scrDat;
snakeChain snake, *chain;
Food food;
food.next = malloc(sizeof(Food));
if(food.next == NULL){
return -1;
}
food.next->next = NULL; //Create food guards.
while(true){
Pos p;
p.x = rand()%matrix->width;
p.y = rand()&matrix->height;
if(isAir(matrix->matrix[p.x][p.y])){
snake.pos = p;
break;
}
}
chain = &snake;
for(int i = 1; i < 3; i++){
Pos p;
p.x = -1;
p.y = -1;
chain->next = malloc(sizeof(snakeChain));
if(chain->next == NULL){
return -1;
}
chain = chain->next;
chain->pos = p;
}
scrDat.size.x = 0;
scrDat.size.y = 0;
scrDat.repeatMap = repeatMap;
while(42){
if(tick(matrix, &scrDat, &snake, &d, &food, feedAmount, canBite)){
break;
}
unisleep(tickspeed); //Special sleep to work both in windows and unix environment
}
return 0;
}
//------------TESTING METHODS-------------
void _testprint(Matrix *map){
for (int y = 0; y < map->height; y++){
for(int x = 0; x < map->width; x++){
for(int i = 0; i < 2; i++){
//printf("%c", map->matrix[x][y].chars->bytes.c[i * 4]); //WTF... I didn't indexed correctly, but accidentaly I've got the right value...
printChar(map->matrix[x][y].chars[i]);
}
//printf("|");
}
printf("\n");
}
}
void _print1char(Matrix *map){
int x, y;
while(scanf(" %d%d", &x, &y) == 2){
if(x >= 0 && y >= 0 && x < map->width && y < map->height){
for(int i = 0;i < 2; i++){
printf("\nvalue: %hx, %hx, %hx, %hx\n",
(unsigned)map->matrix[x][y].chars[i].bytes.c[0],
(unsigned)map->matrix[x][y].chars[i].bytes.c[1],
(unsigned)map->matrix[x][y].chars[i].bytes.c[2],
(unsigned)map->matrix[x][y].chars[i].bytes.c[3]);
printf("as character: \"");
printChar(map->matrix[x][y].chars[i]);
printf("\"\n");
}
}
}
}
//-------------CORE METHOD----------------
int core(int argc, char const *argv[])
{
FILE *f;
Matrix map;
initMultiplatform(); // init stuff.
int tickspeed = 100, repeatMap = 0, feedAmount = 20, canBite = true; // if no config, default value
//----load config----
if(loadConfig(&tickspeed, &repeatMap, &feedAmount, &canBite)){
printf("Error while loading config...");
}
//----init tasks----
if(isUnicodeEncoding(0)){
#ifndef __linux__
setlocale(LC_ALL, ".utf-8");
#endif
}
//----import map----
if(argc == 1){
printf("Usage: snake <map name> [<snake skin>]");
return 0;
}
else{
f = fopen(argv[1], "rb");
if(f == NULL){
printf("Map file not found: %s", argv[1]);
return EOF;
}
readFile(f, &map);
}
//----start game----
loop(&map, tickspeed, repeatMap, feedAmount, canBite);
//test code
//printf("map:\n");
//_testprint(&map);
//_print1char(&map);
/* code */
//free stuff
rmMatrix(&map);
return 0;
}
/*
int main(int argc, char const *argv[])
{
return core(argc, argv);
}
*/
int main(int argc, char const *argv[])
{
//2 + 3; //... this does nothing...
int ret;
char const *array[] = {argv[0], "map1.txt"};
ret = core(2, array);
printf("\npress any key to continue");
getchar();
//return 0, ret; //Miért van ez a függvény tele szeméttel??? pl 0, smt...
return ret; // így szép.
}