Nimbin2git.christianimmanuel.de / Games / Sokoban_in_C / main.c

Sokoban_in_C git · main

git clone https://git.christianimmanuel.de/games/Sokoban_in_C.gitwget https://git.christianimmanuel.de/games/Sokoban_in_C/archive/Sokoban_in_C.tar.gz
main.c 10 KB · 351 lines raw
#include <stdlib.h>
#include <stdio.h>

#ifdef _WIN32
#define WINDOWS
#endif

/* ------------------------------------------------------------ */

#ifdef WINDOWS

#include <windows.h>
#include <conio.h>

char get_keypress(){
    char c = getch();
    return c;
}

#else

#include <unistd.h>
#include <termios.h>

char get_keypress() {

    char buf = 0;
    struct termios old = {0};
    fflush(stdout);
    if (tcgetattr(0, &old) < 0)
        perror("tcsetattr()");
    old.c_lflag &= ~ICANON;
    old.c_lflag &= ~ECHO;
    old.c_cc[VMIN] = 1;
    old.c_cc[VTIME] = 0;
    if (tcsetattr(0, TCSANOW, &old) < 0)
        perror("tcsetattr ICANON");
    if (read(0, &buf, 1) < 0)
        perror("read()");
    old.c_lflag |= ICANON;
    old.c_lflag |= ECHO;
    if (tcsetattr(0, TCSADRAIN, &old) < 0)
        perror("tcsetattr ~ICANON");
    return buf;
}

#endif

/* ------------------------------------------------------------ */

#define MAX_LEVELS 100
#define MAX_BOARD_WIDTH 25
#define MAX_BOARD_HEIGHT 25

// a square is either empty, a wall, a box or the player
typedef enum {
    EMPTY, WALL, BOX, PLAYER
} square_t;

typedef struct {
    unsigned x;
    unsigned y;
} position_t;

/*
 * read a single level from a given file,
 * information is stored in board and target arrays,
 */
void read_board(FILE *fp,
                square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                unsigned target[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT]) {

    unsigned x = 0;
    unsigned y = 0;
    unsigned end_of_level = 0;

    while (!feof(fp) && !end_of_level) {

        switch (fgetc(fp)) {
            case '\n':
                // fill the rest of row with empty squares
                for (int i = x; i < MAX_BOARD_WIDTH; i++) {
                    board[i][y] = EMPTY;
                    target[i][y] = 0;
                }
                // move to the next line of the board
                x = 0;
                y++;
                break;
            case '#':
                board[x][y] = WALL;
                target[x][y] = 0;
                x++;
                break;
            case '$':
                board[x][y] = BOX;
                target[x][y] = 0;
                x++;
                break;
            case ' ':
                board[x][y] = EMPTY;
                target[x][y] = 0;
                x++;
                break;
            case '@':
                board[x][y] = PLAYER;
                target[x][y] = 0;
                x++;
                break;
            case '.':
                board[x][y] = EMPTY;
                target[x][y] = 1;
                x++;
                break;
            case ';':
                // skip all remaining characters of the line
                while (fgetc(fp) != '\n');
                end_of_level = 1;
                break;
        }
    }

}


/* ------------------------------------------------------------ */

/*
 * read all levels from the given file,
 * information is stored in boards and targets arrays,
 * total number of levels is stored in nr_levels
 */

