最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言實現(xiàn)三子棋游戲的示例代碼

 更新時間:2022年10月12日 14:16:58   作者:努力學(xué)習(xí)游泳的魚  
今天我們將會用C語言實現(xiàn)三子棋。所謂三子棋,就是三行三列的棋盤,玩家可以和電腦下棋,率先連成三個的獲勝。話不多說,我們開始吧

1. 前言

大家好,我是努力學(xué)習(xí)游泳的魚,今天我們會用C語言實現(xiàn)三子棋。所謂三子棋,就是三行三列的棋盤,玩家可以和電腦下棋,率先連成三個的獲勝。話不多說,我們開始吧。

2. 準(zhǔn)備工作

我們可以在一個項目中創(chuàng)建三個文件,分別是:

  • test.c,測試游戲的邏輯。
  • game.c,游戲的實現(xiàn)。
  • game.h,函數(shù)聲明,符號的定義。

測試這個游戲時,我們玩一把肯定不過癮,所以需要使用do while循環(huán),每次可以選擇繼續(xù)玩或者退出游戲。先把大致的框架搭出來。

#include <stdio.h>

void menu()
{
	printf("****************************\n");
	printf("********  1. play     ******\n");
	printf("********  0. exit     ******\n");
	printf("****************************\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("請選擇(1/0):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			break;
		case 0:
			printf("退出游戲\n");
			break;
		default:
			printf("選擇錯誤\n");
			break;
		}
	} while (input);

	return 0;
}

當(dāng)然,我們的游戲不可能只是打印“三子棋”這三個字這么簡單,具體的實現(xiàn)我們會封裝成一個函數(shù),暫且取名為game。

3. 使用二維數(shù)組存儲下棋的數(shù)據(jù)

當(dāng)我們下三子棋的時候,需要把下棋的數(shù)據(jù)存起來。由于三子棋的棋盤是3×3的,我們就需要一個三行三列的數(shù)組來存儲下棋的數(shù)據(jù)。char board[3][3] = { 0 };

4. 初始化棋盤為全空格

當(dāng)我們還沒開始下棋時,棋盤上應(yīng)該啥都沒有,但是真的是啥都沒有嗎?事實上,如果我們打印棋盤時,能打印出“沒有棋子”的效果,數(shù)組里應(yīng)該是全空格。所以,我們需要寫一個函數(shù),初始化棋盤為全空格。InitBoard(board, 3, 3);這個函數(shù)的聲明,我們會放在game.h里。具體的實現(xiàn),我們會放在game.c里。以下的函數(shù)同理。該函數(shù)的聲明:void InitBoard(char board[3][3], int row, int col);(以下的函數(shù)均省略聲明)。該函數(shù)的實現(xiàn),只需遍歷這個二維數(shù)組,全部賦值為空格。

void InitBoard(char board[3][3], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			board[i][j] = ' ';
		}
	}
}

有沒有發(fā)現(xiàn),這個程序中,到處都要用到數(shù)組“三行三列”這個特點。如果我們想要改變這一點,比如改成五行五列,就需要改很多地方,非常麻煩。怎么解決這個問題呢?我們可以在game.h里定義兩個常量ROW和COL,這樣每次只需要改變#define后的值就行了。

#define ROW 3
#define COL 3

這樣以上出現(xiàn)的所有3都可以用ROW和COL替代了(此處省略)。注意:如果想使用game.h里的符號,我們需要在game.c和test.c里引用這個頭文件。引用自己寫的頭文件要用雙引號。#include "game.h"而對于其他頭文件如stdio.h,我們不需要在game.c和test.c里包含兩次,只需在game.h里包含就行了。

5. 打印棋盤

我們初始化棋盤后,會想要看一看棋盤長啥樣,所以接下來寫一個打印棋盤的函數(shù)DisplayBoard(board, ROW, COL);

如果你認(rèn)為打印出數(shù)組的數(shù)據(jù)就行了,從而這樣寫:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			printf("%c", board[i][j]);
		}
		printf("\n");
	}
}

實際運行時,你會發(fā)現(xiàn),打印了,但沒完全打印。

事實上,此時打印的是一堆空格,非常難看。如果我們想打印得好看點,可以考慮加上一些橫向和豎向的分割。比如我設(shè)想了這樣一種打印的效果:

   |   |   
---|---|---
   |   |   
---|---|---
   |   |   

假設(shè)把

   |   |   
---|---|---

當(dāng)成一組,總共需要打印3組,為什么呢?因為有3行。

每一組里面,又分為數(shù)據(jù)行和分割行。我們需要先打印數(shù)據(jù)行,再打印分割行。

   |   |    // 數(shù)據(jù)行
---|---|--- // 分割行

所以一個簡單的想法是這么寫:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印數(shù)據(jù)
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		// 打印分割的行
		printf("---|---|---\n");
	}
}

效果如下:

我們發(fā)現(xiàn)多打印了一行分割行,所以打印分割行時要加一條判斷,不是最后一行才打印分割行。

