Skip to content
Snippets Groups Projects
snake.c 30 KiB
Newer Older
KosmX's avatar
:D
KosmX committed
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
KosmX's avatar
KosmX committed

#ifdef DEBUG //include debugmalloc only in debug mode compile :D
KosmX's avatar
KosmX committed
#include "debugmalloc.h"
KosmX's avatar
KosmX committed
#endif
KosmX's avatar
KosmX committed

161400@bme.hu's avatar
161400@bme.hu committed
#include<locale.h>
KosmX's avatar
KosmX committed
#include<ctype.h>
KosmX's avatar
KosmX committed
#include<time.h>
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
#include "structs.h"
#include "multiplatformLib.h"
KosmX's avatar
:D
KosmX committed

KosmX's avatar
KosmX committed
#define true 1
#define false 0

KosmX's avatar
KosmX committed
//debugmalloc from infoc

KosmX's avatar
KosmX committed
#define green "\e[0;32m"
#define blue "\e[0;34m"

KosmX's avatar
KosmX committed
//There are lots of hints on https://infoc.eet.bme.hu/
KosmX's avatar
:D
KosmX committed

KosmX's avatar
KosmX committed
//#define windows 1 with #ifndef __linux__ solved...
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
//-----------methods--------------
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
/**
 * @param   set set the unicode encoding
 * @return  is the program in unicode mode
 */
161400@bme.hu's avatar
161400@bme.hu committed
int isUnicodeEncoding(int set){
    static int bl = 0;
    if(set){
        bl = 1;
    }
    return bl;
}
KosmX's avatar
:D
KosmX committed

/**
 * Only the first byte is required
KosmX's avatar
KosmX committed
 * @param   c   first byte of the unicode char
 * @return  How long is this utf-8 character (in bytes)
KosmX's avatar
:D
KosmX committed
 */
int checkUnicharLen(char c){
    int i = 0;
KosmX's avatar
KosmX committed
    if(!isUnicodeEncoding(0)){
KosmX's avatar
:D
KosmX committed
        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;
}

KosmX's avatar
KosmX committed
/**
 * @deprecated only used for debugging
 */
KosmX's avatar
:D
KosmX committed
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]);
    }
}

KosmX's avatar
KosmX committed
//fueoetoeoecsoeoe *next //what to NOT do
KosmX's avatar
:D
KosmX committed

KosmX's avatar
KosmX committed
/**
 * read file into the map matrix
 * @param   file    the map file "object"
 * @param   matrix  the pointer of the matrix to return
 * @return  was the read successful
 */
KosmX's avatar
:D
KosmX committed
int readFile(FILE *file, Matrix *matrix){
KosmX's avatar
KosmX committed
    int c, len, maxLineLen = 0, lineCount = 1, lineLen = 0; //lineCount = (3,1) ??? why?... i was just bored
KosmX's avatar
:D
KosmX committed
    struct Vec2i pos;
    pos.x = 0;
    pos.y = 0;
    linkedString *linkedStr = 0, *current = 0, *next;
    //linkedStr = malloc(sizeof(linkedString));
KosmX's avatar
KosmX committed
    while(c = fgetc(file), c != EOF){
KosmX's avatar
:D
KosmX committed
        next = malloc(sizeof(linkedString));
        if(next == NULL){
            return EOF;
        }
        #ifdef DEBUG
        memset(next, 0, sizeof(linkedString)); //fill it with zeros. only in debug mode
        #endif
KosmX's avatar
:D
KosmX committed
        next->next = 0;
        while (c == '\r')
        {
            c = fgetc(file);
            if(c == -1){
                break;
            }
        }
        next->value.bytes.c[0] = c;
        len = checkUnicharLen(c);
KosmX's avatar
KosmX committed
        if(isUnicodeEncoding(0) && len -1){
KosmX's avatar
:D
KosmX committed
            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++){
KosmX's avatar
KosmX committed
            matrix->matrix[i][n].isFood = 0; //That is NOT food
KosmX's avatar
:D
KosmX committed
            for(int a = 0; a < 2; a++){
KosmX's avatar
KosmX committed
                matrix->matrix[i][n].data.chars[a].bytes.c[0] = ' ';
KosmX's avatar
:D
KosmX committed
            }
        }
    }

    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++){
KosmX's avatar
KosmX committed
                matrix->matrix[pos.x/2][pos.y].data.chars[pos.x%2].bytes.c[charPos] = current->value.bytes.c[charPos];
KosmX's avatar
:D
KosmX committed
            }
            pos.x++; 
        }
        next = current;
        current = current->next;
        free(next);
    }
    return 0;
}

