1. 程式人生 > 其它 >三子棋-支援N子棋-詳解(以及堵截玩家的方法設計)

三子棋-支援N子棋-詳解(以及堵截玩家的方法設計)

技術標籤:演算法演算法c語言

我可能寫的有些囉嗦,見諒,但我會很詳細的講解每一個步驟。

分析步驟

遊戲框架的構造(遊戲結構的設計)

1,遊戲需要選單
2,需要選項(進入遊戲或退出)
3,三子棋需要一個3*3的棋盤
4,需要將棋盤表現(打印出來)
5,玩家要開始下棋(輸入座標)
6,每次落子後都要判斷輸贏或平局
7,列印目前棋盤的狀況

8,電腦開始落子
9,每次落子後都要判斷輸贏或平局
10,列印目前棋盤的狀況
11.判斷遊戲目前的局面(輸或贏,繼續落子或者平局)

程式碼佈局設計

由於,程式碼量過大,並且各個函式的設計及作用。並且考慮N子棋的可能。我們需要分模組,分檔案來寫。

對遊戲佈局,初始選項的思考

遊戲選單-進入遊戲或者退出—分支語句(switch)
還要列印遊戲選單,使用函式。
為了多次play遊戲,也包括多次選項輸入,考慮do{}while();迴圈,可以至少進入一次迴圈。

void option(int input)
{
	switch (input)//分支語句
	{
	case 1:
		game();
//進入遊戲 break; case 0: printf("Logon out the game\n"); break; default: printf("Input error,please input again\n"); break; } } void menu(void) { printf("Welcome to game\n"); printf("\n"); printf("****************\n"); printf("*----1.play----*\n"
); printf("*----0.exit----*\n"); printf("****************\n"); } int main(void) { int input; //srand((unsigned int)time(NULL));時間戳,為電腦落子設定隨機數 do { menu();//列印遊戲選單 printf("please input option(1/0):>"); scanf("%d", &input); option(input);//選項判斷 } while (input); return 0; }

我們要求輸入數字1或0,如果輸入其他值,就會default結束,並進入while判斷,為,int型,再次迴圈。

game函式的實現

將上述遊戲設計實現

1,三子棋需要一個3*3的棋盤
2,需要將棋盤表現(打印出來)
3.玩家要開始下棋(輸入座標)
4.每次落子後都要判斷輸贏或平局
5,列印目前棋盤的狀況
6,電腦開始落子
7,每次落子後都要判斷輸贏或平局
8,列印目前棋盤的狀況

void game(void)
{
	char ret;
	char board[ROW][COL];//建立二維陣列
	setboard(board, ROW, COL);//製作棋盤
	while (1)
	{
		player(board);//玩家開始下棋
		ret=judgewinner(board);//判斷輸贏
		if (ret != 'C')
			break;
		computer(board);//電腦開始下棋
		ret=judgewinner(board);//判斷輸贏
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("The winner is player\n");
	else if (ret == '#')
		printf("The winner is computer\n");
	else if (ret == 'D')
		printf("Player and computer draw\n");

}

1,三子棋需要一個3*3的棋盤
畢竟是個3 * 3的棋盤,可以使用二維陣列。
char board[3][3]//這兩個行列可以使用巨集定義常量,方便延申N子棋。
#define ROW 3
#define COL 3
char board[ROW][COL]

2, 列印三子棋棋盤

大概是這樣的
在這裡插入圖片描述
二維陣列的元素也要被定義為空格。在後面玩家或電腦輸入後可以被重新定義為*號或#號。

void setboard(char board[ROW][COL], int row, int col)//製作第一個棋盤
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';//元素被定義為空格
		}
	}
	showboard(board,ROW, COL);//列印棋盤的函式
}
void showboard(char board[ROW][COL], int row, int col)//列印棋盤
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{

			printf(" %c ", board[i][j]);
			if (j < col)
				printf("|");
		}
		if (i < row)
			printf("\n---|---|---|\n");
	}
}