void read_boards(char *filename,
                 square_t boards[MAX_LEVELS][MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                 unsigned targets[MAX_LEVELS][MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                 unsigned *nr_levels) {

    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Fatal Error: Could not open input file %s\n");
        exit(1);
    }

    for (*nr_levels = 0; *nr_levels < MAX_LEVELS && !feof(fp); (*nr_levels)++)
        read_board(fp, boards[*nr_levels], targets[*nr_levels]);

    fclose(fp);
}


/* ------------------------------------------------------------ */

unsigned move_left(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                   position_t *player_pos) {
    if ((player_pos->x >= 1) &&
        (board[player_pos->x - 1][player_pos->y] == EMPTY)) {

        board[player_pos->x][player_pos->y] = EMPTY;
        board[player_pos->x - 1][player_pos->y] = PLAYER;
        player_pos->x--;
        // result 1 = make move
        return 1;
    }
    // result 0 = no move is possible
    return 0;
}

unsigned move_right(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                    position_t *player_pos) {
    if ((player_pos->x <= MAX_BOARD_WIDTH - 2) &&
        (board[player_pos->x + 1][player_pos->y] == EMPTY)) {

        board[player_pos->x][player_pos->y] = EMPTY;
        board[player_pos->x + 1][player_pos->y] = PLAYER;
        player_pos->x++;
        // result 1 = make move
        return 1;
    }
    // result 0 = no move is possible
    return 0;
}


unsigned move_up(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                 position_t *player_pos) {
    if ((player_pos->y >= 1) &&
        (board[player_pos->x][player_pos->y - 1] == EMPTY)) {

        board[player_pos->x][player_pos->y] = EMPTY;
        board[player_pos->x][player_pos->y - 1] = PLAYER;
        player_pos->y--;
        // result 1 = make move
        return 1;
    }
    // result 0 = no move is possible
    return 0;
}

unsigned move_down(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                   position_t *player_pos) {
    if ((player_pos->y <= MAX_BOARD_HEIGHT - 2) &&
        (board[player_pos->x][player_pos->y + 1] == EMPTY)) {

        board[player_pos->x][player_pos->y] = EMPTY;
        board[player_pos->x][player_pos->y + 1] = PLAYER;
        player_pos->y++;
        // result 1 = make move
        return 1;
    }
    // result 0 = no move is possible
    return 0;
}


/*
 * draws the board
 */

void draw_board(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                unsigned target[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                unsigned current_level,
                unsigned nr_levels,
                unsigned nr_moves) {
    // first clear the screen
#ifdef WINDOWS
    system("cls");
#else
    system("clear");
#endif

    // top line: some general information
    printf("Level %i of %i / Moves: %i\n", current_level + 1, nr_levels, nr_moves);

    // draw every square of the board
    for (int j = 0; j < MAX_BOARD_HEIGHT; j++) {
        for (int i = 0; i < MAX_BOARD_WIDTH; i++) {
            switch (board[i][j]) {
                case EMPTY:
                    if (target[i][j]) printf("..");
                    else printf("  ");
                    break;
                case WALL:
                    printf("##");
                    break;
                case BOX:
                    if (target[i][j]) printf("()");
                    else printf("[]");
                    break;
                case PLAYER:
                    printf("><");
            }
        }
        printf("\n");
    }
}


/*
 * count the number of boxes in target squares,
 * if all boxes are in target squares, return 1 - otherwise 0
 */
unsigned all_boxes_on_target(square_t board[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT],
                             unsigned target[MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT]) {
    // TODO
    return 0;
}


int main(int argc, const char *argv[]) {

    // stores the boards of all leveles
    square_t boards[MAX_LEVELS][MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT];
    // stores the target information of all levels
    unsigned targets[MAX_LEVELS][MAX_BOARD_WIDTH][MAX_BOARD_HEIGHT];
    // total number of levels (in input file)
    unsigned nr_levels;

    // current level number
    unsigned current_level = MAX_LEVELS;
    // current position of player
    position_t player_pos;
    // current number of moves (in this level)
    unsigned nr_moves;
    // current number of boxes (in this level)
    unsigned nr_boxes;

    // read the boards of all levels from the given file
    read_boards("Simple.txt", boards, targets, &nr_levels);

    // read the start level
    do {
        printf("Select start level (1-%d)...", nr_levels);
        scanf("%u", &current_level);
    } while (current_level > nr_levels);

    // start the game with given level, then proceed to next level
    for (current_level -= 1; current_level < nr_levels; current_level++) {
        // extract player position and number of boxes from the current board
        nr_boxes = 0;
        for (unsigned i = 0; i < MAX_BOARD_WIDTH; i++)
            for (unsigned j = 0; j < MAX_BOARD_HEIGHT; j++) {
                // if player position is found, store it
                if (boards[current_level][i][j] == PLAYER) {
                    player_pos.x = i;
                    player_pos.y = j;
                }
                // count the number of boxes (on target)
                if (boards[current_level][i][j] == BOX) {
                    nr_boxes++;
                }
            }
        // initialize the number of moves for the current level
        nr_moves = 0;

        // for every move of the player, do the following
        do {
            // check if boxes are in target position,
            // if this is the case, current level is finished
            if (all_boxes_on_target(boards[current_level], targets[current_level]))
                break;
            // draw the board
            draw_board(boards[current_level], targets[current_level], current_level, nr_levels, nr_moves);

            // wait for keypress, then execute appropriate function
            switch (get_keypress()) {
                case 'a':
                    nr_moves += move_left(boards[current_level], &player_pos);
                    break;
                case 'd':
                    nr_moves += move_right(boards[current_level], &player_pos);
                    break;
                case 'w':
                    nr_moves += move_up(boards[current_level], &player_pos);
                    break;
                case 's':
                    nr_moves += move_down(boards[current_level], &player_pos);
                    break;
                case 'q':
                    exit(0);
            }
        } while (1);

        // loop exited by break: level completed
        draw_board(boards[current_level], targets[current_level], current_level, nr_levels, nr_moves);
        printf("Level %d completed! Press any key to continue...", current_level);
        get_keypress();
    }
    printf("All levels completed. Congratulation!");
}