1.题目描述
- 三子棋,民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙等。
- 将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。
2.功能实现
想要用c语言完成,就得一步一步将所需实现的功能拆开,逐步实现(切记不可一步到位)。
1.开始界面
可以用一系列输出函数将开始界面进行输出,1为开始游戏,0为退出游戏,同时在主函数里进行调用,为了防止输入一次就结束运行,可用do while循环,先循环一次,再根据输入的是1还是0判断是否继续进行循环。
开始菜单函数。
代码语言:javascript复制void menu() {
printf("***************************n");
printf("**********1.play**********n");
printf("**********0.exit***********n");
printf("***************************n");
}
主函数
代码语言:javascript复制int main() {
int input;
srand((unsigned int)time(NULL));\用于随机数的生成器,后面会有解释
do { \先进行一次循环,再根据input值进行判断是否继续
menu();
printf("请选择:n");
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("选择错误,请重新选择n");
break;
}
} while (input);
return 0;
}
有了开始菜单和输入功能,接下来就可以进行游戏功能实现了。
2.游戏功能
开始游戏功能实现前,我们需要明确它需要实现的功能有哪些。既然下棋,就得有棋盘吧,还得有旗子吧,最后还得有对手吧,这些我们来一一明确。
1.棋盘
可以知道的是,棋盘是一个3*3的矩阵方格,由纵线和横线划分成每一个方格。这里可以用一个二维数组来存放,在这里我们采用空格存放来达到初始化棋盘的目的,并在每一个数组的位置打印相应的线段来达到划分棋盘的效果,而棋盘的头部和尾部不需要进行划分线,所以在遍历时进行判断是否到达底部,否则打印。
代码语言:javascript复制void Initboard(char board[ROW][COL], int row,int col) {
int i = 0;
for (i = 0; i < row; i ) {
for (int j = 0; j < col; j ) {
board[i][j] = ' ' ;
}
}
}
void Displayboard(char board[ROW][COL], int row, int col) {
int i = 0;
for (i = 0; i < row; i ) {
//先打印数据
printf(" %c | %c | %c n", board[i][0], board[i][1], board[i][2]);
//打印分割行
if(i<row-1) printf("---|---|---n");//只打印前两行
}
printf("n");
}
细心的老铁们可能已经看到,打印棋盘的代码只能满足3*3的棋盘,万一以后需要扩大玩法的话,效率并不是很高,所以我们直接进行对于所传入数组(行与列)进行遍历与判断,先打印数据,在打印分割行,这样就可以更加灵活与方便的进行棋盘的调整。
代码语言:javascript复制void Displayboard(char board[ROW][COL], int row, int col) {
int i = 0;
for (i = 0; i < row; i ) {
//先打印数据
for (int j = 0; j < col; j ) {
printf(" %c ", board[i][j]);
if (j < col - 1) printf("|");
}
printf("n");
//打印分割行
if (i < row - 1) {
for (int j = 0; j < col; j ) {
printf("---");
if (j < col - 1) printf("|");
}
}
printf("n");
}
}
2.下棋
(玩家用*表示,电脑用#表示)这里很清楚的是,我们得通过输入棋盘相应的坐标来达到下棋的目标。其次还要对该坐标进行判断,如果上面已经有棋子了,那就得重新下。这里要注意,编译器与我们所输入的坐标不同的区别在于电脑下标从0开始,而我们输入的是1开始(不能保证玩的都是程序员)所以修改的坐标也有讲究。
这是玩家。
代码语言:javascript复制void PlayerMove(char board[ROW][COL], int row, int col) {
int x, y;
printf("玩家下棋n");
while (1) {
printf("请输入要下棋的坐标:>");
scanf_s("%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");
}
}
}
电脑下棋
对于编译器来说,达到下棋的目的便是能够让他随机生成一个坐标,并往里面放入#。这里需要考虑的是1.如何生成随机数;2.控制生成坐标的范围;3.考虑该坐标上是否有棋子
1.针对随机数的情况,我们可以利用srand函数配合time函数(time函数的作用是生成一个时间戳,用于给srand提供可变参数,srand是随机数生成器)来生成随机数,利用rand函数生成相应坐标,为了控制范围,给生成数对行和列求余,同时进行判断相应坐标上是否有棋子
(ps:time函数和srand需要time.h和stalib.h头文件)
代码语言:javascript复制void ComputerMove(char board[ROW][COL], int row, int col) {
printf("电脑下棋n");
int x = 0;
int y = 0;
while (1) {
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = '#';
break;
}
}
}
3.判断结果
对于结果来说,无非三种,输、赢、平局。
这里采用了对返回值进行判断来输出相应的结果(输出*,玩家赢,#则电脑赢,否则平局)。
赢的条件,任意棋子满足横竖或对角线即可,我们可以对数组所存放的值与位置进行判断,如果满足,返回对应棋子,如果都没有满足,则进行平局判断,通过对里面的空格数进行判断,如果都没有,则说明棋盘已满,且同时没有满足输赢条件,输出平局。
代码语言:javascript复制static int is_full(char board[ROW][COL], int row, int col) {//判断平局函数
for (int i = 0; i < row; i ) {
for (int j = 0; j < col; j ) {
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
//判断输赢或平局或游戏继续
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 (is_full(board, row, col)) {
return 'Q';
}
return 'C';
}
相关代码
1.测试代码
代码语言:javascript复制#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
void menu() {
printf("***************************n");
printf("**********1.play**********n");
printf("**********0.exit***********n");
printf("***************************n");
}
void game() {
char ret = 0;
char board[ROW][COL];
Initboard(board, ROW, COL);
Displayboard(board, ROW, COL);
//下棋
while (1) {
PlayerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
//判断输赢
ret=Iswin(board,ROW,COL);
if (ret != 'C') {
break;
}
ComputerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
//判断输赢
if (ret != 'C') {
break;
}
}
if (ret == '*') {
printf("玩家赢n");
}
else if (ret == '#') {
printf("电脑赢n");
}
else {
printf("平局n");
}
}
int main() {
int input;
srand((unsigned int)time(NULL));
do {
menu();
printf("请选择:n");
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("选择错误,请重新选择n");
break;
}
} while (input);
return 0;
}
2.头文件代码
代码语言:javascript复制#pragma once
#define ROW 3
#define COL 3
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
//初始化棋盘
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);
编码相关小技巧
1.如果所需要的头文件过多,可以单独用一个头文件涵盖,在调用时就可以全部调用到,效率特高。
2.对于自定义函数功能的实现,也可以在自己的头文件里进行声明,也可大大提高效率。
3.如果编写的代码功能过多,可以一步一步来实现,切不可一步到位,从简到难更能加深思维的强化与理解。
(ps:新人博主,第一次写博客,还有些不熟悉,请多多指教(害羞))