3,玩家落子

就是輸入座標

void player(char board[ROW][COL])//玩家下棋
{
	int x, y;
	while (1)
	{
		printf("please input the coordinate:>");
		scanf("%d %d", &x, &y);//輸入要落子的座標
		if (x>=1&&x<=ROW&&y>=1&&y<=COL)//判斷該座標處,是否未被輸入過
		{
			if (board[x - 1][y - 1] != ' ')
				printf("Coordinates occupied,please input again\n");
			else
			{
				board[x - 1][y - 1] = '*';
				break;
			}
		}
		else
			printf("Coordinate is illegal,please input again\n");//被使用過,重新輸入
	}
	//printf("*******************\n");
	printf("Player\n");
	showboard(board, ROW, COL);//列印每次下棋後的棋盤
}

由於玩家並不是程式設計師不認為,起點是0,所以賦值時要x-1,y-1.。
並且這是多次輸入,要保證輸入那個座標處不能已經輸入過,就是說要保證該陣列元素只能為空格。同時還要保證輸入座標要有效,必須在33的棋盤內,如果兩個問題有一個存在,必須要重新輸入,所以放入迴圈內,直到輸入正確,才會跳出迴圈。

4,先不考慮判斷輸贏,先列印當前棋盤,就是呼叫showboard(board, ROW, COL);函式,迴圈列印。

先保證電腦和玩家的棋盤輸入輸出正確再來判斷輸贏。

6,電腦開始落子,列印棋盤

暫時先使用隨機落子的演算法,下次我再寫堵截玩家落子的演算法

void computer(char board[ROW][COL])//電腦落子
{
	
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (board[x][y] == ' ')//判斷該座標處,是否未被輸入過
		{
			board[x][y] = '#';
			break;
		}
	}
	printf("Computer\n");
	//printf("*******************\n");
	showboard(board, ROW, COL);//列印每次下棋後的棋盤
}

前面已經使用srand();,保證了隨機數

int x = rand() % ROW;
int y = rand() % COL;

X y,為隨機數除以排或列的餘數,也就是說xy的值必定小於排,列的值,也就保證了xy,為 0,1,2
這也同樣保證了由三子棋走向N子棋的條件。
同樣得保證電腦落子的座標處不能已經使用過,必須得保證座標元素處為空格,才能輸入#。並且呼叫函式列印當前棋盤。

判斷棋盤的局面

呼叫char judgewinner(char board[ROW][COL])函式

