#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <malloc.h> struct rcd;//声明节点结构
typedef struct rcd* Record;//节点指针别名
typedef struct rcd record;//节点别名
#define MAXIMUS 15 //定义棋盘大小 int p[MAXIMUS][MAXIMUS];//存储对局信息
char buff[MAXIMUS*+][MAXIMUS*+];//输出缓冲器
int Cx,Cy;//当前光标位置
int Now;//当前走子的玩家,1代表黑,2代表白
int wl,wp;//当前写入缓冲器的列数和行数位置
char* showText;//在棋盘中央显示的文字信息
int count;//回合数
int Putable;//指示当前是否可以走棋
int Exiting;//1为当场上无子并按ESC时询问是否退出程序的状态,2为非此状态
int ExiRep;//1为当回放到最后一回合并按向后时询问是否退出回放的状态,2为非此状态
Record RecBeg,RecNow;//记录的开始节点和当前节点 struct rcd//记录节点结构,双链表形式
{
int X;//此记录走棋的X坐标
int Y;//此记录走棋的Y坐标
Record Next;//前一个记录
Record Back;//后一个记录
}; Record newRecord()//记录节点构造函数
{
Record r=(Record)malloc(sizeof(record));//申请一个节点对象
r->Next=NULL;//给予前后节点初值NULL
r->Back=NULL;
return r;
} void Exit()//检查退出程序
{
int input;
if(Exiting)//如果是第二次按下ESC
{
exit();
}
else//如果是第一次按下ESC则询问是否退出程序
{
showText="是否退出?再次按下ESC退出,其他键返回";
Exiting=;//指示已经按下过ESC
}
} void ExitRep()//检查退出回放
{
int input;
if(ExiRep)//如果是第二次后移
{
ExiRep=;
}
else//如果是第一次后移则询问是否退出回放
{
showText="是否退出?再次后移退出回放,其他键返回";
ExiRep=;//指示已经按下过后移
}
} void AddRecord()//添加记录
{
RecNow->X=Cx;//记录坐标
RecNow->Y=Cy;
RecNow->Next=newRecord();//创建下一个记录节点
RecNow->Next->Back=RecNow;//完成双链表
RecNow=RecNow->Next;//当前记录推至下一个记录节点
} int DelRecord()//删除当前记录节点,1为删除成功,0为删除失败
{
Record b;//上一个节点
if(RecNow->Back!=NULL)//越界检查
{
b=RecNow->Back;//缓存上一个节点
free(RecNow);//释放当前节点
RecNow=b;//当前记录回至上一个记录节点
return ;
}
else
{
return ;//没有节点可删除时
}
} void CleanRecord()//清理所有记录
{
Record n;//下一个节点
while(RecBeg->Next!=NULL)//删除所有记录,直到越界前为止
{
n=RecBeg->Next;//记下下一个节点
free(RecBeg);//释放当前节点
RecBeg=n;//当前记录推至下一个记录节点
}
} char* Copy(char* strDest,const char* strSrc)//修改过的字符串复制函数,会忽略末端的\0
{
char* strDestCopy = strDest;
while (*strSrc!='\0')
{
*strDest++=*strSrc++;
}
return strDestCopy;
} void Initialize()//初始化一个对局函数
{
int i,j;//循环变量
system("title 对局中(按方向键控制光标,空格走子),Esc撤销");
showText="";//重置显示信息
count=;//回合数归零
RecNow=RecBeg=newRecord();
Exiting=;
for(i=;i<MAXIMUS;i++)//重置对局数据
{
for(j=;j<MAXIMUS;j++)
{
p[i][j]=;
}
}
Cx=Cy=MAXIMUS/;//重置光标到中央
Now=;//重置当前为黑方
} char* getStyle(int i,int j)//获得棋盘中指定坐标交点位置的字符,通过制表符拼成棋盘
{
if(p[i][j]==)//1为黑子
return "●";
else if(p[i][j]==)//2为白子
return "○";
else if(i==&&j==)//以下为边缘棋盘样式
return "┏";
else if(i==MAXIMUS-&&j==)
return "┓";
else if(i==MAXIMUS-&&j==MAXIMUS-)
return "┛";
else if(i==&&j==MAXIMUS-)
return "┗";
else if(i==)
return "┠";
else if(i==MAXIMUS-)
return "┨";
else if(j==)
return "┯";
else if(j==MAXIMUS-)
return "┷";
return "┼";//中间的空位
} char* getCurse(int i,int j){//获得指定坐标交点位置左上格的样式,通过制表符来模拟光标的显示
if(Putable)//可走棋时光标为粗线
{
if(i==Cx){
if(j==Cy)
return "┏";
else if (j==Cy+)
return "┗";
}
else if(i==Cx+)
{
if(j==Cy)
return "┓";
else if (j==Cy+)
return "┛";
}
}
else//不可走棋时光标为虚线
{
if(i==Cx){
if(j==Cy)
return "┌";
else if (j==Cy+)
return "└";
}
else if(i==Cx+)
{
if(j==Cy)
return "┐";
else if (j==Cy+)
return "┘";
}
}
return " ";//如果不在光标附近则为空
} void write(char* c)//向缓冲器写入字符串
{
Copy(buff[wl]+wp,c);
wp+=strlen(c);
} void ln()//缓冲器写入位置提行
{
wl+=;
wp=;
} void Display()//将缓冲器内容输出到屏幕
{
int i,l=strlen(showText);//循环变量,中间文字信息的长度
int Offset=MAXIMUS*+-l/;//算出中间文字信息居中显示所在的横坐标位置
if(Offset%==)//如果位置为奇数,则移动到偶数,避免混乱
{
Offset--;
}
Copy(buff[MAXIMUS]+Offset,showText);//讲中间文字信息复制到缓冲器
if(l%==)//如果中间文字长度为半角奇数,则补上空格,避免混乱
{
*(buff[MAXIMUS]+Offset+l)=0x20;
}
system("cls");//清理屏幕,准备写入
for(i=;i<MAXIMUS*+;i++){//循环写入每一行
printf("%s",buff[i]);
if(i<MAXIMUS*)//写入完每一行需要换行
printf("\n");
}
} void Print()//将整个棋盘算出并储存到缓冲器,然后调用Display函数显示出来
{
int i,j;//循环变量
wl=;
wp=;
for(j=;j<=MAXIMUS;j++)//写入出交点左上角的字符,因为需要打印棋盘右下角,所以很以横纵各多一次循环
{
for(i=;i<=MAXIMUS;i++)
{
write(getCurse(i,j));//写入左上角字符
if(j==||j==MAXIMUS)//如果是棋上下盘边缘则没有连接的竖线,用空格填充位置
{
if(i!=MAXIMUS)
write(" ");
}
else//如果在棋盘中间则用竖线承接上下
{
if(i==||i==MAXIMUS-)//左右边缘的竖线更粗
write("┃");
else if(i!=MAXIMUS)//中间的竖线
write("│");
}
}
if(j==MAXIMUS)//如果是最后一次循环,则只需要处理边侧字符,交点要少一排
{
break;
}
ln();//提行开始打印交点内容
write(" ");//用空位补齐位置
for(i=;i<MAXIMUS;i++)//按横坐标循环正常的次数
{
write(getStyle(i,j));//写入交点字符
if(i!=MAXIMUS-)//如果不在最右侧则补充一个横线承接左右
{
if(j==||j==MAXIMUS-)
{
write("━");//上下边缘的横线更粗
}
else
{
write("─");//中间的横线
}
}
}
ln();//写完一行后提行
}
Display();//将缓冲器内容输出到屏幕
} int Put(){//在当前光标位置走子,如果非空,则返回0表示失败
if(Putable)
{
p[Cx][Cy]=Now;//改变该位置数据
AddRecord();
return ;//返回1表示成功
}
else
{
return ;
}
} int Check()//胜负检查,即判断当前走子位置有没有造成五连珠的情况
{
int w=,x=,y=,z=,i;//累计横竖正斜反邪四个方向的连续相同棋子数目
for(i=;i<;i++)if(Cy+i<MAXIMUS&&p[Cx][Cy+i]==Now)w++;else break;//向下检查
for(i=;i<;i++)if(Cy-i>&&p[Cx][Cy-i]==Now)w++;else break;//向上检查
if(w>=)return Now;//若果达到5个则判断当前走子玩家为赢家
for(i=;i<;i++)if(Cx+i<MAXIMUS&&p[Cx+i][Cy]==Now)x++;else break;//向右检查
for(i=;i<;i++)if(Cx-i>&&p[Cx-i][Cy]==Now)x++;else break;//向左检查
if(x>=)return Now;//若果达到5个则判断当前走子玩家为赢家
for(i=;i<;i++)if(Cx+i<MAXIMUS&&Cy+i<MAXIMUS&&p[Cx+i][Cy+i]==Now)y++;else break;//向右下检查
for(i=;i<;i++)if(Cx-i>&&Cy-i>&&p[Cx-i][Cy-i]==Now)y++;else break;//向左上检查
if(y>=)return Now;//若果达到5个则判断当前走子玩家为赢家
for(i=;i<;i++)if(Cx+i<MAXIMUS&&Cy-i>&&p[Cx+i][Cy-i]==Now)z++;else break;//向右上检查
for(i=;i<;i++)if(Cx-i>&&Cy+i<MAXIMUS&&p[Cx-i][Cy+i]==Now)z++;else break;//向左下检查
if(z>=)return Now;//若果达到5个则判断当前走子玩家为赢家
return ;//若没有检查到五连珠,则返回0表示还没有玩家达成胜利
}
void ReplayMode(){
int i,j;//循环变量
system("title 回放中(按左键后退,右键或空格前进),Esc退出");
showText="";//重置显示信息
count=;//回合数归零
Putable=;//不可走棋状态
RecBeg->Back=newRecord();
RecBeg->Back->Next=RecBeg;
RecBeg=RecBeg->Back;
for(i=;i<MAXIMUS;i++)//重置对局数据
{
for(j=;j<MAXIMUS;j++)
{
p[i][j]=;
}
}
Now=;//重置当前为黑方
} void RepForward()//回放模式前进
{
if(RecNow->Next->Next!=NULL)//越界检查
{
RecNow=RecNow->Next;//当前节点推至下一个记录节点
p[RecNow->X][RecNow->Y]=Now;//按照记录还原一个回合
Cx=RecNow->X;//设置光标位置
Cy=RecNow->Y;
Now=-Now;//转换当前的黑白方
}
else//若已达到最后则询问退出
{
ExitRep();
}
} void RepBackward()//回放模式后退
{
if(RecNow->Back!=NULL)//越界检查
{
p[RecNow->X][RecNow->Y]=;//按照记录撤销一个回合
if(RecNow->Back->Back==NULL)//在整个棋盘没有棋子时隐藏光标
{
Cx=-;
Cy=-;
}
else if(RecNow->Back==NULL)//在只有一个棋子时移动光标到这个棋子的位置
{
Cx=RecNow->X;
Cy=RecNow->Y;
}
else//正常情况下移动光标到上一回合的位置
{
Cx=RecNow->Back->X;
Cy=RecNow->Back->Y;
}
RecNow=RecNow->Back;//当前节点后退至上一个记录节点
Now=-Now;//转换当前的黑白方 }
} void ShowReplay()
{
int input;//输入变量
ReplayMode();//初始化回放模式
RecNow=RecBeg;//当前观察从头开始
RepForward();//显示第一次走棋
while()//开始无限回合的死循环,直到Esc退出
{
if(ExiRep==)
{
ExiRep=;
break;
}
Print();//打印棋盘
input=getch();//等待键盘按下一个字符
if(input==)//如果是ESC则退出回放
{
return;
}
else if(input==0x20)//如果是空格则前进
{
RepForward();
continue;
}
else if(input==0xE0)//如果按下的是方向键,会填充两次输入,第一次为0xE0表示按下的是控制键
{
input=getch();//获得第二次输入信息
switch(input)//判断方向键方向并移动光标位置
{
case 0x4B:
RepBackward();//向左后退
break;
case 0x4D:
RepForward();//向右前进
continue;
}
}
ExiRep=;//未再次按后移则不准备退出
showText="";
}
} void Regret()//悔棋撤销,如果棋盘上没有子即为退出
{
if(DelRecord()){//尝试删除当前节点,如果有节点可以删除则
p[RecNow->X][RecNow->Y]=;//撤除当前回合
if(RecNow->Back==NULL)//如果删除的是第一颗子则将光标移动到第一颗子原来的位置上
{
Cx=RecNow->X;
Cy=RecNow->Y;
}
else//否则将光标移动到上一颗子上
{
Cx=RecNow->Back->X;
Cy=RecNow->Back->Y;
}
Now=-Now;//反转当前黑白方
}
else
{
Exit();//如果没有棋子可以撤销,则询问退出
}
} int RunGame()//进行整个对局,返回赢家信息(虽然有用上)
{
int input;//输入变量
int victor;//赢家信息
Initialize();//初始化对局
while(){//开始无限回合的死循环,直到出现胜利跳出
Putable=p[Cx][Cy]==;
Print();//打印棋盘
input=getch();//等待键盘按下一个字符
if(input==)//如果是ESC则悔棋或退出
{
Regret();
Print();
continue;
}
else if(input==0x20)//如果是空格则开始走子
{
if(Put())//如果走子成功则判断胜负
{
victor=Check();
Now=-Now;//轮换当前走子玩家
count++;
if(victor==)//如果黑方达到胜利,显示提示文字并等待一次按键,返回胜利信息
{
showText="黑方胜利!按R查看回放,按其他键重新开局";
Print();
input=getch();
if(input==0xE0)
{
getch();
}
else if(input=='R'||input=='r')
{
ShowReplay();
}
return Now;
}
else if(victor==)//如果白方达到胜利,显示提示文字并等待一次按键,返回胜利信息
{
showText="白方胜利!按R查看回放,按其他键重新开局";
Print();
input=getch();
if(input==0xE0)
{
getch();
}
else if(input=='R'||input=='r')
{
ShowReplay();
}
return Now;
}else if(count==MAXIMUS*MAXIMUS)//如果回合数达到了棋盘总量,即棋盘充满,即为平局
{
showText="平局!按R查看回放,按其他键重新开局";
Print();
input=getch();
if(input==0xE0)
{
getch();
}
else if(input=='R'||input=='r')
{
ShowReplay();
}
CleanRecord();
return ;
}
}
}
else if(input==0xE0)//如果按下的是方向键,会填充两次输入,第一次为0xE0表示按下的是控制键
{
input=getch();//获得第二次输入信息
switch(input)//判断方向键方向并移动光标位置
{
case 0x4B://
Cx--;
break;
case 0x48:
Cy--;
break;
case 0x4D:
Cx++;
break;
case 0x50:
Cy++;
break;
}
if(Cx<)Cx=MAXIMUS-;//如果光标位置越界则移动到对侧
if(Cy<)Cy=MAXIMUS-;
if(Cx>MAXIMUS-)Cx=;
if(Cy>MAXIMUS-)Cy=;
}
Exiting=;//未再次按下ESC则不准备退出
showText="";
}
} int main()//主函数
{
system("mode con cols=63 lines=32");//设置窗口大小
system("color E0");//设置颜色
while(){//循环执行游戏
RunGame();
}
}

C语言 五子棋的更多相关文章

  1. c语言五子棋

    #include <stdio.h>#include <stdlib.h>#include <windows.h>#include <conio.h> ...

  2. C语言 五子棋2

    #include<windows.h> #include<stdlib.h> #include<stdio.h> #include<conio.h> # ...

  3. C 语言控制台实现五子棋项目

    花了一天时间实现了控制台五子棋项目,把项目贴上来.也算是告一段落了. 为了进一步了解C语言编程,熟悉优秀的编码风格,提升编码能力,丰富项目经验.所以在编程初期选择了控制台小游戏<单机五子棋> ...

  4. 用c语言实现简单的五子棋

    用c语言实现简单的五子棋 这个小游戏是从零开始的实现的,框架灵感来自于小游戏<走迷宫>. 游戏代码配置: 二维数组+简单逻辑+getch读取键盘+windows函数(刷屏,改颜色,改窗口大 ...

  5. C/C++编程笔记:C语言开发经典游戏项目《五子棋》,内含源码

    上一节给大家分享了扫雷游戏的源代码,本篇文章当然也不会让大家失望,专门针对C语言入门或者学习了部分知识之后的小伙伴来练手的游戏项目——<五子棋大战>,本期并不是使用的easyX,因为考虑到 ...

  6. 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网)

    使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网) 一,前期准备 1,Java IDE(Eclipse)与JDK的安装与配置jdk-15.0.1-免配置路径版提取码:earu免安装版 ...

  7. C语言实现五子棋简单功能

    /******************************************************************** C-4.29-1: 实现五子棋游戏 操作说明:用方向键或者& ...

  8. 笔记:C语言图形基本函数及实例五子棋游戏

    初始化图形界面 int gdriver; int gmode; gdriver = DETECT; initgraph(&gdriver,&gmode,"" ); ...

  9. C语言实现五子棋

    可以称得上史上最简单的五子棋版本了. 可以使用curses库来改进页面和下棋方式. 并且对于输入的坐标没有进行鉴别,如果输入的坐标超过棋盘大小,就会段错误退出. 我改进了一点,但是还是没有完全避免这个 ...

随机推荐

  1. 洛谷 P1514 引水入城 解题报告

    P1514 引水入城 题目描述 在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠.该国的行政区划十分特殊,刚好构成一个 NN 行 \times M×M 列的矩形,如上图所示,其中每个格 ...

  2. Codeforces 671C. Ultimate Weirdness of an Array(数论+线段树)

    看见$a_i\leq 200000$和gcd,就大概知道是要枚举gcd也就是答案了... 因为答案是max,可以发现我们很容易算出<=i的答案,但是很难求出单个i的答案,所以我们可以运用差分的思 ...

  3. 实现了一下Berlekamp-Massey

    //from https://www.cnblogs.com/TSHugh/p/9265155.html //在FP中求固定项数数列的线性递推式 //此递推式严格符合数学定义,故可能在末尾出现一些看起 ...

  4. Python高级语法总结

    1.Python面向对象 创建类 使用class语句来创建一个新类,class之后为类的名称并以冒号结尾,如下实例: class ClassName: '类的帮助信息' #类文档字符串 class_s ...

  5. 我学习的第一个uiautomator从创建到运行结束

    一.新建自动化脚本     1.新建java工程包              [file]----[new]----[Java Project]    新建工程                  [右 ...

  6. 一元回归_R相关系数_多重检验

     sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

  7. eclipse/myeclipse添加插件3种方式

    个人比较偏爱links的方式,以下方式eclipse/myeclipse均适合 1.links方式 在eclipse目录先新建links目录,新建一个xx.link(例如:android.link) ...

  8. word2vec 和 doc2vec 词向量表示

    Word2Vec 词向量的稠密表达形式(无标签语料库训练) Word2vec中要到两个重要的模型,CBOW连续词袋模型和Skip-gram模型.两个模型都包含三层:输入层,投影层,输出层. 1.Ski ...

  9. NOIP模拟赛16

    NOIP2017金秋冲刺训练营杯联赛模拟大奖赛第一轮Day2 期望得分:100+100+ =200+ 实际得分:100+40+70=210 T1天天寄快递 直接模拟,代码丢了...... T2天天和不 ...

  10. linux内核文件系统:proc、tmpfs、devfs、sysfs简要介绍

    linux内核文件系统:proc.tmpfs.devfs.sysfs proc:虚拟文件系统,在linux系统中被挂载与/proc目录下.里面的文件包含了很多系统信息,比如cpu负载. 内存.网络配置 ...