用C語言來實現一個三子棋,我們首先要有一個思維框架。
遊戲開始前首先要有一個菜單供玩家選擇,選項有進入和退出兩種選項。當然,也要考慮玩家不小心按錯的情況。退出遊戲則直接退出,而進入遊戲結束一局遊戲後應該再次讓玩家進行菜單選擇。這樣就又回到了開始的選項,形成了死循環,跳出的途徑只有選擇菜單上的退出選項。
以上的情況就是我們遊戲的基本設定,以C語言來實現也十分簡單:
//三字棋
#include "play.h"
int main()
{
int tmp = 0;//定義一個變量來表示玩家的選擇
do
{
menu();//打印遊戲菜單
printf("請輸入您的選擇");
scanf("%d", &tmp);//玩家進行選擇
switch (tmp)//選擇後出現的情況
{
case 1:
game();
break;
case 0:
return 0;
break;
default:
printf("輸入錯誤,重新輸入\n");
}
} while (1);
return 0;
}
這就實現了整個三字棋程序的基本框架,接下來我們對這個框架進行填充就可以了。
打印遊戲菜單函數 void menu():
這個函數就是打印出菜單信息,我們直接用printf函數打印出要給玩家傳遞的信息就可以了:
//打印三子棋遊戲菜單
void menu()
{
printf("****************************\n");
printf("***** 三子棋 *****\n");
printf("***** 1.play 0.exit *****\n");
printf("****************************\n");
}
接下來玩家選擇,的三種情況都有所處理,只要在實現game()函數就可以了。
game()函數中,我們先要打印出棋盤,並且考慮先手問題。電腦或玩家每次下子後還要打印棋盤使玩家能夠看到當前遊戲的狀態。每次落子後要判斷是否已經達成勝利條件,棋盤是否已滿。若無人勝利並且棋盤已滿則這局爲平局。其中電腦的落子也要額外進行設計,這樣game()函數將會比較複雜,所以我們依然先對它進行一個基本架構,之後再慢慢填充。
遊戲體函數 void game():
//遊戲函數架構
void game()
{
char arr[ROW][COL]; //這個數組的內容就是表示棋盤上落子的情況
init(arr, ROW, COL); //對數組進行初始化,這就是未落子時的狀態
print(arr); //打印棋盤
int ret3 = XS(); //玩家選擇先手方
while (1)
{
if (ret3)
{//電腦走,走完打印棋盤並判斷遊戲是否結束。
compter(arr, ROW, COL);
print(arr);
int ret1 = iswin(arr, ROW, COL);
if (panduan(ret1))
return;
/*if ('O' == ret1) //這個判斷在玩家和電腦操作後均要進行判斷
{ //所以我把它封裝爲一個函數,需要時調用即可。
printf("電腦贏\n");
return;
}
else if ('X' == ret1)
{
printf("玩家贏\n");
return;
}
else if (0 == ret1)
{
printf("平局\n");
return;
}*/
}
player(arr, ROW, COL);
print(arr);
int ret2 = iswin(arr, ROW, COL);
if (panduan(ret2))
return;
/*if ('O' == ret2)
{
printf("電腦贏\n");
return;
}
else if ('X' == ret2)
{
printf("玩家贏\n");
return;
}
else if (0 == ret2)
{
printf("平局\n");
return;
}*/
if (0 == ret3)
{
ret3 = ret3 + 1;
}
}
}
以上就是對這個遊戲體的函數的架構,我們下來對它進行填充:
數組初始化函數 void init():
我們將未落子時的狀態初始化爲字符空格:‘ ’。
//數組初始化(未落子時的狀態)
void init(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
打印棋盤函數 void print():
我們打印一個棋盤,這個棋盤要顯示當前的落子狀態——即arr數組。爲了讓玩家能夠便捷的找到座標,我們在給棋盤加上行列號。
//打印棋盤
void print(char arr[ROW ][COL])
{
printf(" 0 1 2 \n");
printf(" |---|---|---|\n");
printf(" 0| %c | %c | %c |\n",arr[0][0],arr[0][1],arr[0][2]);
printf(" |---|---|---|\n");
printf(" 1| %c | %c | %c |\n",arr[1][0],arr[1][1],arr[1][2]);
printf(" |---|---|---|\n");
printf(" 2| %c | %c | %c |\n",arr[2][0],arr[2][1],arr[2][2]);
printf(" |---|---|---|\n");
}
玩家選擇先手方函數 int XS():
我們讓這個函數返回一個int型的值,返回1則爲電腦先手,返回0則爲玩家先手。
//選擇先手方
int XS()
{
int i = 0;
printf("請您決定誰先手:1.電腦先手 0.您先手\n");
scanf("%d", &i);
return i;
}
電腦落子函數 void compter():
電腦落子的座標我們用rand()%3產生隨機數來得到,得到後再判斷一下這個座標是否爲空。爲空則電腦落子,函數結束。若已經有子則再次進行前面的操作,直到電腦落子爲止。
//電腦落子
void compter(char arr[ROW][COL],int row,int col)
{
while (1)
{
srand((unsigned int)time(NULL));
int i = rand() % 3;
int j = rand() % 3;
if (' ' == arr[i][j]) //此處爲空則落子,否則繼續循環。
{
arr[i][j] = 'O';
return;
}
}
}
判斷是否達成勝利條件函數 int iswin():
判斷勝利條件是否達成?哪一方達成,則返回代表那一方棋子的字符的ASCII值。若未達成勝利條件且棋盤未滿,則返回數字1(遊戲繼續)。若未達成勝利條件但棋盤已滿,則返回數字0(遊戲平局)。
//判斷是否有人達成勝利條件,或遊戲平局
int iswin(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if ((arr[i][0] == arr[i][1])&&(arr[i][1]==arr[i][2])&& arr[i][0]!= ' ')
return arr[i][0];
}
for (j = 0; j < col; j++)
{
if ((arr[0][j] == arr[1][j])&& (arr[1][j]== arr[2][j])&& (arr[0][j]!= ' '))
return arr[0][j];
}
if ((arr[0][0] == arr[1][1])&&(arr[1][1] == arr[2][2])&&( arr[0][0]!= ' '))
return arr[0][0];
if ((arr[0][2] == arr[1][1])&&(arr[1][1] == arr[2][0])&&( arr[0][2]!= ' '))
return arr[0][2];
for (i = 0; i < row; i++) //若沒有人勝利並且棋盤未滿,返回1遊戲繼續
{
for (j = 0; j < col; j++)
{
if (' ' == arr[i][j])
{
return 1;
}
}
}
return 0; //無人勝利並且棋盤已滿,返回0平局
}
判斷遊戲狀態函數 int panduan():
對遊戲的狀態進行判斷,遊戲未結束則返回0。遊戲結束則輸出遊戲勝負並且返回1。
//判斷遊戲狀態
int panduan(int ret)
{
if (1 == ret)
{
return 0;
}
if ('O' == ret)
{
printf("電腦贏\n");
}
else if ('X' == ret)
{
printf("玩家贏\n");
}
else if (0 == ret)
{
printf("平局\n");
}
return 1;
}
玩家落子函數 void player():
玩家輸入座標,若座標輸入錯誤則進行提示,重新輸入。
//玩家落子
void player(char arr[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("請輸入您要下棋的座標:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= row || y >= col || arr[x][y] != ' ')
{
printf("座標錯誤,請重新輸入:>");
}
else
{
arr[x][y] = 'X';
return;
}
}
}
由於我們是將遊戲基本架構函數 int main()和其它函數放在兩個不同的源文件中,所以我們要自己創建一個頭文件。
//頭文件
#ifndef __PLAY_H__
#define __PLAY_H__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
char arr[ROW][COL];
void menu();
void init(char arr[ROW][COL],int row,int col);
void print(char arr[ROW][COL]);
void game();
int XS();
void compter(char arr[ROW][COL],int row,int col);
int iswin(char arr[ROW][COL],int row,int col);
void player(char arr[ROW][COL],int row,int col);
int panduan(int ret);
#endif //__PLAY_H__
以上就是我們這個三子棋的所有代碼,我們最後再將它們整合在一起運行驗證一下。
//頭文件
#ifndef __PLAY_H__
#define __PLAY_H__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 3
#define COL 3
char arr[ROW][COL];
void menu();
void init(char arr[ROW][COL],int row,int col);
void print(char arr[ROW][COL]);
void game();
int XS();
void compter(char arr[ROW][COL],int row,int col);
int iswin(char arr[ROW][COL],int row,int col);
void player(char arr[ROW][COL],int row,int col);
int panduan(int ret);
#endif //__PLAY_H__
//遊戲基本主架構
//三字棋
#include "play.h"
int main()
{
int tmp = 0;//定義一個變量來表示玩家的選擇
do
{
menu();//打印遊戲菜單
printf("請輸入您的選擇");
scanf("%d", &tmp);//玩家進行選擇
switch (tmp)//選擇後出現的情況
{
case 1:
game();
break;
case 0:
return 0;
break;
default:
printf("輸入錯誤,重新輸入\n");
}
} while (1);
return 0;
}
//函數實現
#include "play.h"
int panduan(int ret)
{
if (1 == ret)
{
return 0;
}
if ('O' == ret)
{
printf("電腦贏\n");
}
else if ('X' == ret)
{
printf("玩家贏\n");
}
else if (0 == ret)
{
printf("平局\n");
}
return 1;
}
void game()
{
char arr[ROW][COL]; //這個數組的內容就是表示棋盤上落子的情況
init(arr, ROW, COL); //對數組進行初始化,這就是未落子時的狀態
print(arr); //打印棋盤
int ret3 = XS(); //玩家選擇先手方
while (1)
{
if (ret3)
{//電腦走,走完打印棋盤並判斷遊戲是否結束。
compter(arr, ROW, COL);
print(arr);
int ret1 = iswin(arr, ROW, COL);
if (panduan(ret1))
return;
}
player(arr, ROW, COL);
print(arr);
int ret2 = iswin(arr, ROW, COL);
if (panduan(ret2))
return;
if (0 == ret3)
{
ret3 = ret3 + 1;
}
}
}
void menu()
{
printf("****************************\n");
printf("***** 三子棋 *****\n");
printf("***** 1.play 0.exit *****\n");
printf("****************************\n");
}
void init(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ' ';
}
}
}
void print(char arr[ROW ][COL])
{
printf(" 0 1 2 \n");
printf(" |---|---|---|\n");
printf(" 0| %c | %c | %c |\n",arr[0][0],arr[0][1],arr[0][2]);
printf(" |---|---|---|\n");
printf(" 1| %c | %c | %c |\n",arr[1][0],arr[1][1],arr[1][2]);
printf(" |---|---|---|\n");
printf(" 2| %c | %c | %c |\n",arr[2][0],arr[2][1],arr[2][2]);
printf(" |---|---|---|\n");
}
int XS()
{
int i = 0;
printf("請您決定誰先手:1.電腦先手 0.您先手\n");
scanf("%d", &i);
return i;
}
void compter(char arr[ROW][COL],int row,int col)
{
while (1)
{
srand((unsigned int)time(NULL));
int i = rand() % 3;
int j = rand() % 3;
if (' ' == arr[i][j]) //此處爲空則落子,否則繼續循環。
{
arr[i][j] = 'O';
return;
}
}
}
int iswin(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if ((arr[i][0] == arr[i][1])&&(arr[i][1]==arr[i][2])&& arr[i][0]!= ' ')
return arr[i][0];
}
for (j = 0; j < col; j++)
{
if ((arr[0][j] == arr[1][j])&& (arr[1][j]== arr[2][j])&& (arr[0][j]!= ' '))
return arr[0][j];
}
if ((arr[0][0] == arr[1][1])&&(arr[1][1] == arr[2][2])&&( arr[0][0]!= ' '))
return arr[0][0];
if ((arr[0][2] == arr[1][1])&&(arr[1][1] == arr[2][0])&&( arr[0][2]!= ' '))
return arr[0][2];
for (i = 0; i < row; i++) //若沒有人勝利並且棋盤未滿,返回1遊戲繼續
{
for (j = 0; j < col; j++)
{
if (' ' == arr[i][j])
{
return 1;
}
}
}
return 0; //無人勝利並且棋盤已滿,返回0平局
}
void player(char arr[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("請輸入您要下棋的座標:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= row || y >= col || arr[x][y] != ' ')
{
printf("座標錯誤,請重新輸入:>");
}
else
{
arr[x][y] = 'X';
return;
}
}
}
運行結果:
驗證結果成功。
但是上面的電腦落子是隨機的,並不會和玩家進行“博弈”,這樣遊戲的可玩性就很差。我們應該對電腦進行一定的優化,使這個遊戲具有一定的挑戰。
我的優化十分簡單:
1.電腦會搶中間的位置:
當電腦落子時,只要中間的位置爲空,電腦就會在中間落子。
2.電腦會“攔截”:
電腦會判斷當前的棋盤上有是否有兩個連着的情形。如果有這種情況並且他們的第三個位置爲空,則電腦會落在第三個位置。(但是沒有對捺三個相同的這個情況進行考慮)。
電腦優化函數 void new_compter():
//電腦落子優化
void new_compter(char arr[ROW][COL], int row, int col) //電腦優化
{
while (1)
{
if (' ' == arr[1][1])
{
arr[1][1] = '0';
return;
}
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
j = 0;
if (((arr[i][j] == arr[i][j + 1])&& arr[i][j]!= ' ')
|| ((arr[i][j] == arr[i][j + 2])&& arr[i][j]!= ' ')
|| ((arr[i][j + 1] == arr[i][j + 2])&& arr[i][j+1]!= ' '))
//行有兩個相同時進行攔截
{
srand((unsigned int)time(NULL));
j = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
for (j = 0; j < COL; j++)
{
i = 0;
if (((arr[i][j] == arr[i + 1][j])&& arr[i][j]!= ' ')
|| ((arr[i][j] == arr[i + 2][j])&& arr[i][j]!= ' ')
|| ((arr[i + 1][j] == arr[i + 2][j])&& arr[i+1][j]!= ' '))
//列有兩個時進行攔截
{
srand((unsigned int)time(NULL));
i = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
if (((arr[1][1] == arr[2][2])&& arr[1][1]!= ' ')
|| ((arr[1][1] == arr[3][3])&& arr[1][1]!= ' ')
|| ((arr[2][2] == arr[3][3])&& arr[2][2]!= ' '))
//撇有兩個時進行攔截(捺未考慮,留做勝利後門)
{
srand((unsigned int)time(NULL));
i = rand() % 3;
j = i;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
srand((unsigned int)time(NULL));
i = rand() % 3;
j = rand() % 3;
if (' ' == arr[i][j])
{
arr[i][j] = '0';
return;
}
}
}
再次進行驗證:
電腦果然聰明瞭許多。