Terminal-Based Sokoban Implementation in C for Linux

Custom Input Handling Implemantation

#ifndef KEY_READER_H
#define KEY_READER_H

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

int read_key() {
    struct termios original_settings, modified_settings;
    tcgetattr(STDIN_FILENO, &original_settings);
    modified_settings = original_settings;
    
    modified_settings.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &modified_settings);
    
    int key_value = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &original_settings);
    return key_value;
}

#endif

Game Representation and Initialization

#define EMPTY 0
#define PLAYER 1
#define WALL 2
#define BOX 3
#define TARGET 4
#define PLAYER_ON_TARGET 5
#define BOX_ON_TARGET 7

char game_grid[8][8] = {
    {0,0,2,2,2,2,0,0},
    {0,0,2,4,4,2,0,0},
    {0,2,2,0,4,2,2,0},
    {0,2,0,0,3,4,2,0},
    {2,2,0,3,0,0,2,2},
    {2,0,0,2,3,3,0,2},
    {2,0,0,1,0,0,0,2},
    {2,2,2,2,2,2,2,2}
};

int player_row = 6, player_col = 3;
int move_count = 0;

Game Rendering and Input Processing

while(1) {
    system("clear");
    int completed_targets = 0;

    for(int i = 0; i < 8; i++) {
        for(int j = 0; j < 8; j++) {
            switch(game_grid[i][j]) {
                case EMPTY: printf("  "); break;
                case PLAYER: printf("\u25B2 "); break;
                case WALL: printf("\u2588\u2588"); break;
                case BOX: printf("\u25A0 "); break;
                case TARGET: printf("\u25CB "); break;
                case PLAYER_ON_TARGET: printf("\u25B3 "); break;
                case BOX_ON_TARGET: printf("\u25A1 "); completed_targets++; break;
            }
        }
        printf("\n");
    }

    if(completed_targets == 4) {
        printf("Game completed in %d moves\n", move_count);
        return 0;
    }

    int input = read_key();
    int next_row = player_row;
    int next_col = player_col;
    
    switch(input) {
        case 183: next_row--; break; // Up
        case 184: next_row++; break; // Down
        case 185: next_col++; break; // Right
        case 186: next_col--; break; // Left
    }

    if(game_grid[next_row][next_col] == EMPTY || 
       game_grid[next_row][next_col] == TARGET) {
        game_grid[next_row][next_col] += PLAYER;
        game_grid[player_row][player_col] -= PLAYER;
        player_row = next_row;
        player_col = next_col;
        move_count++;
    }
    else if(game_grid[next_row][next_col] == BOX || 
            game_grid[next_row][next_col] == BOX_ON_TARGET) {
        int push_row = next_row + (next_row - player_row);
        int push_col = next_col + (next_col - player_col);
        
        if(game_grid[push_row][push_col] == EMPTY || 
           game_grid[push_row][push_col] == TARGET) {
            game_grid[push_row][push_col] += BOX;
            game_grid[next_row][next_col] -= BOX - PLAYER;
            game_grid[player_row][player_col] -= PLAYER;
            player_row = next_row;
            player_col = next_col;
            move_count++;
        }
    }
}

Tags: c programming Linux Terminal Applications game development sokoban

Posted on Mon, 02 Nov 2026 16:32:27 +0000 by jawapro