KosmX's avatar
KosmX committed
/** free up matrix
 * @param map matrix to free up
 */
KosmX's avatar
:D
KosmX committed
void rmMatrix(Matrix *map){
    for(int i = 0; i < map->width; i++){
        free(map->matrix[i]);
    }
    free(map->matrix);
}

KosmX's avatar
KosmX committed
//Use what chunk,
//witch screen coordinates?
//just the screen data
KosmX's avatar
KosmX committed
/**
 * prints a chunk into the screen, if the input position is relative
 * @param ch chunk to print
 * @param pos position relative to the screen
 * @param scrDat standard screen infos
 */
KosmX's avatar
KosmX committed
void printChunk(chunk ch, Pos pos, screenData *scrDat){
KosmX's avatar
KosmX committed
    //pos.x -= scrDat->pos.x;
    //pos.y -= scrDat->pos.y;
    if(pos.x < 0 || pos.y < 0 || pos.x >= scrDat->size.x || pos.y >= scrDat->size.y){
KosmX's avatar
KosmX committed
        return; //if pos is not on the screen, just do nothing.
    }
KosmX's avatar
KosmX committed
    if(ch.isFood){
KosmX's avatar
KosmX committed
        ch = scrDat->foodTexture.text[ch.data.FRand%scrDat->foodTexture.len];
        printf(green);
KosmX's avatar
KosmX committed
    }
KosmX's avatar
KosmX committed
    printf("\e[%d;%dH", pos.y+1, pos.x *2+1);
KosmX's avatar
KosmX committed
    for(int i = 0; i < 2; i++){
KosmX's avatar
KosmX committed
        printChar(ch.data.chars[i]); 
KosmX's avatar
KosmX committed
    }
KosmX's avatar
KosmX committed
    printf("\e[0m");
KosmX's avatar
KosmX committed
    #ifdef DEBUG
    printf("\e[0;0H\n"); //to update terminal after EVERY print but drasticlally slowing it down
    #endif
KosmX's avatar
KosmX committed
}

KosmX's avatar
KosmX committed
/** Print characters to the screen with an absolute position input
KosmX's avatar
KosmX committed
 * It will check if the character is on the screen
 * 
KosmX's avatar
KosmX committed
 * @param ch chunk to print
 * @param pos absolute position
 * @param scrDat standard screen data
 * @param width map width
 * @param height map height
KosmX's avatar
KosmX committed
 */
KosmX's avatar
KosmX committed
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);
KosmX's avatar
KosmX committed
}

KosmX's avatar
KosmX committed

/** prints snake chain onto the map, listening to directions
 */
//TODO
void printSnakeChunk(snakeChain *snake, Direction prevDir, screenData *scrDat){
KosmX's avatar
KosmX committed
    //TODO
}

KosmX's avatar
KosmX committed

/**
 * update the windows size if resized
 * @param map map for width and height
 * @param scrDat standard screen data. changes will apply
 * @return did the screen size changed
 */
KosmX's avatar
KosmX committed
int updateScreenSize(Matrix *map, screenData *scrDat){
    struct Vec2i size = getWindowSize();
KosmX's avatar
KosmX committed
    size.x = size.x / 2;
    size.y = size.y;// / 2;//Why half?
KosmX's avatar
KosmX committed
    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;
}

KosmX's avatar
KosmX committed
/**
 * Update the screen if it changed size of snek is near to the screen border
 * The game doesn't update every character in every round to be more efficient
 * 
 * @param map map
 * @param scrDat screenData
 * @param head the head of the snake
 * @param d snek's current moving direction
 */