char ifdraw(char board[ROW][COL])
{
	int i;
	for (i = 0; i < ROW; i++)
	{
		int j;
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
				return 'C';
		}
	}
	return 'D';
}
char judgewinner(char board[ROW][COL])
{
	int count;
	//判斷一行贏
	for (int i = 0; i < ROW; i++)
	{
		int j,count=0;
		for (j = 0; j < COL-1; j++)
		{
			if (board[i][j] != board[i][j + 1])
				break;
			else if (board[i][j] == board[i][j + 1])
				count++;
		}
		if (count == COL - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		if ((count != COL - 1) && (i == ROW - 1))
			return ifdraw(board);//平局
	}
	//判斷列贏
	for (int i = 0; i < COL; i++)
	{
		int j, count = 0;
		for (j = 0; j < ROW - 1; j++)
		{
			if (board[i][j] != board[i][j + 1])
				break;
			else if (board[i][j] == board[i][j + 1])
				count++;
		}
		if (count == ROW - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		if ((count != ROW - 1) && i == COL - 1)
			return ifdraw(board);//平局
	}
	//判斷對角線\贏
	count = 0;
	int i = 0;
	int j;
	while ((count != COL - 1) && (i <ROW-1))
	{
		j = i;
		if (board[i][j] == board[i + 1][j + 1])
			count++;
		else if (board[i][j] != board[i + 1][j + 1])
			break;
		i++;
		if (count == ROW - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
	}
	
	return ifdraw(board);//平局}
		
	
	//對角線/
	count=0;
	i = ROW - 1;
		
	while (i>0)
	{
		j=ROW-i-1;
		if (board[i][j] == board[i - 1][j + 1])
			count++;
		else if (board[i][j] = board[i - 1][j + 1])
			break;
		i--;
		if (count == COL - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		
	}
	
	return ifdraw(board);//平局
	
}

判斷一行或者一列的棋子都相等的情況下,我們使用l兩個for迴圈,我們同時定義一個控制變數。外迴圈,保證每一個行或每列都能覆蓋。內迴圈來確定每一行或每一列上的元素,相鄰之間是否相等?如果相等,count加一,不相等就可以確定,不存在決定當前棋局的條件,直接跳出。同樣支援N子棋的判斷。

對於左斜線以及右斜線上的三指相的條件,這個就比較簡單,,不需要一遍遍的遍歷,只需要找到特定的三個元素去判斷是否相等即可得出條件。

用這些迴圈條件來判斷,只能得出是否是玩家贏還是電腦贏,但是還需要新增一個函式來判斷是否是平局或者棋局未滿,可以繼續落子。

char ifdraw(char board[ROW][COL])

判斷平局或繼續落子。

用兩個for迴圈去遍歷陣列只要判斷出有一個元素是空格隔那麼就返回c就可以直接得出可以繼續落子,就需要返回來D來決定已經平局無法再落子。
因為一旦進入這卡數就表明已經判斷出無法決定誰勝或者誰負,只能繼續走兩條路一就是平局,二就是繼續落子

同時,設定了ret來接受judgewinner(char board[ROW][COL])函式的返回值,來判斷是否可以繼續迴圈落子。

計算機堵截玩家的演算法設計:思路

遊戲的規則是行列斜線,三種情況下,只要有一種情況,該線上都歸屬於三個同樣的旗子,即可判斷輸贏
我們可以考慮迴圈遍歷
依靠迴圈進入每行每列,每個斜線,進行每個元素的遍歷,如果該行該列或該斜線上有兩個相同的元素,立馬在第三個元素上進行,若無論是否與自己相同.

該思路目前只適用於三字棋,也可延伸到四子棋,4則是兩個起步,如果該列該行或該切線上有兩個以上的相同元素,就立馬在第三或第四個元素中進行落子

以下便是為拆開講解的全部程式碼。

三子棋

主函式介面

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "game.h"
#define ROW 3
#define COL 3
//返回值
//*
//#
//C-繼續
//D-平局
void game(void)
{
	char ret;
	char board[ROW][COL];//建立二維陣列
	setboard(board, ROW, COL);//製作棋盤
	while (1)
	{
		player(board);//玩家開始下棋
		ret=judgewinner(board);//判斷輸贏
		if (ret != 'C')
			break;
		computer(board);//電腦開始下棋
		ret=judgewinner(board);//判斷輸贏
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("The winner is player\n");
	else if (ret == '#')
		printf("The winner is computer\n");
	else if (ret == 'D')
		printf("Player and computer draw\n");

}
void option(int input)
{
	switch (input)//分支語句
	{
	case 1:
		game();
		break;
	case 0:
		printf("Logon out the game\n");
		break;
	default:
		printf("Input error,please input again\n");
		break;
	}
}
void menu(void)
{
	printf("Welcome to game\n");
	printf("\n");
	printf("****************\n");
	printf("*----1.play----*\n");
	printf("*----0.exit----*\n");
	printf("****************\n");
}
int main(void)
{
	int input;
	srand((unsigned int)time(NULL));
	do
	{
		menu();//列印遊戲選單
		printf("please input option(1/0):>");
		scanf("%d", &input);
		option(input);//選項判斷
	} while (input);
}

game.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "game.h"
//void game(void)
//{
//	char board[ROW][COL];//建立二維陣列
//	setboard(board, ROW, COL);//製作棋盤
//	player(board);//玩家開始下棋
//
//	computer(board);//電腦開始下棋
//
//}
void setboard(char board[ROW][COL], int row, int col)//製作第一個棋盤
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
	showboard(board,ROW, COL);
}
void showboard(char board[ROW][COL], int row, int col)//列印棋盤
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{

			printf(" %c ", board[i][j]);
			if (j < col)
				printf("|");
		}
		if (i < row)
			printf("\n---|---|---|\n");
	}
}
void player(char board[ROW][COL])//玩家下棋
{
	int x, y;
	while (1)
	{
		printf("please input the coordinate:>");
		scanf("%d %d", &x, &y);//輸入要落子的座標
		if (x>=1&&x<=ROW&&y>=1&&y<=COL)//判斷該座標處,是否未被輸入過
		{
			if (board[x - 1][y - 1] != ' ')
				printf("Coordinates occupied,please input again\n");
			else
			{
				board[x - 1][y - 1] = '*';
				break;
			}
		}
		else
			printf("Coordinate is illegal,please input again\n");//被使用過,重新輸入
	}
	//printf("*******************\n");
	printf("Player\n");
	showboard(board, ROW, COL);//列印每次下棋後的棋盤
}
void computer(char board[ROW][COL])//電腦落子
{
	
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (board[x][y] == ' ')//判斷該座標處,是否未被輸入過
		{
			board[x][y] = '#';
			break;
		}
	}
	printf("Computer\n");
	//printf("*******************\n");
	showboard(board, ROW, COL);//列印每次下棋後的棋盤
}
char ifdraw(char board[ROW][COL])
{
	int i;
	for (i = 0; i < ROW; i++)
	{
		int j;
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
				return 'C';
		}
	}
	return 'D';
}
char judgewinner(char board[ROW][COL])
{
	int count;
	//判斷一行贏
	for (int i = 0; i < ROW; i++)
	{
		int j,count=0;
		for (j = 0; j < COL-1; j++)
		{
			if (board[i][j] != board[i][j + 1])
				break;
			else if (board[i][j] == board[i][j + 1])
				count++;
		}
		if (count == COL - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		if ((count != COL - 1) && (i == ROW - 1))
			return ifdraw(board);//平局
	}
	//判斷列贏
	for (int i = 0; i < COL; i++)
	{
		int j, count = 0;
		for (j = 0; j < ROW - 1; j++)
		{
			if (board[i][j] != board[i][j + 1])
				break;
			else if (board[i][j] == board[i][j + 1])
				count++;
		}
		if (count == ROW - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		if ((count != ROW - 1) && i == COL - 1)
			return ifdraw(board);//平局
	}
	//判斷對角線\贏
	count = 0;
	int i = 0;
	int j;
	while ((count != COL - 1) && (i <ROW-1))
	{
		j = i;
		if (board[i][j] == board[i + 1][j + 1])
			count++;
		else if (board[i][j] != board[i + 1][j + 1])
			break;
		i++;
		if (count == ROW - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
	}
	
	return ifdraw(board);//平局}
		
	
	//對角線/
	count=0;
	i = ROW - 1;
		
	while (i>0)
	{
		j=ROW-i-1;
		if (board[i][j] == board[i - 1][j + 1])
			count++;
		else if (board[i][j] = board[i - 1][j + 1])
			break;
		i--;
		if (count == COL - 1)
		{
			if (board[i][j] == '*')
				return '*';//玩家贏
			else if (board[i][j] == '#')
				return '#';//電腦贏
		}
		
	}
	
	return ifdraw(board);//平局
	
}

game.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3
void game(void);//三子棋遊戲
void setboard(char board[ROW][COL], int row, int col);//建立棋盤
void showboard(char board[ROW][COL], int row, int col);//列印、展示棋盤
void player(char board[ROW][COL]);//玩家落子
void computer(char board[ROW][COL]);//電腦落子
char judgewinner(char board[ROW][COL]);//判斷輸贏
char ifdraw(char board[ROW][COL]);//判斷是否平局