三子棋
多文件实现,分装成三个模块。
声明部分 game.h
#ifndef _GAME_H_
#define _GAME_H_
#include<stdio.h>
#include<windows.h>
#pragma warning(disable:4996)
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
void game();
void InitBoard(char Board[][COL],int row,int col);
void ShowBoard(char Board[][COL], int row, int col);
void Player(char Board[][COL], int row, int col);
char Judge(char Board[][COL], int row, int col);
void Computer(char Board[][COL], int row, int col);
//player--0
//computer--x
//‘e’平局
// ‘n'--next
#endif
主架构逻辑
main.c
#include"game.h"
void menu()
{
printf("欢迎游戏参与三子棋游戏!\n");
printf("1--玩游戏 2--退出游戏\n");
printf("请输入你的选项:>\n");
}
int main()
{
int intput = 0;
srand((unsigned int)time(NULL));
do{
menu();
scanf("%d", &intput);
switch (intput)
{
case 1:
game();
break;
case 2:
intput = 0;
printf("成功退出游戏\n");
break;
default:
printf("输入有误,请重新输入\n");
}
}while (intput);
system("pause");
return 0;
}
游戏具体的实现部分
game.c
#include"game.h"
void InitBoard(char Board[][COL], int row, int col)
{
//memset函数
//memset(void*dest,int c,size_t count)
memset(Board, ' ', row*col*(sizeof(Board[0][0])));
//int i = 0;
//for (; i < row; i++)
//{
// int j = 0;
// for (; j < col; j++)
// {
// Board[i][j]=' ' ;
// }
//}
}
void ShowBoard(char Board[][COL], int row, int col)
{
//列号
int j = 0;
{
for (j=0; j < col+1; j++)
{
printf(" %d ", j);
}
}
printf("\n");
int i = 0;
for (; i < row; i++)
{
printf(" %d |", i + 1);
int j = 0;
for (; j < col; j++)
{
printf(" %c |",Board[i][j]);
}
printf("\n");
if (i < row )
{
for (j = 0; j <= col; j++)
{
printf("___|");
}
printf("\n");
}
}
}
void Player(char Board[][COL], int row, int col)
{
printf("请输入你所选择位置对应的坐标\n");
int x = 0;
int y = 0;
while (1){
scanf("%d %d", &x, &y);
//判断坐标合法性
//1不被占用
if (x > 0 && x <= row && y > 0 && y <= col )
{
if (Board[x - 1][y - 1] ==' ')
{
Board[x - 1][y - 1] = '0';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
continue;
}
}
//2没有出界
else
{
printf("输入坐标不合法,请重新输入!\n");
}
}
}
char Judge(char Board[][COL], int row, int col)
{
//任何一方赢的情况
int i = 0;
//判断一行
for (; i < row; i++)
{
if (Board[i][0] != ' ' && Board[i][0] == Board[i][1] && Board[i][1] == Board[i][2]){
return Board[i][0];
}
}
//判断一列
int j = 0;
for (j = 0; j < col; j++)
{
if (Board[0][j] != ' ' && Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j])
{
return Board[0][j];
}
}
//主对角线
if (Board[0][0] != ' ' && Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2])
{
return Board[0][0];
}
//副对角线
if (Board[0][2] != ' ' && Board[0][2] == Board[1][1] && Board[1][1] == Board[2][0])
{
return Board[1][1];
}
//还有空位置--继续
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (Board[i][j] == ' ')
{
return 'n';
}
}
}
//平局
return 'e';
}
void Computer(char Board[][COL], int row, int col)
{
do
{
int x = rand() % row;
int y = rand() % col;
//坐标一定是合法的,关键看有没有被占用
if (Board[x][y] == ' ')
{
Board[x][y] = 'x';
break;
}
//被占用的话就重新生成坐标
} while (1);//注意语法 do while 语句后有;
printf("电脑已运行完毕!\n");
}
void game()
{
//建立棋盘 赋值为空字符 注意类型
char Board[ROW][COL];
//初始化棋盘
InitBoard(Board, ROW, COL);//meyset 初始化另一种方式
ShowBoard(Board, ROW, COL);
printf("请玩家先走!\n");
//玩游戏阶段
char result = 'n';
while (1)
{
Player(Board, ROW, COL);
ShowBoard(Board, ROW, COL);
Judge(Board, ROW, COL);
result = Judge(Board, ROW, COL);
if (result != 'n')
{
break;
}
printf("轮到电脑下棋了!\n");
Sleep(1000);
Computer(Board, ROW, COL);
ShowBoard(Board, ROW, COL);
Judge(Board, ROW, COL);
result = Judge(Board, ROW, COL);
if (result != 'n')
{
break;
}
}
switch (result)
{
case'x':
printf("电脑赢!\n");
break;
case'0':
printf("玩家赢!\n");
break;
case'e':
printf("平局,没有一方赢得本次游戏!\n");
break;
default:
printf("hhh\n");
break;
}
system("pause");
}
代码解释
=每一个部分开始会先解释这一个部分的功能(或者说逻辑思维关系)之后会具体解释一些函数、语句的用法。
声明部分
主要为整个工程里面需要引用的头文件、定义的宏以及函数的声明。
1.//player–0
//computer–x
//‘e’平局
// ‘n’–next
不同字符所对应的意思
2.#define ROW 3
#define COL 3
动态实现棋盘的变化,代码可维护性比较高
3.在声明函数的部分,请注意是否需要返回值和传什么参。
(二维数组传参本质也是一个指针,故而传递的本质是传址调用)因此通过函数传递数组名—传地址——就可以给数组赋值。
主要框架逻辑
始终要注意的一点就是,系统要上,流程先行
主要框架要有一个流程图在心里,这要代码逻辑性才会强。(故而这也是此游戏最先搭建的部分)
1.do循环让用户自己选择是否进行下一次游戏,而不是一次游戏就结束。
2.用switch case 语句来实现分支选择的功能。
游戏具体的实现部分
写具体游戏之前,要先明白有这个游戏真正在干什么,有哪些需求
整理游戏需求如下:
实现让玩家一步电脑一步在棋盘里下棋并且判断每一步的运行结果。
此代码以玩家先行为例子:
----玩家通过坐标输入,选择下棋的位置(判断坐标输入)
----判断这一步棋对整个游戏的结果(赢了,输了,还是怎么样?当然下的第一步不存在这个问题)
-----电脑通过事先种好的随机种子生成随机数,生成坐标,下棋
-----判断运行结果
1.InitBoard 初始化棋盘部分
建议使用memset 函数,两个for循环也不是不可以,只是memset 一行就会解决。
循环部分不多介绍,主要来谈一下
Memset 函数
memset(void*dest,int c,size_t count)
voiddest 表示指针 ,从哪开始赋值
int c 把它们赋值成什么以及什么类型
size_t count 赋值的字节大小 ==元素个数每一个元素的字节 rowcol(sizeof(Board[0][0])))
2.ShowBoard 打印棋盘部分
高能预警!!!
先看一个图(因为我画图板技能为0,甚至为负数,所以谅解手画一个棋盘,方便且快速)
如下我把每一行这样的棋盘样式分解为下图(现在问题就分解为先打印下图这一部分,然后循环就行了)
实际上打印上图这样的棋盘
先打印空格 然后放字符 再打空格 再一次打印| 对应语句:printf(" %c |",Board[i][j]); 循环三次
换行打印
换行打印三个_一个 | 对应语句:printf(" ___|"); 循环三次
希望我苍白的语言能让你理解(哈哈哈哈哈)
3.Player 玩家下棋部分
判断坐标输入的合法性,以及是否此坐标被占用。若以上情况看都不满足则才可以对此位置赋值。
4.Judge 判断游戏结果部分
主要是
一赢 : ①一行三子连线
②一列三子连线
③主副对角线三子连线
二.无任何一方赢,并且仍有空位置,游戏继续
三.以上均不满足:平局(即棋盘被下完,没有一方胜利)
5.Computer 电脑下棋部分
主要是需要电脑随机生成坐标,并且此坐标没有被占用
在开始时,用时间作为随机数的种子
srand((unsigned int)time(NULL));
用生成随机数函数 注意:生成的随机数是有要求的,保证不会出界。(即必须保证数字 x的范围在0—row-1 ,y的范围在0—col-1。否则你生成任意一个大数字作为坐标,就数组出界)
int x = rand() % row;)
int y = rand() % col;
干货内容已经结束,下面部分可忽略不看
复盘
我不是一个逻辑思维特别强的人,所以以后会特别注重在写这种比较复杂的工程之前,先理清逻辑框架。
即使这样会花费很长时间。做不到边敲代码边能理清思路就一点点来!
在这种行数两三百行的代码中,细节真的非常重要,还是有发现一些平常学习不细致的地方。
比如关于任何一方下完棋之后,都要重新赋值rusult 。do{} while(); 语句后面的分号也常常被我遗漏。
大家彼此学习,相互参照!