KosmX's avatar
KosmX committed
void updateScreen(Matrix *map, screenData *scrDat, snakeChain *head, Direction d){
KosmX's avatar
KosmX committed
    int do_update;
    do_update = updateScreenSize(map, scrDat);
KosmX's avatar
KosmX committed

    if(do_update){
        if(scrDat->size.x < map->width){
KosmX's avatar
KosmX committed
            scrDat->pos.x = mod(head->pos.x - scrDat->size.x / 2 ,map->width, scrDat->repeatMap);
KosmX's avatar
KosmX committed
        }else{
            scrDat->pos.x = 0;
        }
        if(scrDat->size.y < map->height){
KosmX's avatar
KosmX committed
            scrDat->pos.y = mod(head->pos.y - scrDat->size.y / 2 ,map->height, scrDat->repeatMap);
KosmX's avatar
KosmX committed
        }else{
            scrDat->pos.y = 0;
KosmX's avatar
KosmX committed
        }
    }
    else{
KosmX's avatar
KosmX committed
        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++){
KosmX's avatar
KosmX committed
            printf("\e[%d;0H\e[K", pos.y - scrDat->pos.y + 1); //Clear the screen line
            if(pos.y >= map->height && !scrDat->isYRepeat){
                break;
            }
            for (pos.x = scrDat->pos.x; pos.x < scrDat->pos.x + scrDat->size.x; pos.x++){
                if(pos.x < map->width || scrDat->isXRepeat){
                    print(map->matrix[pos.x%map->width][pos.y%map->height], pos, scrDat, map->width, map->height);
KosmX's avatar
KosmX committed
                }
            }
        }
KosmX's avatar
KosmX committed
    }
}

KosmX's avatar
KosmX committed
/**
 * reads the stdin for new control commands (wasd), apply them to the scrDat commands standardDat
 * and dooesn't start to wait for user input if not available.
 * 
 * @param scrDat standard game data
 * @param direction current direction
 */
KosmX's avatar
KosmX committed
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 registrate a non command...
        }

        if(*direction == NONE){
            *direction = next;
            continue;
KosmX's avatar
KosmX committed
        }

        if(next == *direction){
            scrDat->commands[0] = NONE;
            scrDat->commands[1] = NONE;
            continue;
        }
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
        // [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;
            }
        }
    }
}

KosmX's avatar
KosmX committed
/**
 * @param c input chunk
 * @return false if the input chunk is a wall
 */
KosmX's avatar
KosmX committed
int isNotWall(chunk c){
    return c.isFood || (c.data.chars[0].bytes.c[0] == ' ' && c.data.chars[1].bytes.c[0] == ' ');
}

KosmX's avatar
KosmX committed
/**
 * @param c input chunk
 * @return is the chunk air (not wall and not food)
 */
KosmX's avatar
KosmX committed
int isAir(chunk c){
KosmX's avatar
KosmX committed
    return !c.isFood && isNotWall(c);
KosmX's avatar
KosmX committed
}

KosmX's avatar
KosmX committed
/**
 * after some round it will place a new food onto the map randomly.
 * 
 * @param map map
 * @param foodTick how long ago was the last food placed (pointer)
 * @param feedAmount how often (in ticks) sould be a new food placed
 * @param firstSnake snake, to avoid placing food into snake
 * @param scrDat food will be printed, screed data is needed for this
 */
KosmX's avatar
KosmX committed
void updateFood(Matrix *map, int *foodTick, int feedAmount, snakeChain *firstSnake, screenData *scrDat){
KosmX's avatar
KosmX committed
    if((*foodTick)++ > feedAmount){
        for(int i = 0; i < 128; i++){
            Pos pos;
            int isFree = 1;
            snakeChain *snake = firstSnake;
            pos.x = rand()%map->width;
            pos.y = rand()%map->height;

KosmX's avatar
KosmX committed
            if(isAir(map->matrix[pos.x][pos.y])){
                while(snake != NULL){
                    if(snake->pos.x == pos.x && snake->pos.y == pos.y){
                        isFree = false;
                        break;
                    }
                    snake = snake->next;
KosmX's avatar
KosmX committed
                }
KosmX's avatar
KosmX committed
                if(!isFree){
                    continue;
KosmX's avatar
KosmX committed
                }
KosmX's avatar
KosmX committed
                //pos is available
                {
                    *foodTick = 0;
KosmX's avatar
KosmX committed
                    
                    //chunk c;
KosmX's avatar
KosmX committed
                    map->matrix[pos.x][pos.y].isFood = 1;
                    map->matrix[pos.x][pos.y].data.FRand = rand();
KosmX's avatar
KosmX committed
                    //c.data.chars[0].bytes.c[0] = 'X';
                    //c.data.chars[1].bytes.c[0] = 'X';
                    
                    //printf(green); //foods are green
                    print(map->matrix[pos.x][pos.y], pos, scrDat, map->width, map->height);
                    //printf("\e[0m");
KosmX's avatar
KosmX committed
                    break;
KosmX's avatar
KosmX committed
                }
            }
        }
    }
}

//EOF fatal error, 1 game over
KosmX's avatar
KosmX committed

/**
 * step and check snake
 * 
 * 
 * @param map map
 * @param scrDat screenData
 * @param d current direction
 * @param head head of snek
 * @param canBite can sneak bite itself (boolean)
 * @return EOF if fatal error happened, 1 if the game is over, 0 instead.
 */
KosmX's avatar
KosmX committed
int updateSnake(Matrix *map, screenData *scrDat, Direction d, snakeChain *head, int canBite){
KosmX's avatar
KosmX committed
    Pos pos = head->pos;
    switch (d)
    {
    case UP:
        pos.y--;
KosmX's avatar
KosmX committed
        break;
    case DOWN:
        pos.y++;
KosmX's avatar
KosmX committed
        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;
        }
    }
    
KosmX's avatar
KosmX committed
    if(isNotWall(map->matrix[pos.x][pos.y])){
        int isFood = map->matrix[pos.x][pos.y].isFood;
KosmX's avatar
KosmX committed
        if(isFood){
            map->matrix[pos.x][pos.y].isFood = false;
            map->matrix[pos.x][pos.y].data.chars[0].bytes.c[0] = ' ';
            map->matrix[pos.x][pos.y].data.chars[1].bytes.c[0] = ' ';
        }
KosmX's avatar
KosmX committed
        snakeChain *snake = head;
        Pos tmp_pos1 = head->pos, tmp_pos2;
KosmX's avatar
KosmX committed
        Direction dir_tmp = head->dir, dir_tmp2;
        head->dir = d;
KosmX's avatar
KosmX committed
        head->pos = pos;
KosmX's avatar
KosmX committed
        while (snake->next != 0)
        {
KosmX's avatar
KosmX committed
            snake = snake->next;
KosmX's avatar
KosmX committed
            chunk c;
KosmX's avatar
KosmX committed
            c.isFood = 0;
KosmX's avatar
KosmX committed

            //if snek hit itself
KosmX's avatar
KosmX committed
            if(snake != head && snake->pos.x == head->pos.x && snake->pos.y == head->pos.y){
                if(snake->next != NULL){
                    if(canBite){
KosmX's avatar
KosmX committed
                        //if snek can bite its own tail, free that space and fill these places with   .
KosmX's avatar
KosmX committed
                        snakeChain *current = snake, *tmp_snek = snake->next;
KosmX's avatar
KosmX committed
                        current->next = NULL;
                        while (tmp_snek != NULL)
KosmX's avatar
KosmX committed
                        {
                            current = tmp_snek;
                            tmp_snek = tmp_snek->next;
KosmX's avatar
KosmX committed
                            c.data.chars[0].bytes.c[0] = ' ';
                            c.data.chars[1].bytes.c[0] = ' ';
                            print(c, current->pos, scrDat, map->width, map->height);
KosmX's avatar
KosmX committed
                            free(current);
                        }
                    }
                    else
                    {
                        return 1; //GAME OVER snake hit itself;
                    }
                }else
                {
                    isFood = false;
                }
                
                
            }
KosmX's avatar
KosmX committed
            //step position
KosmX's avatar
KosmX committed
            tmp_pos2 = snake->pos;
            snake->pos = tmp_pos1;
            tmp_pos1 = tmp_pos2;

KosmX's avatar
KosmX committed
            //step direction
            dir_tmp2 = snake->dir;
            snake->dir = dir_tmp;

            //render snek
KosmX's avatar
KosmX committed
            dir_tmp = dir_tmp2;
            
KosmX's avatar
KosmX committed
            
KosmX's avatar
KosmX committed
            c.data.chars[0].bytes.c[0] = '>';
            c.data.chars[1].bytes.c[0] = '<';            
KosmX's avatar
KosmX committed
            if(snake->pos.x != -1){
KosmX's avatar
KosmX committed
                printf(blue);
                print(c, snake->pos, scrDat, map->width, map->height); //TODO direction snake
KosmX's avatar
KosmX committed
            }

            //create a new segment
KosmX's avatar
KosmX committed
            if(snake->next == NULL && isFood){
                isFood = false;
KosmX's avatar
KosmX committed
                snake->next = malloc(sizeof(snakeChain));
KosmX's avatar
KosmX committed
                if(snake->next == NULL){
                    return EOF;
                }
                snake->next->next = NULL;
KosmX's avatar
KosmX committed
                snake->next->pos = tmp_pos1;
KosmX's avatar
KosmX committed
                snake->next->num = snake->num + 1;
KosmX's avatar
KosmX committed
            }
KosmX's avatar
KosmX committed

            #ifdef DEBUG
            printf("\e[0m"); //if debug active, update display after every segments
            #endif
KosmX's avatar
KosmX committed

        }
KosmX's avatar
KosmX committed

        //clear last segment form the screen
KosmX's avatar
KosmX committed
        if(tmp_pos2.x != -1){
            chunk c; 
KosmX's avatar
KosmX committed
            c.isFood = 0;
KosmX's avatar
KosmX committed
            c.data.chars[0].bytes.c[0] = ' ';
            c.data.chars[1].bytes.c[0] = ' ';
KosmX's avatar
KosmX committed
            print(c, tmp_pos2, scrDat, map->width, map->height); //set this to air.
        }
        chunk c;
KosmX's avatar
KosmX committed
        c.isFood = 0;
KosmX's avatar
KosmX committed
        c.data.chars[0].bytes.c[0] = '(';
        c.data.chars[1].bytes.c[0] = ')';
KosmX's avatar
KosmX committed
        printf(blue);
        print(c, head->pos, scrDat, map->width, map->height); //TODO direction snake
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
        //set color back to white
KosmX's avatar
KosmX committed
        printf("\e[0m");
KosmX's avatar
KosmX committed
    }
    else
    {
        return 1; //GAME OVER hit the wall;
    }
    return 0;
