C语言 五子棋
#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语言 五子棋的更多相关文章
- c语言五子棋
#include <stdio.h>#include <stdlib.h>#include <windows.h>#include <conio.h> ...
- C语言 五子棋2
#include<windows.h> #include<stdlib.h> #include<stdio.h> #include<conio.h> # ...
- C 语言控制台实现五子棋项目
花了一天时间实现了控制台五子棋项目,把项目贴上来.也算是告一段落了. 为了进一步了解C语言编程,熟悉优秀的编码风格,提升编码能力,丰富项目经验.所以在编程初期选择了控制台小游戏<单机五子棋> ...
- 用c语言实现简单的五子棋
用c语言实现简单的五子棋 这个小游戏是从零开始的实现的,框架灵感来自于小游戏<走迷宫>. 游戏代码配置: 二维数组+简单逻辑+getch读取键盘+windows函数(刷屏,改颜色,改窗口大 ...
- C/C++编程笔记:C语言开发经典游戏项目《五子棋》,内含源码
上一节给大家分享了扫雷游戏的源代码,本篇文章当然也不会让大家失望,专门针对C语言入门或者学习了部分知识之后的小伙伴来练手的游戏项目——<五子棋大战>,本期并不是使用的easyX,因为考虑到 ...
- 使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网)
使用Java语言编写一个五子棋UI界面并实现网络对战功能(非局域网) 一,前期准备 1,Java IDE(Eclipse)与JDK的安装与配置jdk-15.0.1-免配置路径版提取码:earu免安装版 ...
- C语言实现五子棋简单功能
/******************************************************************** C-4.29-1: 实现五子棋游戏 操作说明:用方向键或者& ...
- 笔记:C语言图形基本函数及实例五子棋游戏
初始化图形界面 int gdriver; int gmode; gdriver = DETECT; initgraph(&gdriver,&gmode,"" ); ...
- C语言实现五子棋
可以称得上史上最简单的五子棋版本了. 可以使用curses库来改进页面和下棋方式. 并且对于输入的坐标没有进行鉴别,如果输入的坐标超过棋盘大小,就会段错误退出. 我改进了一点,但是还是没有完全避免这个 ...
随机推荐
- 洛谷P1273 有线电视网 【树上分组背包】
题目描述 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点. 从转播站到转播站以及从 ...
- uoj 36 玛里苟斯
[清华集训2014]玛里苟斯 - 题目 - Universal Online Judge k=1,2,3,4,5各占20pts是提示 应当分开考虑 k=1 拆位,如果第i位有1,则有1/2的概率xor ...
- 【Nowcoder】玩游戏
Portal --> Nowcoder197D Solution 所以说这是一道==纯粹的人类智慧题是这样吗qwq 一开始的时候想sg函数qwq然后发现..好像根本不能拆成独立的子游戏嘛qw ...
- jquery、css3动态显示百分比圆
动态显示百分圆 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <m ...
- poj1204 Word Puzzles
Word Puzzles Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 12090 Accepted: 4547 S ...
- ASP.NET 使用ajaxupload.js插件出现上传较大文件失败的解决方法
在网上下载了一个ajaxupload.js插件,用于无刷新上传图片使的,然后就按照demo的例子去运行了一下,上传啊什么的都OK,但是正好上传的示例图片有一个比较大的,4M,5M的样子,然后上传就会报 ...
- [吴恩达机器学习笔记]12支持向量机5SVM参数细节
12.支持向量机 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考资料 斯坦福大学 2014 机器学习教程中文笔记 by 黄海广 12.5 SVM参数细节 标记点选取 标记点(landma ...
- 怎样把一个DIV放到另一个div右下角
怎样把一个DIV放到另一个div右下角??? 借助CSS定位来实现,你将右下角的那个DIV放在另一个DIV里面,参考代码如下示: <div id="box1"> < ...
- How To Build Compelling Stories From Your Data Sets
How To Build Compelling Stories From Your Data Sets Every number has a story. As a data scientist, y ...
- 百度搜索URL中的参数都是什么
最近,点石排名更新了一个新功能——站内搜索.其实理解起来也很简单,就是通过URL限定搜索结果为某个网站,从而参与点击(例如:https://www.baidu.com/s?wd=SEO&si= ...