首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用C语言写个能“玩”的三子棋游戏!——完整源码+逻辑详解+扩展方向

用C语言写个能“玩”的三子棋游戏!——完整源码+逻辑详解+扩展方向

作者头像
Extreme35
发布2025-12-23 18:04:11
发布2025-12-23 18:04:11
1740
举报
文章被收录于专栏:DLDL

C 语言实现“三子棋”(井字棋)——从 0 到 1 的完整实战


1. 游戏规则与交互说明

  • 棋盘为 3×3,玩家与电脑轮流在空位落子。
  • 玩家落子符号:*;电脑落子符号:#;空位用空格 ' ' 表示。
  • 任意行 / 列 / 对角线先连成三个相同棋子者获胜;棋盘被下满仍无胜者则为平局
  • 控制台交互流程:
    1. 打开程序 → 菜单(1=开始游戏 / 0=退出)
    2. 打印棋盘 → 玩家输入坐标(行 列)
    3. 程序校验合法性与占用情况
    4. 玩家落子 → 判断胜负 → 电脑随机落子 → 判断胜负 → 循环

2. 项目结构与模块划分

代码语言:javascript
复制
.
├─ game.h      // 宏定义、函数声明
├─ game.c      // 棋盘初始化/打印、玩家/电脑落子、胜负与平局判断
└─ test.c      // 菜单与主循环(main)
  • game.h:声明对外接口。
  • game.c:实现逻辑与算法。
  • test.c:实现主菜单和游戏主循环。

3. 核心逻辑与实现要点

3.1 初始化棋盘

游戏开始时所有格子全部置空,便于后续玩家和电脑输入

代码语言:javascript
复制
void InitBoard(char board[ROW][COL], int row, int col)
{
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            board[i][j] = ' ';
}
3.2 打印棋盘

如果单独给出二维数组,在玩家输入时不太好判断输入坐标且不够美观,故在打印的时候尽量模拟真实棋盘,如图所示:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
void DisplayBoard(char board[ROW][COL], int row, int col)
{
    for (int 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");
        }
    }
}
3.3 玩家与电脑落子

在玩家落子时只需要通过肉眼判断当前输入坐标,防止手滑输入错误,仅需要添加一个检测即可。

代码语言:javascript
复制
void PlayerMove(char board[ROW][COL], int row, int col)
{
    int x, y;
    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");
    }
}

但在电脑落子时,不能做到直接看到,只能通过随机种子扫描。

rand() 本身只是一个伪随机数发生器: 它内部用一条固定的数学公式递推,只要初始值(种子)相同,整个序列就完全一样。 如果不播种,每次启动程序时种子默认是 1,于是你两次运行得到的“随机”数列一字不差。 而time(NULL) 给 rand() 提供一个“每次运行都不一样”的起始值,避免每次得到同一串伪随机数。

代码语言:javascript
复制
void ComputerMove(char board[ROW][COL], int row, int col)
{
    printf("电脑下棋:>\n");
    while (1)
    {
        int x = rand() % row;
        int y = rand() % col;
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;
        }
    }
}
3.4 判断胜负

由于这里是三子棋,故可以将所有可能罗列出来,也就是写死的状态,而N子棋则需要将这部分改写,下篇推出。

代码语言:javascript
复制
char IsWin(char board[ROW][COL], int row, int col)
{
    for (int i = 0; i < row; i++)
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
            return board[i][1];

    for (int j = 0; j < col; j++)
        if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
            return board[1][j];

    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];

    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            if (board[i][j] == ' ') return 'C';

    return 'Q';
}

4. 完整三文件源码

4.1 game.h
代码语言:javascript
复制
#pragma once

#include<stdio.h>
#include<Windows.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);
//判断输赢 玩家赢 - '*' 电脑赢 - '#' 平局 - 'C' 继续 - 'Q'
char IsWin(char board[ROW][COL], int row, int col);
4.2 game.c
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
void InitBoard(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++)
		{
			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]);
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");

		//打印分割信息
		//printf("---|---|---\n");
		if (i < row - 1)
		{
			for (int 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)
{
	printf("电脑下棋:>\n");

	int x = 0;
	int y = 0;


	while (1)
	{

		x = rand() % row;//0 1 2
		y = rand() % col;//0 1 2 

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断输赢
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] == ' ')
			{
				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][1] != ' ')
		{
			return board[i][1];
		}
	}

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

	//对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][j];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][j];
	}

	//平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}

	//游戏继续
	return 'C';
}
4.3 test.c
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

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

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };

	//初始化棋盘函数
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);

	//开始下棋
	while (1)
	{
		PlayerMove(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);

		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
		DisplayBoard(board, ROW, COL);
	}
	//判断输赢
	if (ret == '*')
		printf("玩家赢!!!\n");
	else if (ret == '#')
		printf("电脑赢!!!\n");
	else
		printf("平局!!!\n");
	DisplayBoard(board, ROW, COL);

}

int main()
{
	srand((unsigned int)time(NULL));//设置随机数生成起点。
	int choice = 0;
	do
	{
		menu();
		printf("请选择(0/1):>");
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
			//printf("注意!1秒后清屏进入游戏!!!\n");
			//Sleep(1000);
			//system("cls");
			game();
			//printf("1111111\n");
			break;
		case 0:
			break;
		default:
			printf("选择错误,请重新选择(0/1):>\n");
			break;
		}
	} while (choice);
	return 0;
}

5. 可扩展方向

方向

说明

图形界面

使用 SDL / Qt 改造为 GUI 版

多局统计

增加比分、胜率记录

动态棋盘

改成 N×N 自定义版本

动态棋盘和N子棋实现,在下一节推出!!!


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C 语言实现“三子棋”(井字棋)——从 0 到 1 的完整实战
    • 1. 游戏规则与交互说明
    • 2. 项目结构与模块划分
    • 3. 核心逻辑与实现要点
      • 3.1 初始化棋盘
      • 3.2 打印棋盘
      • 3.3 玩家与电脑落子
      • 3.4 判断胜负
    • 4. 完整三文件源码
      • 4.1 game.h
      • 4.2 game.c
      • 4.3 test.c
    • 5. 可扩展方向
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档