目录
一.文章概述
本文将深入探讨用C语言实现一个经典的双人策略游戏——三子棋(Tic Tac Toe)。我们不仅会详细解释游戏规则、数据结构选择,还会阐述如何设计和编码游戏的逻辑流程。通过本篇文章,读者能够理解如何从概念分析到具体代码实现,一步步构建出完整的三子棋游戏。
二.游戏规则概述
三子棋是一种经典的两人策略游戏,通常在一个3x3(本次代码可以随意更改棋盘大小)的网格上进行。玩家轮流放置他们的标记(这里代码玩家下棋用“*”,电脑下棋用“#”来示例),目的是在横行、竖列或斜对角线上获得三个连续的标记及为获胜。
1.一个玩家形成一条直线上的三个标记。
2.所有的格子都被占用(平局)。
三.理解思路
1. 定义游戏数据结构
创建一个二维数组来表示棋盘。使用字符型数组
//存放数据需要一个3*3的二维数组
char board[ROW][COL] = {0};
来存储每个位置的棋子,其中 ‘’, ‘*’, 和 ‘#’分别代表空位、玩家和电脑。
2. 游戏搭建思路及其步骤
·菜单选择列表:
//声明菜单是menu定义格式
void menu()
{
printf("*******************************\n");
printf("******** 1. play ******\n");
printf("******** 0. exit ******\n");
printf("*******************************\n");
}
·初始化棋盘:所有位置均为空格
//初始化棋盘
InitBoard(board, ROW, COL);
//方法一
void InitBoard(char board[ROW][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 InitBoard(char board[ROW][COL], int row, int col)
{
memset(&board[0][0], ' ', row*col*sizeof(board[0][0]));
}
·创建棋盘样式
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 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");
}
}
·设置玩家下棋
使用循环结构(如 while
循环)来处理玩家和电脑输入,询问玩家和电脑选择的位置并验证。通常需要一个函数来接受用户输入的行和列,并转换为有效的数组索引。
//判断玩家下棋输入坐标是否符合要求
//若符合要求,则判断该位置是否是没有超出棋盘范围的
//若没有超出且该位置是空的,玩家就下棋
//为了能达成赢,输,平局三种其一的局面
//需要while不停的循环
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋:>\n");
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 ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋>:\n");
while (1)
{
//假如row=9
//rand()%row=0-8
//那么+1就是1-9
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
·检查游戏状态:
需要有函数来检查游戏是否结束(是否有胜者或平局)和验证下棋操作的有效性(确保不是重复落子、非空位等)。
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//判断行
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
for (i = 0; i < col; i++)
{
//判断列
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][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 (IsFull(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
int IsFull(char board[ROW][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] == ' ')
{
//没有空格子了就直接返回0终止程序
return 0;
}
}
}
//继续下棋
return 1;
}
四.代码示例
一.game.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
int i,j;
//方法一
//for (i = 0; i < row; i++)
//{
// for (j = 0; j < col; j++)
// {
// //刚开始棋盘上是什么都没有的
// board[i][j]=' ';
// }
//}
//方法二 包含头文件#include "string.h"
//函数规定将&/*部分指向的地方前多大空间转化为空格(' ')
memset(&board[0][0], ' ', row * col * sizeof(board[0][0]));
}
//void DispalyBoard(char board[ROW][COL], int row, int col)
//{
// int i, j;
//
// for (i = 0; i < row; i++)
// {
// //写死了只能打印3列
// printf(" %c | %c | %c \n",board[i][0], board[i][1], board[i][2]);
// if (i < row - 1)
// {
// printf("---|---|---\n");
// }
// }
//
//}
void DispalyBoard(char board[ROW][COL], int row, int col)
{
int i, j;
//灵活变换的棋盘
for (i = 0; i < row; i++)
{
//把一个字符和一个竖杠看作一组
//---|---|---
//那么最后一组没有‘|’
//先打印数据和|
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 PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋\n");
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 ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
//随机函数
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] = ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断棋盘是否满了
int Is_Full(char board[ROW][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;
}
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//判断行是否连起来
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
///判断主副对角线
//为什么要是board[1][1] != ' ',board[0][0] != ' '不可以吗,前面不都是判断了board[1][1] ==board[0][0]
//因为他是中间相互公共部分,是00或者22的话
//意思就是如果要改的话,主副对角线就要检查对角位置就可以了,用中心检查是为了提高代码效率而已
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))
{
return 'Q';
}
return 'C';
}
二.game.h部分
#pragma once
//头文件的包含
#include <string.h>
#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char IsWin(char board[ROW][COL],int row, int col);
三.test.c部分
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("***** 请 选 择 *****\n");
printf("***** 1.play 0.exit *****\n");
printf("**************************\n");
}
void game()
{
//如果是这样的话,确实打印出了棋盘
//但是直接打印的话那么如何落子输入?
/*printf(" | | \n");
printf("--|--|--\n");
printf("__|__|__\n");
printf(" | | \n");*/
//运用数组解决问题
//存放一个3*3的数组
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DispalyBoard(board, ROW, COL);
while (1)
{
//下棋
//玩家下
PlayerMove(board, ROW, COL);
//打印
DispalyBoard(board, ROW, COL);
//判断输赢
ret=IsWin(board, ROW, COL);
if (ret != 'C')
{
//在这里不判断谁赢
//当不平局直接跳出,再进行判断
//节省了代码
break;
}
//电脑下
ComputerMove(board, ROW, COL);
//打印
DispalyBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if ('*' == ret)
{
printf("玩家赢\n");
}
else if ('#' == ret )
{
printf("电脑赢\n");
}
else if ('C' == ret)
{
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;
}
结论
这个简单的C语言程序实现了三子棋的基本功能:定义游戏数据结构、游戏搭建思路及其步骤、检查胜利条件和切换玩家等。使用标准输入输出函数进行人机交互,并通过数组来管理棋盘状态。
我期待这篇博客示例能为你提供宝贵的思路和指导,在使用C语言构建三子棋游戏的过程中遇到问题时找到有效的解决方案。在这段旅程中,请记住,正确地挑选并应用合适的函数是关键所在——这一点在实际编程过程中尤为重要。通过合理选择这些功能,你可以确保程序不仅高效而且易于维护。希望这个资源能成为你编程之旅中的得力助手,并激发你进一步探索和优化代码的热情。