目錄
前言
正文
主函數部分(test.c)
頭檔案部分(game.h)
功能實作函數部分(game.c)
1.初始化函數
2.列印棋盤函數
3.玩家移動函數
4.電腦移動函數
5.判斷輸赢函數
6.重回game函數
效果展示
總結
前言
經過一段時間的學習,分支與循環、數組與函數的相關知識已經足夠支撐我們完成一個小遊戲了,這個小遊戲不需要太多的功能,也不需要太複雜的邏輯,隻需要懂點循環與分支,懂點數組的使用以及函數如何傳參和返值即可。作為草稿紙上常出現的小遊戲,三子棋的邏輯可謂是非常簡單了,隻需要玩家走一步、電腦走一步,并在八種可能獲勝結果中比對就行了,下面讓我們一起來看看三子棋的實作。
注意:本文為模拟遊戲實作,代碼量兩百左右,共有兩個源檔案+一個頭檔案。
正文
本程式一共就下面三個檔案
主函數部分(test.c)
我們直接從主函數開始!
下面看看game(遊戲)函數的設計
//三子棋
#include"game.h"
void menu()
{
printf("**************************\n");
printf("******** 1.Play ********\n");
printf("******** 0.Exit ********\n");
printf("**************************\n");
}
void game()
{
char board[ROW][COL] = { 0 };//定義
init_board(board, ROW, COL);//初始化
display_board(board, ROW, COL);//列印
//下棋
char tmp = 0;
while (1)
{
player_move(board, ROW, COL);//玩家走
display_board(board, ROW, COL);//列印
tmp=who_win(board, ROW, COL);//判斷
if (tmp != 'C')
break;
computer_move(board, ROW, COL);//電腦走
display_board(board, ROW, COL);//列印
tmp=who_win(board, ROW, COL);//判斷
if (tmp != 'C')
break;
}
if (tmp == '*')
printf("玩家獲勝\n");
else if (tmp == '#')
printf("電腦獲勝\n");
else
printf("和局\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請輸入你的選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出遊戲!\n");
break;
default :
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (input);
return 0;
}
主函數檔案(test.c就介紹完了),下面介紹game.h這個頭檔案
頭檔案部分(game.h)
這個頭檔案是自己建立的,目的就是在主函數源檔案(test.c)和功能實作函數(game.c)中搭起一座溝通的橋梁。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
void init_board(char board[][COL],int row,int col);//初始化
void display_board(char board[][COL], int row, int col);//列印
void player_move(char board[][COL], int row, int col);//玩家走
void computer_move(char board[][COL], int row, int col);//電腦走
char who_win(char board[][COL], int row, int col);//判斷
自定義頭檔案部分代碼量最少,功能也比較簡單,如果說test.c是大腦、控制中心,那麼game.h就相當于神經系統的一部分,是函數間溝通的橋梁,能對函數起主導作用。
功能實作函數部分(game.c)
此部分是程式運作必不可少的部分,沒有它們,程式能跑,但什麼都幹不了,類似于植人。
本部分函數細節較多,是以我會分開逐個講解。
1.初始化函數
初始函數主要就是把整個數組周遊一遍,然後将字元 空格 (' ')賦給每個數組即可。
void init_board(char board[][COL], int row, int col)//初始化
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//初始化數組為空格' '
}
}
}
2.列印棋盤函數
列印棋盤函數需要思考下:什麼情況列印 | 和 什麼情況列印 --- ,不想清楚這些問題就無法列印出優美的棋盤。
void display_board(char board[][COL], int row, int col)//列印
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; 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");
}
}
}
3.玩家移動函數
玩家移動就是讓玩家輸入坐标,輸入坐标有三種情況:非法、占用、成功,依次判斷即可,其次要注意不是人人都知道坐标從0開始,是以在使用x、y時要減1,確定坐标不會越界。
void player_move(char board[][COL], int row, int col)//玩家走
{
int x = 0;
int y = 0;
while (1)
{
printf("請落子:>");
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("此坐标已被占用,重新輸入\n");
}
else
printf("坐标非法,重新輸入\n");
}
}
4.電腦移動函數
電腦移動不要太多判斷,隻需要給電腦一個随機值,一個判斷條件。
直到電腦落子成功再跳出即可
void computer_move(char board[][COL], int row, int col)//電腦走
{
printf("輪到電腦落子:>");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
printf("電腦已落子\n");
}
5.判斷輸赢函數
這個函數出現于玩家或電腦移動之後,目的就是判斷是否已達成勝利條件(共八種),如果達成了,就能跳出game函數中的死循環,結束遊戲。
注意:本程式中函數就判斷輸赢函數和判斷和局函數有傳回值,一個是char,一個是int
int place(char board[][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//排除所有情況
}
}
return 1;
}
char who_win(char board[][COL], int row, int col)//判斷
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] &&
board[i][1] == board[i][2] && board[i][2] != ' ')
return board[i][0];
}//判斷行
for (j = 0; j < row; j++)
{
if (board[0][j] == board[1][j] &&
board[1][j] == board[2][j] && board[2][j] != ' ')
return board[0][j];
}//判斷列
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] && board[2][2] != ' ')
return board[0][0];//判斷正斜
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] && board[2][0] != ' ')
return board[0][2];//判斷反斜
if (place(board, ROW, COL) == 1)
return 'Q';//判斷和局
return 'C';//預設選項
}
6.重回game函數
經過game.c中各函數的一頓操作,最終由判斷輸赢函數攜帶傳回值回到game函數進行判斷
效果展示
讓我們直接來看看效果如何:
可以看到三種情況都能實作,現在隻需要将代碼進行優化,主要是優化輸赢判斷部分,使其相容性更強,以後在玩五子棋時能沿用這套方案。
下面我将展示功能實作函數(game.c)的全部代碼:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_board(char board[][COL], int row, int col)//初始化
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//初始化數組為空格' '
}
}
}
void display_board(char board[][COL], int row, int col)//列印
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; 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[][COL], int row, int col)//玩家走
{
int x = 0;
int y = 0;
while (1)
{
printf("請落子:>");
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("此坐标已被占用,重新輸入\n");
}
else
printf("坐标非法,重新輸入\n");
}
}
void computer_move(char board[][COL], int row, int col)//電腦走
{
printf("輪到電腦落子:>");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
printf("電腦已落子\n");
}
int place(char board[][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//排除所有情況
}
}
return 1;
}
char who_win(char board[][COL], int row, int col)//判斷
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] &&
board[i][1] == board[i][2] && board[i][2] != ' ')
return board[i][0];
}//判斷行
for (j = 0; j < row; j++)
{
if (board[0][j] == board[1][j] &&
board[1][j] == board[2][j] && board[2][j] != ' ')
return board[0][j];
}//判斷列
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] && board[2][2] != ' ')
return board[0][0];//判斷正斜
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] && board[2][0] != ' ')
return board[0][2];//判斷反斜
if (place(board, ROW, COL) == 1)
return 'Q';//判斷和局
return 'C';//預設選項
}
總結
我們本次三子棋之旅到這就要結束了,說實在的,三子棋這段代碼蘊含邏輯比較簡單,唯一需要注意的就是棋盤列印和電腦随機值,隻有了解透徹了,代碼敲起來才能得心應手!