KosmX's avatar
KosmX committed
}
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
//----------texture loader(s)----------

/**
 * load food skins from file
 * 
 * @param f file
 * @param data data pointer to store the information
 * @param size size pointer for the data
 */
int load_food_skin(FILE *f, chunk **data, int *len){
    int size;
    fscanf(f, "%d", &size);
    *data = calloc(size, sizeof(chunk));
    if(data == 0){
        return EOF;
    }
    *len = size;
    for(int i = 0; i < size; i++){
        int c;
        while(c = fgetc(f), c != '\n'){
            if(c == EOF){
                return c;
            }
        }
        for(int ch = 0; ch < 2; ch++){
            c = fgetc(f);
            (*data)[i].data.chars[ch].bytes.c[0] = c;
            if(isUnicodeEncoding(0)){
                int n = checkUnicharLen(c);
                for (int p = 1; p < n; p++){
                    (*data)[i].data.chars[ch].bytes.c[p] = fgetc(f);
                }
            }
        }
    }
    return 0;
}

KosmX's avatar
KosmX committed
//------------config loader------------

KosmX's avatar
KosmX committed
/**
 * Loads game config file
 * 
 * @param tickSpeed game tickSpeed in miliseconds
 * @param repeatMap is the map repeated
 * @param feedAmount food spawn rate
 * @param canBite can snek bite itself
KosmX's avatar
KosmX committed
 * @param foodText food textures
 * @param foodLen food textures amount
KosmX's avatar
KosmX committed
 * 
 * @return EOF if error 0 instead
 */
KosmX's avatar
KosmX committed
int loadConfig(int *tickSpeed, int *repeatMap, int *feedAmount, int *canBite, chunk **foodText, int *foodLen){
KosmX's avatar
KosmX committed
    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);
        }
KosmX's avatar
KosmX committed
        else if(strncmp(name, "repeat_map", 10) == 0){
KosmX's avatar
KosmX committed
            fscanf(config, " %d", repeatMap);
        }
KosmX's avatar
KosmX committed
        else if(strncmp(name, "feed_amount", 12) == 0){
            fscanf(config, " %d", feedAmount);
        }
        else if(strncmp(name, "can_bite", 9) == 0){
            fscanf(config, " %d", canBite);
        }
KosmX's avatar
KosmX committed
        else if(strncmp(name, "food_text_file", 10) == 0){
            char fname[64] = {0}; //set it to zeros
            int tmp_bool = true;
            while(c = fgetc(config), isspace(c)){
                if(c == '\n'){
                    tmp_bool = false;
                    break;
                }
            }
            if(tmp_bool){
                int tmp_int = 0;
                do{
                    fname[tmp_int++] = c;
                    c = fgetc(config);
                }while(!isspace(c));
            }
            FILE *skin_file = fopen(fname, "rb");
            if(skin_file == 0){
                return 1;
            }
            if(load_food_skin(skin_file, foodText, foodLen)){
                fclose(skin_file);
                return 2;
            }
            fclose(skin_file);

        }