if (i < row - 1)
	printf("---|---|---\n");

這樣子打印就好看多了。

但是這么寫的話,就相當(dāng)于,一行一定要打印3列,后面就沒有改變的可能了。比如我把ROW和COL都改成5,效果如下:

所以這種寫法還是不夠好。最好的寫法是,再用一層循環(huán)控制列的打印。比如對于數(shù)據(jù)行:

   |   |   

我們可以把

   |  

當(dāng)成一組數(shù)據(jù),打印三組,最后一組就不用加上|了(同上一種寫法用一個if語句來控制)。

對于分割行

---|---|---

我們可以把

---|

當(dāng)成一組數(shù)據(jù),打印三組,最后一組就不用加上|了(還是用if語句來控制)。

實現(xiàn)如下:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印數(shù)據(jù)
		int j = 0;
		for (; j < col; ++j)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		// 打印分割的行
		if (i < row - 1)
		{
			for (j = 0; j < col; ++j)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

效果也沒問題:

若把ROW和COL改成10,打印出來的效果如下:

這樣寫出來的代碼就比較通用,具有比較強的可維護性。

6. 玩家下棋

接下來寫玩家下棋的函數(shù)。player_move(board, ROW, COL);

先讓玩家輸入坐標(biāo),若x和y都在1到3之間,則輸入的坐標(biāo)合法,在數(shù)組對應(yīng)的位置是board[x-1][y-1],若該位置仍然是空格,則這個位置沒有被下過,就把數(shù)組的這個元素改成*。由于若玩家輸入的坐標(biāo)非法或者被占用時,需要重新輸入,故需要一個循環(huán)。

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("請輸入坐標(biāo):>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			// 下棋
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("該坐標(biāo)被占用,請重新輸入\n");
			}
		}
		else
		{
			printf("坐標(biāo)非法,請重新輸入\n");
		}
	}
}

玩家下棋后再把棋盤打印一下DisplayBoard(board, ROW, COL);效果如下:

7. 電腦下棋

玩家下完棋后就輪到電腦下棋computer_move(board, ROW, COL);

我們讓電腦隨機下棋,只需生成兩個隨機的坐標(biāo)x和y,若該位置是空格,就下在這個位置,如果是空格,那就再次產(chǎn)生隨機坐標(biāo),可見,這也需要一個循環(huán)來實現(xiàn)。

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("電腦下棋\n");
	while (1)
	{
		x = rand() % row; // 0~2
		y = rand() % col; // 0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

使用rand函數(shù)前需要使用srand函數(shù)來設(shè)置隨機數(shù)生成器的起點。我們需要給srand函數(shù)傳一個時間戳,這就要用到time函數(shù)。srand((unsigned int)time(NULL));使用rand和srand都需要引用頭文件stdlib.h,使用time函數(shù)需要引用頭文件time.h。

我們用一個循環(huán),就能實現(xiàn)玩家和電腦輪流下棋的效果。

while (1)
{
	// 玩家下棋
	player_move(board, ROW, COL);
	DisplayBoard(board, ROW, COL);

	// 電腦下棋
	computer_move(board, ROW, COL); // 隨機下棋
	DisplayBoard(board, ROW, COL);
}

8. 判斷輸贏

什么時候游戲就結(jié)束了呢?如果玩家贏了,或者電腦贏了,或者平局,游戲就結(jié)束了,否則游戲繼續(xù)。

我們來設(shè)計一個is_win函數(shù)來判斷棋局是上面四種狀態(tài)的哪一種。我們這么設(shè)計is_win函數(shù)的返回值:

  • 玩家贏 - ‘*’
  • 電腦贏 - ‘#’
  • 平局 - ‘Q’
  • 繼續(xù) - ‘C’

當(dāng)棋局狀態(tài)不是C時說明游戲結(jié)束了,就跳出循環(huán),接著根據(jù)不同情況打印不同的結(jié)果。

char ret = 0;
while (1)
{
	// 玩家下棋
	player_move(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	// 判斷輸贏
	ret = is_win(board, ROW, COL);
	if (ret != 'C')
	{
		break;
	}
	// 電腦下棋
	computer_move(board, ROW, COL); // 隨機下棋
	DisplayBoard(board, ROW, COL);
	ret = is_win(board, ROW, COL);
	if (ret != 'C')
	{
		break;
	}
}
if (ret == '*')
{
	printf("玩家贏\n");
}
else if (ret == '#')
{
	printf("電腦贏\n");
}
else
{
	printf("平局\n");
}

如何實現(xiàn)is_win函數(shù)呢?只需判斷每行,每列,每條對角線是否有三個同樣的棋子就行了。

先判斷行(由于玩家贏和電腦贏都是返回對應(yīng)的棋子——*和#,所以直接把對應(yīng)的數(shù)組元素返回就行了。):

int i = 0;
for (; i < row; ++i)
{
	if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
	{
		return board[i][1];
	}
}

判斷列同理:

for (i = 0; i < col; ++i)
{
	if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
	{
		return board[1][i];
	}
}

最后判斷對角線:

if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
	return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
	return board[1][1];
}

如果有人贏了,在前面的代碼中就會返回。如果沒人贏,再來判斷是否平局。如果棋盤沒有空格了,那就是平局。我們可以寫一個is_full函數(shù)來判斷棋盤是否滿了。如果滿了就返回1,沒滿就返回0。

由于is_full函數(shù)只是寫給is_win函數(shù)的,只需要在game.c這個文件內(nèi)使用,所以加上static。具體的實現(xiàn),只需要遍歷數(shù)組,若發(fā)現(xiàn)空格,說明棋盤沒滿,就返回0,否則返回1。

而如果沒人贏,也不是平局,則游戲繼續(xù),return 'C';即可。

9. 效果展示

寫到這,我們就把三子棋程序?qū)懲昀病=酉聛砜纯葱Ч?/p>

玩家贏:

電腦贏:

平局:

10. 完整代碼

下面是完整的代碼:

game.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 3
#define COL 3

// 初始化棋盤
void InitBoard(char board[ROW][COL], int row, int col);

// 打印棋盤
void DisplayBoard(char board[ROW][COL], int row, int col);

// 玩家下棋
void player_move(char board[ROW][COL], int row, int col);

// 電腦下棋
void computer_move(char board[ROW][COL], int row, int col);

// 判斷輸贏
char is_win(char board[ROW][COL], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			board[i][j] = ' ';
		}
	}
}

