#define BOARD_SIZE 9
#define COLOR_MAX 7
char chesscolor[BOARD_SIZE][BOARD_SIZE]; // 0=空格, 1~7=顏色
char chessboard[BOARD_SIZE][BOARD_SIZE]; // 0=有棋子, 1=可通行空格
long grade = 0; // 分數
雙陣列分離「顏色屬性」與「連通性狀態」,便於路徑搜尋與消除判斷獨立演化。
boardfull()int boardfull(void) {
for (int i = 0; i < 9; ++i)
for (int t = 0; t < 9; ++t)
if (chessboard[i][t] == 1) return 0; // 仍有空格
return 1;
}
O(81) 掃描,極輕量。
buildchess()void buildchess(void) {
int x, y;
do {
x = rand() % 9;
y = rand() % 9;
} while (chesscolor[y][x] != 0); // 找空格
chesscolor[y][x] = rand() % 7 + 1;
chessboard[y][x] = 0;
delchess(y, x); // 立即檢查是否形成連線(極罕見)
}
注意:rand()%9 有模數偏差,嚴謹專案應用 arc4random_uniform(9) 或自製拒絕採樣。
canmove() — DFS 遞迴int canmove(int x, int y, int tx, int ty) {
if (x == tx && y == ty) return 1;
chessboard[x][y] = 0; // 標記已訪問
// 四方向嘗試
if (x < 8 && chessboard[x+1][y] && canmove(x+1, y, tx, ty)) { chessboard[x][y]=1; return 1; }
if (x > 0 && chessboard[x-1][y] && canmove(x-1, y, tx, ty)) { chessboard[x][y]=1; return 1; }
if (y < 8 && chessboard[x][y+1] && canmove(x, y+1, tx, ty)) { chessboard[x][y]=1; return 1; }
if (y > 0 && chessboard[x][y-1] && canmove(x, y-1, tx, ty)) { chessboard[x][y]=1; return 1; }
chessboard[x][y] = 1; // 回溯恢復
return 0;
}
利用 chessboard 做訪問標記(0=已訪/不可通行,1=可通行),避免額外 visited 陣列。9×9 格最大遞迴深度 81,堆疊安全。
delchess() — 四方向掃描對新落子座標 (x,y),分別向雙向掃描四條線(垂直、水平、主對角、副對角),統計同色連續數 sum。若 5(含自身共 5 顆以上),將該線上所有同色棋子清空(chesscolor=0, chessboard=1),返回 1 觸發加分。
關鍵細節:掃描條件 chesscolor[x][y] != chesscolor[i][j] 以首顆顏色為基準,確保只消除同色連線。
movechess() — 滑鼠事件迴圈scare_mouse()/unscare_mouse():繪圖前隱藏滑鼠游標,防止殘影(DOS 圖形庫慣用技巧)。(mouse-20)/40,邊界檢查 <=20 || >=380。canmove 後交換 chessboard/chesscolor,檢查消除。| 函式 | 功能 | 關鍵參數 |
|---|---|---|
drawboard() | 繪製網格線 + 所有棋子圓形 | hline/vline 畫線, circlefill 實心圓 |
circlefill(x,y,r,color) | 畫棋子 | 中心 (col*40+40, row*40+40), 半徑 12 |
rect() | 選中棋子紅框 | 左上/右下座標 |
neo_printf() | 顯示分數 | 座標 (0,0) 字串格式 |
main()int main(void) {
neo_init(); // 圖形庫初始化
set_video_mode(640,480,8,0,0); // 640×480 256 色
install_keyboard(); _install_timer(); change_timer(500);
install_mouse(); srand(100);
memset(chessboard, 1, sizeof(chessboard)); // 全部標記為空格
for (int i=0; i<3; ++i) buildchess(); // 初始 3 顆
while (!boardfull()) {
movechess(); // 玩家移動
for (int i=0; i<3; ++i) // 移動後生成 3 顆
if (!boardfull()) buildchess();
}
// 遊戲結束顯示分數
scare_mouse(); drawboard();
neo_printf(0,0, "遊戲結束,最終得分:%ld", grade);
_getch(); return 0;
}
| 原組件 | 現代替代 | 遷移要點 |
|---|---|---|
| neo.h / DOS 圖形 | SDL2 / SFML / Raylib | 將繪圖呼叫封裝為 Renderer 介面 |
滑鼠輪詢 get_click_info() | 事件迴圈 SDL_PollEvent | 按鍵按下/釋放分離邏輯 |
rand() | std::mt19937 / random_device | 均勻分布 uniform_int_distribution(0,8) |
| 全域變數 | GameState 結構體 + 單例/依賴注入 | 便於單元測試、存檔/讀檔 |
| 遞迴 DFS | BFS 隊列 / A* 启發式 | 大棋盤時避免堆疊溢位 |
chesscolor 快照(81 bytes),支援多步回溯。此程式雖基於 DOS 時代圖形庫,但完整展示了:狀態分離、DFS 路徑搜尋、多方向掃描消除、事件驅動主迴圈 等遊戲核心架構。代碼結構清晰,模組化程度高,是從「控制台小程式」邁向「圖形互動遊戲」的優秀教學案例。建議讀者嘗試移植至現代框架,並在保持核心邏輯不變的前提下重構架構,體會「業務邏輯與渲染分離」的工程智慧。