KosmX's avatar
KosmX committed
        else{
            printf("Unknown keyword: %s", name);
        }
    }
    return 0;
}



KosmX's avatar
KosmX committed
//------------LOOP METHODs--------------

//Tick the game, step the snake, collect and place food.
//check new direction
//update display
//update food state
//step snake
KosmX's avatar
KosmX committed
/**
 * ticks the game including everything
 * updates the control
 * updates the screen
 * updates the foods
 * steps snek
 * 
 * @param map map
 * @param scrDat screenData
 * @param snake snake's head
 * @param d direction
 * @param feedAmount food spawn rate
 * @param canBite can snake bite its own tail
 * @return 1 if game over
 */
KosmX's avatar
KosmX committed
int tick(Matrix *map, screenData *scrDat, snakeChain *snake, Direction *d, int feedAmount, int canBite){
KosmX's avatar
KosmX committed
    static int foodTick = 0;
    getControl(scrDat, d);
    if(scrDat->commands[0] != NONE){
        *d = scrDat->commands[0];
    }
KosmX's avatar
KosmX committed
    scrDat->commands[0] = scrDat->commands[1];
    scrDat->commands[1] = NONE;
KosmX's avatar
KosmX committed
    updateScreen(map, scrDat, snake, *d);
    if(*d == NONE){
KosmX's avatar
KosmX committed
        chunk c;
KosmX's avatar
KosmX committed
        c.isFood = false;
KosmX's avatar
KosmX committed
        c.data.chars[0].bytes.c[0] = '(';
        c.data.chars[1].bytes.c[0] = ')';
KosmX's avatar
KosmX committed
        printf(blue);
        print(c, snake->pos, scrDat, map->width, map->height);
        printf("\e[0m");
        return 0; // waiting for user input.
    }
KosmX's avatar
KosmX committed
    updateFood(map, &foodTick, feedAmount, snake, scrDat);
KosmX's avatar
KosmX committed
    if(updateSnake(map, scrDat, *d, snake, canBite)){
        *d = NONE;
KosmX's avatar
KosmX committed
        return 1; //game over 
KosmX's avatar
KosmX committed
    }
KosmX's avatar
KosmX committed
    //printf("\e[0;0H\n"); //Update the terminal (mostly on Unix based systems)
    fflush(stdout);
KosmX's avatar
KosmX committed
    return 0;
}
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
/**
 * prepares the game for loop and then starts the infinite loop
 * 
 * @param matrix map
 * @param tickspeed game tickspeed in milis
 * @param repeatMap does the map repeat itself
 * @param feedAmount food spawn rate
 * @param canBite can snake bite itself
KosmX's avatar
KosmX committed
 * @param foodText food textures in chunks array
 * @param foodTextLen food texture length
KosmX's avatar
KosmX committed
 * @return 0
 */
KosmX's avatar
KosmX committed
int loop(Matrix *matrix, int tickspeed, int repeatMap, int feedAmount, int canBite, chunk *foodText, int foodTextLen){
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
    Direction d = NONE; //Init with none
KosmX's avatar
a  
KosmX committed
    screenData scrDat;
KosmX's avatar
KosmX committed
    snakeChain snake, *chain;

    while(true){
        Pos p;
        p.x = rand()%matrix->width;
        p.y = rand()&matrix->height;
KosmX's avatar
KosmX committed
        if(isNotWall(matrix->matrix[p.x][p.y])){
KosmX's avatar
KosmX committed
            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;
KosmX's avatar
KosmX committed
        chain->next = NULL;
KosmX's avatar
KosmX committed
        chain->num = i-1;
KosmX's avatar
KosmX committed
    }

KosmX's avatar
KosmX committed
    scrDat.size.x = 0;
    scrDat.size.y = 0;
KosmX's avatar
KosmX committed
    scrDat.repeatMap = repeatMap;
KosmX's avatar
KosmX committed
    scrDat.commands[0] = NONE;
    scrDat.commands[1] = NONE;
KosmX's avatar
KosmX committed
    scrDat.foodTexture.text = foodText;
    scrDat.foodTexture.len = foodTextLen;
    scrDat.isXRepeat = false;
    scrDat.isYRepeat = false;
KosmX's avatar
a  
KosmX committed
    while(42){
KosmX's avatar
KosmX committed
        if(tick(matrix, &scrDat, &snake, &d, feedAmount, canBite)){
KosmX's avatar
KosmX committed
            break;
        }
KosmX's avatar
a  
KosmX committed
        unisleep(tickspeed); //Special sleep to work both in windows and unix environment
    }
KosmX's avatar
KosmX committed
    chain = snake.next;
    while (chain != NULL)
    {
        snakeChain *next = chain->next;
        free(chain);
        chain = next;
    }
KosmX's avatar
KosmX committed
    return 0;
}
KosmX's avatar
:D
KosmX committed

//------------TESTING METHODS-------------
KosmX's avatar
KosmX committed

/**
 * ONLY FOR DEBUG
 * print the map for debug
 * 
 * @param map map
 */
KosmX's avatar
:D
KosmX committed
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...
KosmX's avatar
KosmX committed
                printChar(map->matrix[x][y].data.chars[i]);
                //unisleep(1000);
KosmX's avatar
:D
KosmX committed
            }
            //printf("|");
        }
        printf("\n");
    }
}