//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (; i < row; ++i)
//	{
//		int j = 0;
//		for (; j < col; ++j)
//		{
//			printf("%c", board[i][j]);
//		}
//		printf("\n");
//	}
//}

//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (; i < row; ++i)
//	{
//		// 打印數(shù)據(jù)
//		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//		// 打印分割的行
//		if (i < row - 1)
//			printf("---|---|---\n");
//	}
//}

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		// 打印數(shù)據(jù)
		int j = 0;
		for (; j < col; ++j)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		// 打印分割的行
		if (i < row - 1)
		{
			for (j = 0; j < col; ++j)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("請輸入坐標(biāo):>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			// 下棋
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("該坐標(biāo)被占用,請重新輸入\n");
			}
		}
		else
		{
			printf("坐標(biāo)非法,請重新輸入\n");
		}
	}
}

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("電腦下棋\n");
	while (1)
	{
		x = rand() % row; // 0~2
		y = rand() % col; // 0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (; i < row; ++i)
	{
		int j = 0;
		for (; j < col; ++j)
		{
			if (board[i][j] == ' ')
			{
				return 0; // 沒有滿
			}
		}
	}
	return 1; // 滿了
}

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	// 判斷行
	for (; i < row; ++i)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return board[i][1];
		}
	}
	// 判斷列
	for (i = 0; i < col; ++i)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}
	// 對角線
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	// 判斷平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	// 繼續(xù)
	return 'C';
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
	printf("****************************\n");
	printf("********  1. play     ******\n");
	printf("********  0. exit     ******\n");
	printf("****************************\n");
}

void game()
{
	// 三子棋小游戲的具體實現(xiàn)
	char ret = 0;
	// 存放下棋的數(shù)據(jù)
	char board[ROW][COL] = { 0 };
	// 初始化棋盤為全空格
	InitBoard(board, ROW, COL);
	// 打印棋盤
	DisplayBoard(board, ROW, COL);

	while (1)
	{
		// 玩家下棋
		player_move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		// 判斷輸贏
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		// 電腦下棋
		computer_move(board, ROW, COL); // 隨機下棋
		DisplayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家贏\n");
	}
	else if (ret == '#')
	{
		printf("電腦贏\n");
	}
	else
	{
		printf("平局\n");
	}
	//DisplayBoard(board, ROW, COL);
}

//
// 什么時候,游戲就結(jié)束了
// 玩家贏 - '*'
// 電腦贏 - '#'
// 平局   - 'Q'
// 繼續(xù)   - 'C'
//
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("請選擇(1/0):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戲\n");
			break;
		default:
			printf("選擇錯誤\n");
			break;
		}
	} while (input);

	return 0;
}

到此這篇關(guān)于C語言實現(xiàn)三子棋游戲的示例代碼的文章就介紹到這了,更多相關(guān)C語言三子棋游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

平昌县| 泗水县| 扶绥县| 景泰县| 沈丘县| 黔江区| 航空| 斗六市| 文山县| 东丽区| 磴口县| 米易县| 黔南| 霍山县| 弥渡县| 霞浦县| 长寿区| 久治县| 莱西市| 仁怀市| 耿马| 固安县| 泗阳县| 上杭县| 安塞县| 华阴市| 洛川县| 垦利县| 五台县| 久治县| 长葛市| 石城县| 库伦旗| 北京市| 当雄县| 靖宇县| 房产| 淮南市| 正蓝旗| 远安县| 宽甸|