91网首页-91网页版-91网在线观看-91网站免费观看-91网站永久视频-91网站在线播放

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

中國(guó)象棋小游戲(C版)

freeflydom
2025年6月2日 9:25 本文熱度 297

! 此文僅展示此游戲的最簡(jiǎn)單版本,可以實(shí)現(xiàn)中國(guó)象棋雙人對(duì)戰(zhàn)的基本功能。更多功能體驗(yàn)可訪問(wèn)上方鏈接。

說(shuō)明:

  • #include<graphics.h> 一個(gè)在 C/C++ 中用于圖形編程的頭文件,主要用于創(chuàng)建和操作圖形界面。具有繪制圖形、設(shè)置顏色、鼠標(biāo)和鍵盤時(shí)間處理等功能。
  • #include<conio.h> 提供了對(duì)控制臺(tái)輸入/輸出的簡(jiǎn)單操作,如字符讀取和屏幕刷新。
  • #include<windows.h> 包含了 Windows API 的各種函數(shù)和數(shù)據(jù)結(jié)構(gòu)定義,允許程序直接調(diào)用 Windows 操作系統(tǒng)的功能。可用于窗口創(chuàng)建與管理、進(jìn)程和線程操作、文件操作、系統(tǒng)信息獲取等。
  • #include<mmsystem.h> 提供了多媒體功能,包括音頻和定時(shí)器功能。同時(shí)需要鏈接winmm.lib庫(kù),通過(guò)#pragma comment(lib,"winmm.lib")實(shí)現(xiàn)

整體思路

  1. 創(chuàng)建圖形窗口,繪制中國(guó)象棋棋局圖案
  2. 定義棋子,并在棋局上繪制初始化棋子
  3. 實(shí)現(xiàn)游戲控制功能。即可通過(guò)鼠標(biāo)操作實(shí)現(xiàn)棋子的移動(dòng)以及紅黑雙方的交換操作
  4. 添加棋子的走法規(guī)則,將軍的判定以及勝負(fù)判定
  5. 添加背景音樂(lè)、走棋音效等
  6. 添加紅黑雙方計(jì)時(shí)功能
  7. 打包軟件

實(shí)現(xiàn)過(guò)程

  1. 創(chuàng)建圖形窗口,繪制中國(guó)象棋棋局圖案。

    我們先看此步驟完成后的結(jié)果。

    只要對(duì)graphics.h有所了解,創(chuàng)建圖形窗口并不困難。

    我們首先在主函數(shù)中直接初始化圖形窗口。

    #include<stdio.h>
    #include<graphics.h>
    int main(){
    	initgraph(800,800);
    }
    

    接下來(lái)我們需要定義一個(gè)繪制游戲的函數(shù)。首先我們可以定義一個(gè)行數(shù)、列數(shù)、間隔以及棋盤格子大小

    #define INTERVAL 50 // 定義間隔
    #define CHESS_GRID_SIZE 70 // 定義棋盤格子大小
    #define ROW 10 // 定義行數(shù)
    #define COL 9 // 定義列數(shù)
    

    接下來(lái)繪制棋局的格子

    void GameDraw() {
    	setbkcolor(RGB(252, 215, 162));	// 設(shè)置背景色
    	cleardevice();	// 清屏
    	setlinecolor(BLACK); // 設(shè)置線條顏色
    	setlinestyle(PS_SOLID, 2); // 設(shè)置線條樣式
    	setfillcolor(RGB(252, 215, 162));
    	//繪制外邊框
    	fillrectangle(INTERVAL - 5, INTERVAL - 5, CHESS_GRID_SIZE * 8 + INTERVAL + 5, CHESS_GRID_SIZE * 9 + INTERVAL + 5);
    	// 繪制棋盤
    	for (int i = 0;i < 10;i++) {
    		//畫(huà)橫線
    		line(INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, CHESS_GRID_SIZE * 8 + INTERVAL, i * CHESS_GRID_SIZE + INTERVAL);
    		//畫(huà)豎線
    		if (i < 9) {
    			line(i * CHESS_GRID_SIZE + INTERVAL, INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
    		}
    	}
    }
    

    接著顯示“楚河漢界”文字

    void GameDraw(){
    	//顯示楚河,漢界
    	fillrectangle(INTERVAL, 4 * CHESS_GRID_SIZE + INTERVAL, 8 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL);
    	//顯示文字
    	settextcolor(BLACK);
    	settextstyle(50, 0, "楷體");
    	char river[25] = "楚 河        漢 界";
    	//讓文字居中
    	int twidth = textwidth(river);
    	int theight = textheight(river);
    	twidth = (8 * CHESS_GRID_SIZE - twidth) / 2;
    	theight = (CHESS_GRID_SIZE - theight) / 2;
    	outtextxy(INTERVAL + twidth, 4 * CHESS_GRID_SIZE + theight + INTERVAL, river);
    }
    

    最后畫(huà)米字完成第一步。

    void GameDraw(){
    	//畫(huà)米字
    	line(3 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
    	line(5 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
    	line(3 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
    	line(5 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);  
    }
    
  2. 定義棋子,并在棋局上繪制初始化棋子

    我們先看此步驟完成后的結(jié)果。

根據(jù)中國(guó)象棋游戲規(guī)則,棋子分為紅黑雙方,不同棋子有不同的走法和不同的過(guò)河標(biāo)準(zhǔn)。

于是我們首先需要定義紅黑雙方的棋子名稱,這里使用兩個(gè)指向常量的指針數(shù)組分別代表紅方棋子和黑方棋子。(用指針數(shù)組存儲(chǔ)每個(gè)棋子名稱字符串的起始地址,且是只讀模式。更靈活高效且易于修改和維護(hù))

const char* redChess[7] = { "車","馬","相","仕","帥","炮","兵" };	// 紅方棋子
const char* blackChess[7] = { "車","馬","象","士","將","砲","卒" };	// 黑方棋子

每個(gè)棋子的位置由橫縱坐標(biāo)確定,且每個(gè)棋子都有名稱、坐標(biāo)、紅黑方、是否過(guò)河等屬性。于是我們定義一個(gè)棋子結(jié)構(gòu)體如下:

//定義棋子結(jié)構(gòu)體
struct Chess {
	char name[4];	// 棋子名稱一個(gè)漢字
	int x;	// 棋子x坐標(biāo)
	int y;	// 棋子y坐標(biāo)
	char type;	// 棋子類型(紅方還是黑方)
	bool flag;	// 棋子是否過(guò)河;
}map[ROW][COL];

接下來(lái)就需要對(duì)結(jié)構(gòu)體進(jìn)行初始化,在相應(yīng)的位置放入相應(yīng)的棋子。

//游戲初始化
void GameInit() {
	//遍歷二維數(shù)組
	for (int i = 0;i < ROW;i++) {
		int temp = 0, temp1 = 0, temp2 = 1;
		for (int k = 0;k < COL;k++) {
			char chessname[4] = ""; // 定義棋子名稱
			char mcolor = 'B';	// 定義棋子顏色黑色
			if (i <= 4) {	// 黑棋初始化
				if (i == 0) {	//第一行棋子初始化
					if (temp <= 4) temp++;
					else {
						temp1 = 4 - temp2;
						temp2++;
					}
					sprintf(chessname, "%s", blackChess[temp1]);
					temp1++;
				}
				//設(shè)置砲
				if (i == 2 && (k == 1 || k == 7)) {
					strcpy(chessname, blackChess[5]);
				}
				//設(shè)置卒
				if (i == 3 && (k % 2 == 0)) {
					strcpy(chessname, blackChess[6]);
				}
			}
			else {
				mcolor = 'R';	// 定義棋子顏色紅色
				if (i == 9) {	//最后一行棋子初始化
					if (temp <= 4) temp++;
					else {
						temp1 = 4 - temp2;
						temp2++;
					}
					sprintf(chessname, "%s", redChess[temp1]);
					temp1++;
				}
				//設(shè)置炮
				if (i == 7 && (k == 1 || k == 7)) {
					strcpy(chessname, redChess[5]);
				}
				//設(shè)置兵
				if (i == 6 && (k % 2 == 0)) {
					strcpy(chessname, redChess[6]);
				}
			}
			map[i][k].type = mcolor;
			strcpy(map[i][k].name, chessname);
			map[i][k].flag = false;
			map[i][k].x = k * CHESS_GRID_SIZE + INTERVAL;
			map[i][k].y = i * CHESS_GRID_SIZE + INTERVAL;
		}
	}
}

最后,我們?cè)诶L制圖形函數(shù)中繼續(xù)添加關(guān)于繪制棋子的代碼,同時(shí)這里以圓圈代表一個(gè)棋子。

void DameDraw(){
	//繪制棋子
	settextstyle(40, 0, "楷體");  // 設(shè)置字體大小
	for (int i = 0;i < ROW;i++) {
		for (int k = 0;k < COL;k++) {
			if (strcmp(map[i][k].name, "") != 0) {
				//繪制棋子
				if (map[i][k].type == 'B') {
					settextcolor(BLACK);
					setlinecolor(BLACK);
				}
				else {
					settextcolor(RED);
					setlinecolor(RED);
				}
				fillcircle(map[i][k].x, map[i][k].y, 30);
				outtextxy(map[i][k].x - 20, map[i][k].y - 20, map[i][k].name);
			}
		}
	}   
}
  1. 實(shí)現(xiàn)游戲控制功能。即可通過(guò)鼠標(biāo)操作實(shí)現(xiàn)棋子的移動(dòng)以及紅黑雙方的交換操作。

    我們先看此步驟完成后的結(jié)果。

    在實(shí)現(xiàn)此功能前我們需要了解#include<windows.h>頭文件。

    • MOUSEMSG:是該頭文件下的一個(gè)結(jié)構(gòu)體,有以下成員
    成員變量類型描述
    uMsguint鼠標(biāo)消息類型,例如 WM_MOUSEMOVE(鼠標(biāo)移動(dòng))、WM_LBUTTONDOWN(左按下鍵)、WM_RBUTTONDOWN(右鍵按下)等。
    xint鼠標(biāo)事件發(fā)生時(shí)的 X 坐標(biāo)。
    yint鼠標(biāo)事件發(fā)生時(shí)的 Y 坐標(biāo)。
    timeuint鼠標(biāo)事件發(fā)生時(shí)的時(shí)間戳。
    dwExtraInfouint額外信息,通常用于區(qū)分相同消息的不同實(shí)例。

    我們首先做出如下定義:

    POINT begin = { -1,-1 }, end = { -1,-1 };	// 記錄前后兩次點(diǎn)擊的坐標(biāo)
    MOUSEMSG msg;
    bool isRedTurn = true; // 紅方先手
    

    接下來(lái)實(shí)現(xiàn)游戲控制功能。

    首先是檢測(cè)鼠標(biāo)的點(diǎn)擊

    if (MouseHit) {                 // 檢測(cè)是否有鼠標(biāo)事件
        msg = GetMouseMsg();        // 獲取鼠標(biāo)消息
        if (msg.uMsg == WM_LBUTTONDOWN) {  // 判斷是否為左鍵按下事件
            // ...后續(xù)處理...
        }
    }
    

    再轉(zhuǎn)換鼠標(biāo)坐標(biāo)為棋盤坐標(biāo)以及檢查點(diǎn)擊位置是否合法

    int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
    int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
    if (k < 0 || k >= COL || i < 0 || i >= ROW) return; // 超出棋盤范圍則退出
    

    接下來(lái)是第一次點(diǎn)擊選擇棋子

    if (begin.x == -1) {  // 首次點(diǎn)擊,選擇起點(diǎn)
        // 必須選擇己方棋子
        if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
            begin.x = k;
            begin.y = i;
        }
    }
    

    最后是第二次點(diǎn)擊移動(dòng)棋子

    else {  // 第二次點(diǎn)擊,選擇終點(diǎn)
        end.x = k;
        end.y = i;
        // 檢查目標(biāo)位置是否為空(未完成邏輯)
        int flagg = 0;
        if (strcmp(map[end.y][end.x].name, "") == 0)  flagg++;
        // 移動(dòng)棋子到目標(biāo)位置
        strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
        map[end.y][end.x].type = map[begin.y][begin.x].type;
        map[end.y][end.x].flag = map[begin.y][begin.x].flag;
        // 清空起點(diǎn)位置
        strcpy(map[begin.y][begin.x].name, "");
        // 切換回合
        isRedTurn = !isRedTurn;
        // 重置起點(diǎn)
        begin.x = -1;
    }
    

    完整的函數(shù)如下:

    void GameControl() {
    	if (MouseHit {
    		msg = GetMouseMsg();
    		if (msg.uMsg == WM_LBUTTONDOWN) {
    			//轉(zhuǎn)換為棋盤坐標(biāo)
    			int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
    			int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
    			//判斷是否在棋盤內(nèi)
    			if (k < 0 || k >= COL || i < 0 || i >= ROW) return;
    			if (begin.x == -1) {	//第一次點(diǎn)擊
    				//必須選擇己方棋子
    				if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
    					begin.x = k;
    					begin.y = i;
    				}
    			}else {	//第二次點(diǎn)擊
    				end.x = k;
    				end.y = i;
    				//執(zhí)行移動(dòng)
    				int flagg = 0;
    				if (strcmp(map[end.y][end.x].name, "") == 0)  flagg++;
    				//移動(dòng)棋子
    				strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
    				map[end.y][end.x].type = map[begin.y][begin.x].type;
    				map[end.y][end.x].flag = map[begin.y][begin.x].flag;
    				//清空原位置
    				strcpy(map[begin.y][begin.x].name, "");
    				//交換走棋方
    				isRedTurn = !isRedTurn;
    				begin.x = -1;	//重置選擇
    			}
    		}
    	}
    }
    

    最后,我們繼續(xù)在繪制函數(shù)中添加對(duì)棋子的選中效果

    void GameDraw(){
        //繪制選中效果
        if (i == begin.y && k == begin.x) {
            setlinecolor(BLUE);
            circle(map[i][k].x, map[i][k].y, 30);
            circle(map[i][k].x, map[i][k].y, 32);
        }    
    }
    
  2. 添加棋子的走法規(guī)則,將軍的判定以及勝負(fù)判定

    這一步是整個(gè)象棋游戲的關(guān)鍵步驟。我們需要定義一個(gè)檢驗(yàn)函數(shù),對(duì)每一個(gè)棋子進(jìn)行走法檢驗(yàn)。

    首先定義該檢驗(yàn)函數(shù):

    bool CheckMove(int fromI, int fromK, int toI, int toK) {	}
    
    • 第一步需要規(guī)定禁止吃己方棋子:

      這個(gè)不能實(shí)現(xiàn),只需要當(dāng)判斷目標(biāo)位置存在棋子且該棋子的陣營(yíng)屬性和移動(dòng)的棋子相同,則禁止移動(dòng)即可。

      struct Chess fromChess = map[fromI][fromK];
      struct Chess toChess = map[toI][toK];
      if (toChess.type == fromChess.type && strcmp(toChess.name, "") != 0)
          return false;
      
    • 第二步檢驗(yàn)車的走法

      • 車的走法就是必須橫向或縱向移動(dòng)(fromI == toI 或 fromK == toK
      • 路徑上所經(jīng)過(guò)的格子都必須為空即可。
      if (strcmp(fromChess.name, "車") == 0 || strcmp(fromChess.name, "車") == 0) {
          // 必須直線移動(dòng)
          if (fromI != toI && fromK != toK) return false;
          // 計(jì)算步長(zhǎng)和距離
          int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
          int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
          // 檢查路徑是否被阻擋
          for (int i = 1; i < distance; i++) {
              int x = (fromI == toI) ? fromI : fromI + step * i;
              int y = (fromI == toI) ? fromK + step * i : fromK;
              if (strcmp(map[x][y].name, "") != 0) return false;
          }
          return true;
      }
      
    • 第三步檢驗(yàn)馬的走法

      • 馬的移動(dòng)方式是日字格(橫向1格,縱向2格,或橫向2格,縱向1格)。
      • 判斷其移動(dòng)是否存在蹩馬腳的情況(即相鄰格子必須為空)
      else if (strcmp(fromChess.name, "馬") == 0 || strcmp(fromChess.name, "馬") == 0) {
          int dx = abs(toK - fromK);
          int dy = abs(toI - fromI);
          // 必須走日字
          if (!((dx == 1 && dy == 2) || (dx == 2 && dy == 1))) return false;
          // 檢查蹩馬腿
          int blockX = fromI + (toI - fromI) / 2;
          int blockY = fromK + (toK - fromK) / 2;
          if (strcmp(map[blockX][blockY].name, "") != 0) return false;
          return true;
      }
      
    • 第四步檢驗(yàn)象的走法

      • 象的移動(dòng)是田字格(橫向和縱向均移動(dòng)2格)
      • 田字中間必須為空。
      • 象無(wú)法過(guò)河。
      else if (strcmp(fromChess.name, "相") == 0 || strcmp(fromChess.name, "象") == 0) {
          int dx = abs(toK - fromK);
          int dy = abs(toI - fromI);
          // 必須走田字
          if (dx != 2 || dy != 2) return false;
          // 檢查田字中心是否被阻擋
          int centerX = (fromI + toI) / 2;
          int centerY = (fromK + toK) / 2;
          if (strcmp(map[centerX][centerY].name, "") != 0) return false;
          // 不能過(guò)河
          if (fromChess.type == 'B' && toI > 4) return false; // 黑象不能過(guò)楚河
          if (fromChess.type == 'R' && toI < 5) return false; // 紅象不能過(guò)漢界
          return true;
      }
      
    • 第五步檢驗(yàn)士的走法

      • 士在己方九宮格內(nèi)移動(dòng)
      • 每次只能斜向移動(dòng)一格(橫向和縱向均移動(dòng)1格)
      else if (strcmp(fromChess.name, "仕") == 0 || strcmp(fromChess.name, "士") == 0) {
          // 九宮格范圍檢查
          if (fromChess.type == 'B') {
              if (toI > 2 || toK < 3 || toK > 5) return false; // 黑方九宮格
          } else {
              if (toI < 7 || toK < 3 || toK > 5) return false; // 紅方九宮格
          }
          // 斜向移動(dòng)一格
          return (abs(toK - fromK) == 1 && abs(toI - fromI) == 1);
      }
      
    • 第六步檢驗(yàn)將的走法

      • 只能在己方九宮格內(nèi)移動(dòng)
      • 每次只能橫向或縱向移動(dòng)一格
      else if (strcmp(fromChess.name, "帥") == 0 || strcmp(fromChess.name, "將") == 0) {
          // 九宮格范圍檢查
          if (fromChess.type == 'B') {
              if (toI > 2 || toK < 3 || toK > 5) return false;
          } else {
              if (toI < 7 || toK < 3 || toK > 5) return false;
          }
          // 直線移動(dòng)一步
          if ((abs(toK - fromK) + abs(toI - fromI)) != 1) return false;
          return true;
      }
      
    • 第七步檢驗(yàn)炮的走法

      • 必須直線移動(dòng)
      • 若目標(biāo)為空,路徑上不能有任何子
      • 若目標(biāo)位敵方棋子,路徑上必須恰好有一個(gè)棋子
      else if (strcmp(fromChess.name, "砲") == 0 || strcmp(fromChess.name, "炮") == 0) {
          // 必須直線移動(dòng)
          if (fromI != toI && fromK != toK) return false;
          int count = 0;
          int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
          int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
          // 統(tǒng)計(jì)路徑上的棋子數(shù)
          for (int i = 1; i < distance; i++) {
              int x = (fromI == toI) ? fromI : fromI + step * i;
              int y = (fromI == toI) ? fromK + step * i : fromK;
              if (strcmp(map[x][y].name, "") != 0) count++;
          }
          // 目標(biāo)為空則路徑必須無(wú)子,否則必須隔一子
          if (strcmp(toChess.name, "") == 0) {
              return (count == 0);
          } else {
              return (count == 1);
          }
      }
      
    • 第八步檢驗(yàn)兵的走法

      • 未過(guò)河
        • 只能向前移動(dòng)一格。
        • 過(guò)河后更新標(biāo)志位。
      • 已過(guò)河
        • 可向前或橫向移動(dòng)一格。
      else if (strcmp(fromChess.name, "卒") == 0 || strcmp(fromChess.name, "兵") == 0) {
          int direction = (fromChess.type == 'B') ? 1 : -1; // 黑卒向下,紅兵向上
          // 未過(guò)河時(shí)只能前進(jìn)
          if (!fromChess.flag) {
              if (toI != fromI + direction || toK != fromK) return false;
              // 更新過(guò)河狀態(tài)
              if ((fromChess.type == 'B' && toI >= 5) || (fromChess.type == 'R' && toI <= 4)) {
                  map[fromI][fromK].flag = true; // 標(biāo)記為已過(guò)河
              }
          } else { // 過(guò)河后可左右或前進(jìn)
              bool valid = false;
              if (toI == fromI + direction && toK == fromK) valid = true; // 前進(jìn)
              if (toI == fromI && abs(toK - fromK) == 1) valid = true;    // 橫向移動(dòng)
              return valid;
          }
          return true;
      }
      

      綜上,我們將CheckMove()函數(shù)插入游戲控制函數(shù)中,當(dāng)且僅當(dāng)移動(dòng)合法時(shí)可對(duì)棋子進(jìn)行移動(dòng)。

      接下來(lái)定義一個(gè)將軍狀態(tài)檢測(cè)函數(shù)。即當(dāng)一方走棋后,檢驗(yàn)接下來(lái)走棋一方是否處于被將軍狀態(tài)。

      // 檢查將帥是否被攻擊
      bool CheckGeneral() {
      	POINT generalPos = { -1,1 };
      	char targetType = isRedTurn ? 'B' : 'R';
      	const char* generalName = (targetType == 'B') ? "將" : "帥";
      	// 查找己方將的位置
      	for (int i = 0; i < ROW; ++i) {
      		for (int j = 0; j < COL; ++j) {
      			if (map[i][j].type == targetType &&
      				strcmp(map[i][j].name, generalName) == 0) {
      				generalPos.x = j;
      				generalPos.y = i;
      				break;
      			}
      		}
      	}
      	// 遍歷所有敵方棋子檢查是否可攻擊將
      	char enemyType = isRedTurn ? 'R' : 'B';
      	for (int i = 0; i < ROW; ++i) {
      		for (int j = 0; j < COL; ++j) {
      			if (map[i][j].type == enemyType &&
      				strcmp(map[i][j].name, "") != 0) {
      				// 臨時(shí)保存目標(biāo)位置棋子
      				struct Chess temp = map[generalPos.y][generalPos.x];
      				// 模擬移動(dòng)
      				strcpy(map[generalPos.y][generalPos.x].name, map[i][j].name);
      				bool canAttack = CheckMove(i, j, generalPos.y, generalPos.x);
      				// 恢復(fù)現(xiàn)場(chǎng)
      				map[generalPos.y][generalPos.x] = temp;
      				if (canAttack) return true;
      			}
      		}
      	}
      	return false;
      }
      

      接下來(lái)我們進(jìn)行勝利條件判斷。

      當(dāng)將帥面對(duì)面或者將帥被吃時(shí),游戲結(jié)束。

      // 勝利條件判斷
      bool CheckWin() {
      	bool redExist = false, blackExist = false;
      	POINT redGeneral = { -1, -1 }, blackGeneral = { -1, -1 };
      	// 遍歷查找將帥位置
      	for (int i = 0; i < ROW; i++) {
      		for (int k = 0; k < COL; k++) {
      			if (strcmp(map[i][k].name, "帥") == 0) {
      				redExist = true;
      				redGeneral.x = k;
      				redGeneral.y = i;
      			}
      			if (strcmp(map[i][k].name, "將") == 0) {
      				blackExist = true;
      				blackGeneral.x = k;
      				blackGeneral.y = i;
      			}
      		}
      	}
      	// 將帥被吃判斷
      	if (!redExist) {
      		MessageBox(GetHWnd(), "黑方勝利!", "游戲結(jié)束", MB_OK);
      		return true;
      	}
      	if (!blackExist) {
      		MessageBox(GetHWnd(), "紅方勝利!", "游戲結(jié)束", MB_OK);
      		return true;
      	}
      	// 將帥對(duì)面判斷(飛將規(guī)則)
      	if (redGeneral.x == blackGeneral.x) {
      		int minY = (redGeneral.y < blackGeneral.y) ? redGeneral.y : blackGeneral.y;
      		int maxY = (redGeneral.y > blackGeneral.y) ? redGeneral.y : blackGeneral.y;
      		bool hasBlock = false;
      		// 檢查中間是否有棋子
      		for (int y = minY + 1; y < maxY; y++) {
      			if (strcmp(map[y][redGeneral.x].name, "") != 0) {
      				hasBlock = true;
      				break;
      			}
      		}
      		// 無(wú)遮擋且輪到對(duì)方走棋時(shí)判負(fù)
      		if (!hasBlock) {
      			// 當(dāng)形成將帥對(duì)面時(shí),由當(dāng)前走棋方的對(duì)手獲勝
      			const char* winner = isRedTurn ? "黑方" : "紅方";
      			// 字符串處理方式
      			char message[50];
      			sprintf(message, "%s 勝利!將帥對(duì)面!", winner);
      			MessageBox(GetHWnd(), message, "游戲結(jié)束", MB_OK);
      			return true;
      		}
      	}
      	return false;
      }
      

      綜上,我們將這些函數(shù)插入游戲控制函數(shù)內(nèi)即可。

  3. 添加背景音樂(lè)、走棋音效等

    這一步很簡(jiǎn)單,直接給出代碼。

    //定義音效資源
    MCI_OPEN_PARMS openBGM;
    DWORD bgmld;
    bool isMusicPlaying = false;
    //音頻初始化
    void SoundInit() {
    	//加載背景音樂(lè)
    	mciSendString("open \"./sounds/bgm1.wav\" type mpegvideo alias bgm", NULL, 0, NULL);
    	//預(yù)加載音效
    	mciSendString("open \"./sounds/click.wav\" alias click", NULL, 0, NULL);
    	mciSendString("open \"./sounds/move.wav\" alias move", NULL, 0, NULL);
    	mciSendString("open \"./sounds/eat.wav\" alias eat", NULL, 0, NULL);
    	mciSendString("open \"./sounds/check.wav\" alias check", NULL, 0, NULL);
    }
    //播放背景音樂(lè)
    void PlayBGM() {
    	mciSendString("play bgm repeat", NULL, 0, NULL);
    	// 設(shè)置主音量(范圍0-1000)
    	mciSendString("setaudio bgm volume to 1000", NULL, 0, NULL);
    	isMusicPlaying = true;
    }
    //音效播放函數(shù)
    void PlaySoundEffect(const char* alias) {
    	char cmd[50];
    	sprintf(cmd, "play %s from 0", alias);
    	mciSendString(cmd, NULL, 0, NULL);
    }
    
  4. 添加紅黑雙方計(jì)時(shí)功能

    我們先看此步驟完成后的結(jié)果。

    這里我們只是實(shí)現(xiàn)最簡(jiǎn)單的計(jì)時(shí)功能,即雙方步時(shí)60秒,總時(shí)長(zhǎng)10分鐘。

    首先我們需要定義時(shí)間結(jié)構(gòu)體包括總時(shí)間和當(dāng)前步時(shí)。

    //定義時(shí)間結(jié)構(gòu)
    struct Timer {
    	int totalTime;	//總剩余時(shí)間(秒)
    	int stepTime;	//當(dāng)前步剩余時(shí)間(秒)
    }redTimer, blackTimer;
    DWORD lastUpdateTime = 0;  // 記錄上次更新時(shí)間戳(毫秒)
    const int INIT_TOTAL_TIME = 600; // 初始總時(shí)間(10分鐘)
    const int INIT_STEP_TIME = 60;   // 初始步時(shí)(60秒)
    

    接著我們?cè)谟螒虺跏蓟瘮?shù)中添加初始化計(jì)時(shí)器。

    void GameInit(){
    	// 初始化計(jì)時(shí)器
    	redTimer.totalTime = INIT_TOTAL_TIME;
    	redTimer.stepTime = INIT_STEP_TIME;
    	blackTimer.totalTime = INIT_TOTAL_TIME;
    	blackTimer.stepTime = INIT_STEP_TIME;
    	lastUpdateTime = GetTickCount(); // 獲取當(dāng)前系統(tǒng)時(shí)間    
    }
    

    在游戲控制函數(shù)中添加雙方交換時(shí)步時(shí)的重置

    void GameControl(){
    	// 重置當(dāng)前玩家的步時(shí)
        if (isRedTurn) {
        	redTimer.stepTime = INIT_STEP_TIME;
        }
        else {
        	blackTimer.stepTime = INIT_STEP_TIME;
        }  
    }
    

    然后我們?cè)谟螒蚶L制函數(shù)中將時(shí)間顯示在棋盤右側(cè)。

    void GameGraw(){
    	//設(shè)置文字樣式
    	settextstyle(20, 0, "楷體");
    	settextcolor(BLACK);
    	// 繪制計(jì)時(shí)器
    	settextstyle(20, 0, "楷體");
    	settextcolor(BLACK);
    	char redTimeStr1[50], redTimeStr2[50],blackTimeStr1[50], blackTimeStr2[50];
    	// 格式:總時(shí)間 MM:SS 步時(shí) SS
    	sprintf(redTimeStr1, "紅方: 總 %02d:%02d",
    		redTimer.totalTime / 60, redTimer.totalTime % 60);
    	sprintf(redTimeStr2, "    步時(shí): %02d",redTimer.stepTime);
    	sprintf(blackTimeStr1, "黑方: 總 %02d:%02d",
    		blackTimer.totalTime / 60, blackTimer.totalTime % 60);
    	sprintf(blackTimeStr2, "    步時(shí): %02d", blackTimer.stepTime);
    	outtextxy(650, 650, redTimeStr1);
    	outtextxy(650, 675, redTimeStr2);
    	outtextxy(650, 50, blackTimeStr1);
    	outtextxy(650, 75, blackTimeStr2);
    }
    

    最后,在主函數(shù)中實(shí)現(xiàn)對(duì)時(shí)間的更新。

    再結(jié)合之前的函數(shù),我們寫(xiě)出主函數(shù)即可實(shí)現(xiàn)中國(guó)象棋的最簡(jiǎn)化版。

    int main() {
    	initgraph(800, 800, SHOWCONSOLE);	// 初始化圖形窗口
    	SoundInit();	// 初始化音效
    	PlayBGM();	// 播放背景音樂(lè)
    	GameInit();	// 初始化游戲
    	while (true) {
    		GameControl();  // 觸發(fā)時(shí)間扣除
    		// 更新時(shí)間邏輯(每秒更新一次)
    		DWORD currentTime = GetTickCount();
    		DWORD elapsed = currentTime - lastUpdateTime;
    		if (elapsed >= 1000) { // 超過(guò)1秒
    			int seconds = elapsed / 1000; // 計(jì)算經(jīng)過(guò)的整秒數(shù)
    			// 扣除當(dāng)前玩家的總時(shí)間和步時(shí)
    			if (isRedTurn) {
    				redTimer.totalTime -= seconds;
    				redTimer.stepTime -= seconds;
    			}
    			else {
    				blackTimer.totalTime -= seconds;
    				blackTimer.stepTime -= seconds;
    			}
    			lastUpdateTime = currentTime;
    			// 檢查超時(shí)
    			if (redTimer.totalTime <= 0 || redTimer.stepTime <= 0) {
    				MessageBox(GetHWnd(), "紅方超時(shí),黑方勝利!", "游戲結(jié)束", MB_OK);
    				exit(0);
    			}
    			if (blackTimer.totalTime <= 0 || blackTimer.stepTime <= 0) {
    				MessageBox(GetHWnd(), "黑方超時(shí),紅方勝利!", "游戲結(jié)束", MB_OK);
    				exit(0);
    			}
    		}
    		// 雙緩沖繪制流程
    		BeginBatchDraw();  // 開(kāi)始批量繪制
    		GameDraw();
    		EndBatchDraw();    // 結(jié)束批量繪制
    	}
    	EndBatchDraw();	// 結(jié)束繪制
    	closegraph();	// 關(guān)閉圖形窗口
    	mciSendString("close all", NULL, 0, NULL); // 關(guān)閉所有音頻
    	return 0;
    }
    

    到這里中國(guó)象棋的基本功能我們就實(shí)現(xiàn)了。由于這是本作者的第一個(gè)項(xiàng)目有點(diǎn)小激動(dòng),于是準(zhǔn)備先進(jìn)性項(xiàng)目打包處理。

這里使用傳統(tǒng)打包方式,使用Microsoft Visual Studio Installer Projects打包。

  1. 安裝擴(kuò)展插件
    • 在VS中安裝Microsoft Visual Studio Installer Projects擴(kuò)展
    • 操作路徑:擴(kuò)展 > 管理擴(kuò)展 > 搜索安裝 > 重啟VS
  2. 創(chuàng)建安裝項(xiàng)目
    • 右鍵解決方案>添加>新建項(xiàng)目
    • 搜索選擇"setup Project"模板
    • 配置項(xiàng)目名稱
  3. 配置安裝內(nèi)容
    • 主程序添加:右鍵Application Folder>Add>項(xiàng)目輸出>選擇主輸出
    • 資源文件處理:將"sound"音效文件夾拖入Application Folder(確保所有依賴的DLL被包含)
  4. 配置快捷方式
    • 右鍵Application>Add>文件>選擇所需的快捷方式圖案.ico文件(圖片轉(zhuǎn)換成.ico格式可以通過(guò)PS轉(zhuǎn)換,需要提前配置插件)
    • 右鍵主輸出>創(chuàng)建快捷方式
    • 將快捷方式拖入U(xiǎn)ser's Desktop和User's Programs Menu
    • 右鍵快捷方式>屬性窗口>Icon>Browse>Browse>選擇Application Folder文件中的ico文件>OK
  5. 處理依賴項(xiàng)
    • 右鍵Application Folder>Add>文件>找到vcredist_x64.exe(路徑:VS安裝目錄\VC\Redist\MSVC\版本號(hào))
  6. 生成安裝包
    • 右鍵安裝項(xiàng)目>生成
    • 在輸出目錄即可獲取Setup.exe和.msi文件

轉(zhuǎn)自https://www.cnblogs.com/Yygz314/p/18899387


該文章在 2025/6/2 9:25:04 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 精品日韩国产 | 国产在线久欧美视频 | 三区精品在线观看 | 精品综合 | 无码精品毛片成人影院 | 国产自产中文一区 | 国产精品国产自线 | 成人导航在 | www亚洲伊| 91看片婬黄大片 | 国产精品欧美亚洲区 | 91视频网站入口 | 91福利1000| 国产网站免费视频 | 欧美专区日韩在线 | 日韩在线观看福利片 | 精品一区精品二区制 | 国产精品三级在 | 人人97在线 | 日韩国产欧美经典 | 国产视频三区 | 国产视频第二页 | 乱淫视频 | 国产精品户| 日本+国产+高清 | 欧美午夜影视 | 精品二区 | 国产精品自在线免费 | 日本欧美视频 | 麻花传媒| 国产大奶在线播放 | 九九热精品免费观看 | 国产又粗又猛又爽 | 日韩在线一区卜 | 日产精品| 国产女人十八毛 | 国产猛男gayb| 日韩一级性生活 | 97视频在线精 | 中文字幕丰 | 国产高清网站 |