KosmX's avatar
KosmX committed
/**
 * ONLY FOR DEBUG
 * reads coordinates from stdin and print this pos to the stdout
 * 
 * @param map map
 */
KosmX's avatar
:D
KosmX committed
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++){
KosmX's avatar
KosmX committed
                printf("\nvalue: %hx, %hx, %hx, %hx\n",
KosmX's avatar
KosmX committed
                (unsigned)map->matrix[x][y].data.chars[i].bytes.c[0],
                (unsigned)map->matrix[x][y].data.chars[i].bytes.c[1],
                (unsigned)map->matrix[x][y].data.chars[i].bytes.c[2],
                (unsigned)map->matrix[x][y].data.chars[i].bytes.c[3]);
KosmX's avatar
:D
KosmX committed
                printf("as character: \"");
KosmX's avatar
KosmX committed
                printChar(map->matrix[x][y].data.chars[i]);
KosmX's avatar
:D
KosmX committed
                printf("\"\n");
            }
        }
    }
}


//-------------CORE METHOD----------------

KosmX's avatar
KosmX committed
/**
 * The main method is respinsible for the debug or release mode start, technically the main is redirected here
 * initialize the game then start tle game
 * 
 * @param argc redirected argc
 * @param argv redirected argv
KosmX's avatar
KosmX committed
 * @return 2 if config error 0 is okay
KosmX's avatar
KosmX committed
 */
KosmX's avatar
:D
KosmX committed
int core(int argc, char const *argv[])
{
KosmX's avatar
KosmX committed
    if(argc == 1){
        printf("Usage: snake <map name> [<snek skin>]");
        return 0;
    }

KosmX's avatar
:D
KosmX committed
    FILE *f;
    Matrix map;
161400@bme.hu's avatar
161400@bme.hu committed

KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
    srand(time(0)); // bee a bit more random

KosmX's avatar
KosmX committed
    int tickspeed = 100, repeatMap = 0, feedAmount = 20, canBite = true; // if no config, default value
KosmX's avatar
KosmX committed

    //----load config----
KosmX's avatar
KosmX committed
    chunk *foodText = NULL;
    int foodLen = 0;
KosmX's avatar
KosmX committed

KosmX's avatar
KosmX committed
    if(loadConfig(&tickspeed, &repeatMap, &feedAmount, &canBite, &foodText, &foodLen)){
KosmX's avatar
KosmX committed
        printf("Error while loading config...");
    }

KosmX's avatar
KosmX committed
    if(foodText == NULL){
        printf("No food texture file were found in config.");
        return 2;
    }
    if(foodLen == 0){
        printf("No food texture error");
        free(foodText);
        return 2;
    }


161400@bme.hu's avatar
161400@bme.hu committed
    //----init tasks----

KosmX's avatar
KosmX committed
    if(isUnicodeEncoding(0)){
KosmX's avatar
KosmX committed
        #ifndef __linux__
KosmX's avatar
KosmX committed
        setlocale(LC_ALL, ".utf-8");
KosmX's avatar
KosmX committed
        #endif
KosmX's avatar
KosmX committed
    }
161400@bme.hu's avatar
161400@bme.hu committed

    //----import map----

KosmX's avatar
KosmX committed
    
    f = fopen(argv[1], "rb");
    if(f == NULL){
        printf("Map file not found: %s", argv[1]);