首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

用 C 語言實作經典推箱子遊戲:從零打造文字版 Sokoban

     編輯:關於C語言

用 C 語言實作經典推箱子遊戲:從零打造文字版 Sokoban

推箱子(Sokoban)是一款經典的益智遊戲,玩家需操控角色在倉庫中將箱子推至指定目標位置。本文將帶領你從零開始,使用 C 語言在終端機環境下實作一個完整的文字版推箱子遊戲,包含地圖繪製、移動邏輯、勝利判定等核心機制。

遊戲核心架構設計

遊戲使用二維字元陣列 char a[11][20] 來儲存地圖資料,其中 # 代表牆壁、$ 代表目標位置、@ 代表箱子、^ 代表玩家、空白則為可通行路徑。透過 system("clear") 清除螢幕(Windows 環境請改用 system("cls")),配合迴圈重繪地圖,即可營造出流暢的動畫效果。

關鍵邏輯解析

移動與推箱判斷

玩家輸入 w/a/s/d 對應上下左右四個方向。移動邏輯分為兩種情況:

  • 前方為空地:直接移動玩家座標 (e,f)
  • 前方為箱子:需檢查箱子前方是否為牆壁或另一箱子,若非則同時移動玩家與箱子座標

程式中分別以 (m,n)、(x,y) 追蹤兩個箱子位置,推箱時需同步更新地圖陣列與座標變數。

勝利條件判定

當兩個箱子分別到達兩個目標位置 (2,8) 與 (2,10) 時,遊戲判定勝利並跳出主迴圈。輸入 q 可隨時退出遊戲。

完整原始碼

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char sr; // 儲存使用者輸入指令
    
    // 繪製地圖:#=牆壁 $=目標 @=箱子 ^=玩家
    char a[11][20] = {
        "###################",
        "###################",
        "### $ $ ###",
        "### ##### ###",
        "### ###",
        "#### #### ### ####",
        "### # @ # ###",
        "### # @ # ###",
        "### ^ ###",
        "###################",
        "###################"
    };
    
    // 定義箱子、玩家與目標座標
    int i = 1, e = 8, f = 9, m = 7, n = 9, x = 6, y = 9;
    
    while (i) {
        system("clear"); // Windows 請改用 system("cls")
        printf("歡迎使用推箱子\n^ 代表你,任務是把 @ 推到 $ 的位置,加油!\n");
        printf("操作說明:w=上、s=下、a=左、d=右、q=退出\n");
        
        // 判斷勝利條件
        if (m == 2 && n == 8) {
            if (x == 2 && y == 10) {
                printf("恭喜你挑戰成功!\n");
                break;
            }
        }
        if (m == 2 && n == 10) {
            if (x == 2 && y == 8) {
                printf("恭喜你挑戰成功!\n");
                break;
            }
        }
        
        // 列印地圖
        for (int b = 0; b < 11; b++) {
            for (int c = 0; c < 20; c++) {
                printf("%c", a[b][c]);
            }
            printf("\n");
        }
        
        // 清除緩衝區並讀取輸入
        fpurge(stdin);
        scanf("%c", &sr);
        
        // 方向判斷與移動邏輯
        switch (sr) {
            case 'w': case 'W':
                if (a[e-1][f] == ' ') {
                    a[e-1][f] = '^'; a[e][f] = ' '; e -= 1;
                } else if (a[e-1][f] == '@' && a[e-2][f] != '#' && a[e-2][f] != '@') {
                    if (e - m == 1 && f == n) {
                        m -= 1; a[m][n] = '@'; a[e-1][f] = '^'; a[e][f] = ' '; e -= 1;
                    } else if (e - x == 1 && f == y) {
                        x -= 1; a[x][y] = '@'; a[e-1][f] = '^'; a[e][f] = ' '; e -= 1;
                    }
                }
                break;
            case 's': case 'S':
                if (a[e+1][f] == ' ') {
                    a[e+1][f] = '^'; a[e][f] = ' '; e += 1;
                } else if (a[e+1][f] == '@' && a[e+2][f] != '#' && a[e+2][f] != '@') {
                    if (m - e == 1 && f == n) {
                        m += 1; a[m][n] = '@'; a[e+1][f] = '^'; a[e][f] = ' '; e += 1;
                    } else if (x - e == 1 && y == f) {
                        x += 1; a[x][y] = '@'; a[e+1][f] = '^'; a[e][f] = ' '; e += 1;
                    }
                }
                break;
            case 'a': case 'A':
                if (a[e][f-1] == ' ') {
                    a[e][f-1] = '^';
                    if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                    else a[e][f] = ' ';
                    f -= 1;
                } else if (a[e][f-1] == '$') {
                    a[e][f-1] = '^';
                    if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                    else a[e][f] = ' ';
                    f -= 1;
                } else if (a[e][f-1] == '@' && a[e][f-2] != '#' && a[e][f-2] != '@') {
                    if (e == m && f - n == 1) {
                        n -= 1; a[m][n] = '@'; a[e][f-1] = '^';
                        if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                        else a[e][f] = ' '; f -= 1;
                    } else if (e == x && f - y == 1) {
                        y -= 1; a[x][y] = '@'; a[e][f-1] = '^';
                        if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                        else a[e][f] = ' '; f -= 1;
                    }
                }
                break;
            case 'd': case 'D':
                if (a[e][f+1] == ' ') {
                    a[e][f+1] = '^';
                    if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                    else a[e][f] = ' ';
                    f += 1;
                } else if (a[e][f+1] == '$') {
                    a[e][f+1] = '^';
                    if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                    else a[e][f] = ' ';
                    f += 1;
                } else if (a[e][f+1] == '@' && a[e][f+2] != '#' && a[e][f+2] != '@') {
                    if (e == m && n - f == 1) {
                        n += 1; a[m][n] = '@'; a[e][f+1] = '^';
                        if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                        else a[e][f] = ' '; f += 1;
                    } else if (e == x && y - f == 1) {
                        y += 1; a[x][y] = '@'; a[e][f+1] = '^';
                        if ((e == 2 && f == 8) || (e == 2 && f == 10)) a[e][f] = '$';
                        else a[e][f] = ' '; f += 1;
                    }
                }
                break;
            case 'q': case 'Q':
                i = 0;
                break;
        }
    }
    return 0;
}

進階優化方向

這個基礎版本可進一步擴展:支援多關卡地圖載入、加入步數計數與最佳紀錄、實作撤銷功能、優化輸入處理(如使用 getch() 免按 Enter)、加入顏色輸出提升視覺體驗。對於初學者而言,這個專案完美展示了陣列操作、迴圈控制、條件判斷與即時互動的綜合應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved