本人原創四人象棋,大家有什麼補充的嗎?
表格不錯,字不行。
我來發個真正正版原創的吧!
當然,這個不是四人的。
我叫它小象棋,也就是迷你的。
和中國象棋規則差不多,Boss死了就算輸。順便還寫了個AI。下面有代碼。
你可以把代碼複製回家,懂的話編譯了就可以運行。不懂編譯可以找我給現成的。
可以人機對弈,棋力不弱喲,當然是DOS級別的界面哈。
C源代碼(可直接編譯使用):
//////////////////////////////////////////////
// New Chess Tiny 1.0 //
// hi@leilei.name //
//////////////////////////////////////////////
#include &
//////////////////////////////////////////////
// 局面表示 //
//////////////////////////////////////////////
#define NOPIECE 0 //無子
#define KING 1 //王
#define ARCHER 2 //弓箭手
#define KNIGHT 3 //騎士
#define FIGHTER 4 //步兵
#define WHITE 0 //白方
#define BLACK 1 //黑方
#define MAX_GEN_MOVES 32 //走法的最大生成數
#define SEARCH_DEPTH 7 //設置搜索深度
#define RANDOM_VALUE 10 //隨機因子
#define INFINITY_VALUE 1000 //設置局麵價值無窮大為1000
//全局變數
int currentPlayer = WHITE; //初始化為白方走棋,BLACK表示黑方走棋
int theMoves[MAX_GEN_MOVES]; //定義一個走法數組用來保存生成的所有走法
int bestMove; //最佳走法,搜索結果
int theDepth; //當前搜索深度
//棋盤數組(帶開局棋子位置,下白上黑)
char board[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 18, 17, 16, 17, 18, 0, 0,
0, 19, 19, 19, 19, 19, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 11, 11, 11, 11, 11, 0, 0,
0, 10, 9, 8, 9, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
//用來標記棋子是否在棋盤上的數組
static const int isInBoard[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
//用來標記王是否在未過河和步兵是否後退的數組
static const int isAtHome[2][64] = {
{ //白王(步兵)
0, 0, 0, 0, 0, 0, 0, 8, //下標為7的元素用來判斷步兵是否後退
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
}, { //黑王(步兵)
0, 0, 0, 0, 0, 0, 0,-8, //下標為7的元素用來判斷步兵是否後退
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
}
};
//這幾個數組用來表示棋子的走子方向
static const char kingMovesTable[8] = {-8, 8, -1, 1, -9, -7, 9, 7}; //王
static const char archerMovesTable[4] = {-9, -7, 9, 7}; //弓手
static const char knightMovesTable[4] = {-8, 8, -1, 1}; //騎士
static const char fightMovesTable[4] = {-8, 8, -1, 1}; //步兵
//棋子的價值數組
static const char pieceValue[4] = {9, 5, 5, 3}; //王9 弓5 騎5 步3
//由棋盤數組下標獲得棋子X坐標
int getXFromLocation(int location){
return (location 7) - 1;
}
//由棋盤數組下標獲得棋子Y坐標
int getYFromLocation(int location){
return (location &>&> 3) - 1;
}
//由棋子X坐標,Y坐標獲得棋盤數組下標
int getLocationFromXY(int x, int y){
return (x + 1) + (y + 1 &<&< 3);
}
//判斷是否為己方棋子
int isMyPiece(char piece){
int tag;
tag = (currentPlayer &<&< 3) + 8;
return piece tag;
}
//改變走棋方,不是0 就是1
void changePlayer(){
currentPlayer = 1 - currentPlayer;
}
//////////////////////////////////////////////
// 走法生成 //
//////////////////////////////////////////////
//在棋盤上放一枚棋子的函數
void addPiece(int location, char piece){
board[location] = piece;
}
//在棋盤上拿走一枚棋子的函數
void delPiece(int location){
board[location] = NOPIECE;
}
//根據走法生成走法起點位置(起點位置的棋盤數組下標)
int generateMoveFrom(int move){
return move 255;
}
//根據走法生成走法目的位置(目的位置的棋盤數組下標)
int generateMoveTo(int move){
return move &>&> 8;
}
//由起點位置和目的位置合成走法
int composeMove(int locationFrom, int locationTo){
return locationFrom + locationTo * 256;
}
//走法生成函數,產生一個局面的所有走法,傳遞一個走法列表數組指針,返回生成的所有走法
int generateAllMoves(int *moves){
int i, genCount, locationFrom, locationTo, sideTag;
char pieceFrom, pieceTo;
//走法計數器清零
genCount = 0;
//走棋方標記
sideTag = (currentPlayer &<&< 3) + 8;
//遍歷棋盤找到當前走棋方棋子
for(locationFrom = 0; locationFrom &< 64; locationFrom ++){
//取得當前位置的棋子
pieceFrom = board[locationFrom];
//1.如果找到的不是本方棋子,繼續找
if(!isMyPiece(pieceFrom)){
continue;
}
//2.找到棋子,根據棋子類型生成走法
switch (pieceFrom - sideTag + 1){
case KING:
for(i = 0; i &< 8; i++){
locationTo = locationFrom + kingMovesTable[i];
if(isAtHome[currentPlayer][locationTo]){ //目標位置是否未出國界
pieceTo = board[locationTo];
if(!isMyPiece(pieceTo)){ //目標位置是否無本方棋子
moves[genCount] = composeMove(locationFrom, locationTo); //保存走法
genCount++; //計數
}
}
}
break;
case ARCHER:
for(i = 0; i &< 4; i++){
locationTo = locationFrom + archerMovesTable[i];
if(isInBoard[locationTo]){ //目標位置是否在棋盤內
pieceTo = board[locationTo];
if(!isMyPiece(pieceTo)){ //目標位置是否無本方棋子
moves[genCount] = composeMove(locationFrom, locationTo); //保存走法
genCount++; //計數
}
}
}
break;
case KNIGHT:
for(i = 0; i &< 4; i++){
locationTo = locationFrom + knightMovesTable[i];
if(isInBoard[locationTo]){ //目標位置是否在棋盤內
pieceTo = board[locationTo];
if(!isMyPiece(pieceTo)){ //目標位置是否無本方棋子
moves[genCount] = composeMove(locationFrom, locationTo); //保存走法
genCount++; //計數
}
}
}
break;
case FIGHTER:
for(i = 0; i &< 4; i++){
locationTo = locationFrom + kingMovesTable[i];
if(isInBoard[locationTo]){ //目標位置是否在棋盤內
if(locationFrom + isAtHome[currentPlayer][7] != locationTo){ //檢查兵是否後退
pieceTo = board[locationTo];
if(!isMyPiece(pieceTo)){ //目標位置是否無本方棋子
moves[genCount] = composeMove(locationFrom, locationTo); //保存走法
genCount++; //計數
}
}
}
}
break;
}
}
return genCount; //返回生成的走法數
}
//能根據走法走一步棋的函數
int makeOneMove(int move, char *captured){ //傳遞一個用來保存吃子位置的數組
int i, genCount, isLegalMove, locationFrom, locationTo;
char pieceFrom;
isLegalMove = 1; //初始化走法為不合法
genCount = generateAllMoves(theMoves); //生成所有走法
//在所有走法中查找當前走法是否存在
for(i = 0; i &< genCount; i++){
//如果找到一個走法等於當前走法
if(theMoves[i] == move){
isLegalMove = 0; //所有走法中有此走法,說明走法合法
break;
}
}
//1.首先判斷走法是否合法
if(isLegalMove == 1){
return 0; //返回走棋失敗
}
//2.分解走法,取得起點位置和目的位置
locationFrom = generateMoveFrom(move);
locationTo = generateMoveTo(move);
//3.取得要走的子
pieceFrom = board[locationFrom];
//4.保存被吃的子
*captured = board[locationTo];
//5.移動棋子
if(*captured != NOPIECE){
delPiece(locationTo);
}
delPiece(locationFrom);
addPiece(locationTo, pieceFrom);
//6.交換走棋方
changePlayer();
return 1; //返回走棋成功
}
//撤銷走一步棋的函數
void undoOneMove(int move, char captured){
int locationFrom, locationTo;
char pieceFrom;
//1.分解走法,取得起點位置和目的位置
locationFrom = generateMoveFrom(move);
locationTo = generateMoveTo(move);
//3.取得要還原的子
pieceFrom = board[locationTo];
//4.交換走棋方
changePlayer();
//5.移動棋子
delPiece(locationTo); //刪除棋子
addPiece(locationFrom, pieceFrom); //添加己方棋子
if(captured != NOPIECE){
addPiece(locationTo, captured);
}
}
//////////////////////////////////////////////
// 局面評估 //
//////////////////////////////////////////////
//判斷指定走棋方是否分出勝負的函數
int isThePlayerDie(int thePlayer){
int i, theKing, isDie;
//初始化標記
isDie = 1;
//取得當前走棋方的王
theKing = (thePlayer &<&< 3) + 8;
//如果生成零個走法,則已被困死,將返回1
if(generateAllMoves(theMoves)){
//檢測王是否死去
for(i = 0; i &< 64; i ++){
if(board[i] == theKing){
isDie = 0;
}
}
}
return isDie;
}
//局面評估函數
int evaluatePosition(){
int i, whiteValue, blackValue, value;
//自己已死
if(isThePlayerDie(currentPlayer)){
return -INFINITY_VALUE + theDepth;
}
//初始化雙方的總價值
whiteValue = blackValue = 0;
//遍歷棋盤,找到棋子
for(i = 0; i &< 64; i++){
if(board[i] &<= 15 board[i] &>= 1){
whiteValue += pieceValue[board[i] % 8];
}
if(board[i] &>= 16){
blackValue += pieceValue[board[i] % 8];
}
}
//計算局麵價值
value = whiteValue - blackValue;
return currentPlayer == WHITE ? value : -value;
}
//////////////////////////////////////////////
// 局面搜索 //
//////////////////////////////////////////////
//Alpha-Beta搜索函數
int AlphaBetaSearch(int depth, int alpha, int beta){
int i, genCount, value;
int allMoves[MAX_GEN_MOVES];
char captured;
//如果搜索到指定深度,則返回局面評估值
if(depth == 0){
return evaluatePosition();
}
//如果是殺棋,就根據距殺棋的步數給出評價
if(isThePlayerDie(currentPlayer)){ //自己已死
return -INFINITY_VALUE + theDepth;
}
genCount = generateAllMoves(allMoves);
for(i = 0; i &< genCount; i++){
if(makeOneMove(allMoves[i], captured)){ //如果走棋成功
theDepth++;
value = -AlphaBetaSearch(depth - 1, -beta, -alpha); //遞歸
undoOneMove(allMoves[i], captured); //還原
theDepth--;
if(value &>= beta){
return beta;
}
if(value &> alpha){
alpha = value;
if(theDepth == 0){ //如果是根節點保存最佳走法
bestMove = allMoves[i];
alpha += (rand() RANDOM_VALUE) - (rand() RANDOM_VALUE); //隨機性
}
}
}
}
return alpha;
}
//讓電腦走棋
void computerThink(){
char captured;
theDepth = 0; //距根結點的距離
AlphaBetaSearch(SEARCH_DEPTH, -INFINITY_VALUE, INFINITY_VALUE);
makeOneMove(bestMove, captured);
}
//////////////////////////////////////////////
// 界面程序 //
//////////////////////////////////////////////
//定義個變數用來設置電腦(引擎)是白方或者是黑方
int engine = BLACK;
//寫個翻轉棋盤的函數
int flipLocation(int location){
location = 62 - location;
return location;
}
//寫個簡單的界面吧
void showBoard(){
int i, j, piece;
int location;
printf("
");
printf(" y **********
");
for(i = 0; i &< 6; i++){
printf(" %d ", i);
for(j = 0; j &< 5; j++){
location = getLocationFromXY(j, i); //取得數組下標
if(engine == WHITE){ //如果引擎持白方,翻轉棋盤位置
location = flipLocation(location);
}
piece = board[location];
if(piece == 8){
printf("");
}else if(piece == 9){
printf("◆");
}else if(piece == 10){
printf("");
}else if(piece == 11){
printf("●");
}else if(piece == 16){
printf("☆");
}else if(piece == 17){
printf("◇");
}else if(piece == 18){
printf("□");
}else if(piece == 19){
printf("○");
}else if(piece == NOPIECE){
printf("╋");
}else{
printf(" ");
}
}
printf("
");
}
printf(" **********
");
printf(" 0 1 2 3 4x
");
}
////////////////////////////////////////////////////////////////////////////////
// 下面是演示程序 //
////////////////////////////////////////////////////////////////////////////////
int main(){
int move, from, to, fromX, fromY, toX, toY;
char command, captured;
//設置隨機數種子
srand((long)time(NULL));
/* *********測試用代碼*********
int i, genCount, value, eatCount;
genCount = generateAllMoves(theMoves); //生成開局所有走法
printf("開局時,白方有%d種走法,分別是:
", genCount);
for(i = 0; i &< genCount;i++){
printf("from %d to %d
", generateMoveFrom(theMoves[i]), generateMoveTo(theMoves[i]));
}
printf("開局時,局面的價值是:%d
", evaluatePosition());
value = AlphaBetaSearch(6, -INFINITY_VALUE, INFINITY_VALUE);
printf("開局搜索局面,找到的最佳局面評分:%d
", value);
printf("開局搜索局面,找到的最佳走法是:from %d to %d
", generateMoveFrom(bestMove), generateMoveTo(bestMove));
*/
///*
printf("/////////////////////////////
");
printf("// New Chess Tiny 1.0 //
");
printf("/////////////////////////////
");
printf("
開始:
");
showBoard();
while(1){
printf("
輸入Q退出,輸入D下棋:");
command = getch();
if(command == "q" || command == "Q"){
return 0;
}
if(command == "d" || command == "D"){ //選擇了下棋
printf("
輸入W選擇白棋,輸入B選擇黑棋,輸入C觀看電腦對弈:");
command = getch();
if(command == "w" || command == "W"){ //人選擇了白棋
engine = BLACK; //設置引擎為黑棋
printf("
您選擇了白棋!
");
}else if(command == "b" || command == "B"){ //人選擇了黑棋
engine = WHITE; //設置引擎為白棋
printf("
您選擇了黑棋!
");
}else if(command == "c" || command == "C"){ //人選擇了黑棋
engine = 2; //設置電腦對弈標誌
printf("
電腦對弈開始!
");
}else{
continue;
}
while(1){
if(isThePlayerDie(currentPlayer) currentPlayer != engine){
printf("
不好意思,你已戰敗!再來一局請重新打開程序!
");
printf("
輸入q退出:");
command = getch();
if(command == "q" || command == "Q"){
return 0;
}
}else if(isThePlayerDie(currentPlayer) currentPlayer == engine){
printf("
恭喜你,電腦被你打敗!再來一局請重新打開程序!
");
printf("
輸入q退出:");
command = getch();
if(command == "q" || command == "Q"){
return 1;
}
}else{
if(engine == WHITE){ //引擎為白棋
showBoard();
if(1){ //如果電腦持白方則先走
printf("
電腦正在思考...
");
computerThink();
move = bestMove;
from = generateMoveFrom(move); //分解走法
to = generateMoveTo(move);
from = flipLocation(from); //翻轉位置
to = flipLocation(to);
fromX = getXFromLocation(from); //取得坐標
fromY = getYFromLocation(from);
toX = getXFromLocation(to);
toY = getYFromLocation(to);
printf("
電腦所走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
showBoard();
}
if(isThePlayerDie(currentPlayer)){ //如果玩家已敗
continue;
}
if(1){ //輪到人走棋
printf("
輪到您下棋,請輸入坐標 x,y x,y :");
scanf("%d,%d %d,%d", fromX, fromY, toX, toY);
printf("
您走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
from = getLocationFromXY(fromX, fromY); //合成位置
to = getLocationFromXY(toX, toY);
from = flipLocation(from); //翻轉位置
to = flipLocation(to);
move = composeMove(from, to); //合成走法
while(!makeOneMove(move, captured)){
printf("
輸入坐標錯誤,請重新輸入坐標:");
scanf("%d,%d %d,%d", fromX, fromY, toX, toY);
from = getLocationFromXY(fromX, fromY);//合成位置
to = getLocationFromXY(toX, toY);
from = flipLocation(from); //翻轉位置
to = flipLocation(to);
move = composeMove(from, to); //合成走法
getchar();
}
}
}
if(engine == BLACK){ //引擎為黑方
showBoard();
if(1){ //輪到人走棋
printf("
輪到您下棋,請輸入坐標 x,y x,y :");
scanf("%d,%d %d,%d", fromX, fromY, toX, toY);
printf("
您走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
from = getLocationFromXY(fromX, fromY);//合成位置
to = getLocationFromXY(toX, toY);
move = composeMove(from, to); //合成走法
while(!makeOneMove(move, captured)){
printf("
輸入坐標錯誤,請重新輸入坐標:");
scanf("%d,%d %d,%d", fromX, fromY, toX, toY);
from = getLocationFromXY(fromX, fromY);//合成位置
to = getLocationFromXY(toX, toY);
move = composeMove(from, to); //合成走法
getchar();
}
showBoard();
}
if(isThePlayerDie(currentPlayer)){ //如果電腦已敗
continue;
}
if(1){ //如果電腦黑方則後走
printf("
電腦正在思考...
");
computerThink();
move = bestMove;
from = generateMoveFrom(move); //分解走法
to = generateMoveTo(move);
fromX = getXFromLocation(from); //取得坐標
fromY = getYFromLocation(from);
toX = getXFromLocation(to);
toY = getYFromLocation(to);
printf("
電腦所走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
}
}
if(engine == 2){ //引擎對戰標誌
showBoard();
if(1){ //電腦A先走棋
printf("
電腦A正在思考...
");
sleep(3000);
computerThink();
move = bestMove;
from = generateMoveFrom(move); //分解走法
to = generateMoveTo(move);
fromX = getXFromLocation(from); //取得坐標
fromY = getYFromLocation(from);
toX = getXFromLocation(to);
toY = getYFromLocation(to);
printf("
電腦A所走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
showBoard();
}
if(isThePlayerDie(currentPlayer)){ //如果玩家已敗
continue;
}
if(1){ //輪到電腦B走棋
printf("
電腦B正在思考...
");
sleep(3000);
computerThink();
move = bestMove;
from = generateMoveFrom(move); //分解走法
to = generateMoveTo(move);
fromX = getXFromLocation(from); //取得坐標
fromY = getYFromLocation(from);
toX = getXFromLocation(to);
toY = getYFromLocation(to);
printf("
電腦B所走的棋是:from (%d,%d) to (%d,%d)
", fromX, fromY, toX, toY);
}
}
}
}
}
}
//*/
return 0;
}
運行效果圖:
歡迎大家來提出更多玩法,豐富一下棋子的走棋規則。讓它變得更好玩。
比如:騎兵是不是可以走兩步的距離,或者弓手是不是可以像炮一樣翻山等。
……如果我補充了「爹」我就上當了 等等......
乾脆把坑也填滿線吧。中原大戰。。。
棋子轉彎攻擊左右手玩家的規則是怎樣的?
馬在轉彎處怎麼跳?車是仍然直線還是轉90度?炮又怎麼打?對面的玩家是隊友嗎?如果是的話,在後期階段隊友間雙方將帥走位很驚悚啊。中華四國象棋_互動百科
小時候玩過四國象棋,挺有意思。是棋是擺在四個角上,下起來互相牽制。不過一種遊戲必須實現各方平衡才好玩。樓主有心設計的要充分考慮這方面。
四人象棋,「三國」象棋都早已有之了吧。看生活大爆炸中,sheldon和leonard玩過三維的「立體國際),這個好像還沒有中國象棋版,建議樓主可以參考設計一下,呵呵。
另外,樓上朋友提到的棋盤中間設計成蟲洞的多維象棋的創意,也挺好的,建議搞個實物出來。這個太難了吧,2家將死一家應該飛快吧
果然中間是個大坑
可以加入一枚色子,已增加遊戲的不確定因素,解決進攻方向以及多方圍攻一方的問題
自己要是能做個象棋程序就好了
推薦閱讀: