Jack's Blog

流淌的心,怎能阻拦,吹来的风,又怎能阻挡。

five in row

Jacob posted @ May 28, 2017 01:42:30 PM in C语言 with tags C语言 , 735 阅读

a very interesting game:

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>                                      //应用 getch() 函数 函数用途:从控制台读取一个字符,但不显示在屏幕上
#include <string.h>

//定义棋盘大小 标准 15*15棋盘
#define MAXIMUS 15

//存储对局信息 二维数组
int p[MAXIMUS][MAXIMUS];

//输出缓冲器 纵横分别 15*2+1个字符图标  15*4+3个字符图标
//目的:1、显示方形光标 2、打印棋盘(单字符长宽不等)
char buff[MAXIMUS*2+1][MAXIMUS*4+3];

//当前光标位置
int Cx,Cy;

//当前走子的玩家,1代表黑,2代表白
int Now;

//当前写入缓冲器的行数和列数位置
int wl,wp;

//在棋盘中央显示的文字信息
char* showText;

//回合数
int count;

//修改过的字符串复制函数,会忽略末端的\0
char* Copy(char* strDest,const char* strSrc);

//初始化一个对局函数
void Initialize();//Initialize:初始化

//获得棋盘中指定坐标交点位置的字符,通过制表符拼成棋盘
char* getStyle(int i,int j);

//获得指定坐标交点位置左上格的样式,通过制表符来模拟光标的显示
//getCurse:获得图案
char* getCurse(int i,int j);

//向缓冲器写入字符串
void write(char* c);

//缓冲器写入位置提行
void ln();

//将缓冲器内容输出到屏幕
void Display();

//将整个棋盘算出并储存到缓冲器,然后调用Display函数显示出来
void Print();

//在当前光标位置走子,如果非空,则返回0表示失败
int Put();

//胜负检查,即判断当前走子位置有没有造成五连珠的情况
int Check();

//进行整个对局,返回赢家信息
int RunGame();

//主函数
int main()
{
    //设置标题
    system("title 简易五子棋 ——翻转课堂");
    //设置窗口大小
    system("mode con cols=63 lines=32");
    /*设置颜色
     *system("color 0A"); 其中color后面的0是背景色代号,A是前景色代号。各颜色代码如下:
     *0=黑色 1=蓝色 2=绿色 3=湖蓝色 4=红色 5=紫色 6=黄色 7=白色 8=灰色 9=淡蓝色
     *A=淡绿色 B=淡浅绿色 C=淡红色 D=淡紫色 E=淡黄色 F=亮白色
     */
    system("color 72");
    //循环执行游戏
    while(1)
       {
         RunGame();
       }
}

//修改过的字符串复制函数,会忽略末端的\0   主要考虑到Dispaly函数
char* Copy(char* strDest,const char* strSrc)
{
    char* strDestCopy = strDest;
    while (*strSrc!='\0')
       {
            *strDest++=*strSrc++;
       }
    return strDestCopy;
}

//进行整个对局,返回赢家信息(虽然有用上)
int RunGame()
{
  //输入变量
  int input;
  //赢家信息
  int victor;
  //初始化对局
  Initialize();
  //开始无限回合的死循环,直到出现胜利跳出
  while(1)
  {
   //打印棋盘
   Print();
   //等待键盘按下一个字符
   //getch 从控制台读取一个字符,但不显示在屏幕上
   input=getch();
   //如果是ESC则退出程序,ESC的ASCLL码为27
  if(input==27)
     {
        //正常退出
        exit(0);
     }
//如果是空格则开始走子
  else if(input==0x20)
     {
//如果走子成功则判断胜负
//put判断当前位置能否走子,即是否已经走过子
       if(Put())
       {
            victor=Check();
            //轮换当前走子玩家,3-1=2,3-2=1
            Now=3-Now;
            //对局数+1
            count++;
   //如果黑方达到胜利,显示提示文字并等待一次按键,返回胜利信息
     if(victor==1)
         {
            showText="黑方获得了胜利!";
            //重新输出棋盘 且中间为showText文字
            Print();
            /*When reading a function key or an arrow key,
             *each function must be called twice;
             *the first call returns 0 or 0xE0,
             *and the second call returns the actual key code.
             */
            if(getch()==0xE0)
            {
                getch();
            }
            return Now;     //并无实际意义,只是结束循环
         }
//如果白方达到胜利,显示提示文字并等待一次按键,返回胜利信息
     else if(victor==2)
         {
            showText="白方获得了胜利!";
            Display();
            if(getch()==0xE0)
            {
                getch();
            }
            return Now;
         }
//如果回合数达到了棋盘总量,即棋盘充满,即为平局
     else if(count==MAXIMUS*MAXIMUS)
         {
            showText="平局!";
            Display();
            if(getch()==0xE0)
            {
                getch();
            }
            return 0;
         }
       }
     }
//如果按下的是方向键,会填充两次输入,第一次为0xE0表示按下的是控制键
   else if(input==0xE0)
     {
    //获得第二次输入信息
    input=getch();
    //判断方向键方向并移动光标位置
    switch(input)
         {
           case 0x4B:
           Cx--;
           break;
           case 0x48:
           Cy--;
           break;
           case 0x4D:
           Cx++;
           break;
           case 0x50:
           Cy++;
           break;
         }
//如果光标位置越界则移动到对侧
if(Cx<0)Cx=MAXIMUS-1;
if(Cy<0)Cy=MAXIMUS-1;
if(Cx>MAXIMUS-1)Cx=0;
if(Cy>MAXIMUS-1)Cy=0;
     }
   }
}

//初始化一个对局函数
void Initialize()
{
    //循环变量
    int i,j;
    //重置显示信息
    showText="";
    //回合数归零
    count=0;
    //重置对局数据
    for(i=0;i<MAXIMUS;i++)
        {
             for(j=0;j<MAXIMUS;j++)
             {
                p[i][j]=0;          //各处走子归零
             }
        }
    //重置光标到中央
    Cx=Cy=MAXIMUS/2;
    //重置当前为黑方
    Now=1;
}

//获得棋盘中指定坐标交点位置的字符,通过制表符拼成棋盘
char* getStyle(int i,int j)
{
    //1为黑子
    if(p[i][j]==1)
    return "●";
    //2为白子
    else if(p[i][j]==2)
    return "○";
    //以下为边缘棋盘样式
    else if(i==0&&j==0)
    return "┏";
    else if(i==MAXIMUS-1&&j==0)
    return "┓";
    else if(i==MAXIMUS-1&&j==MAXIMUS-1)
    return "┛";
    else if(i==0&&j==MAXIMUS-1)
    return "┗";
    else if(i==0)
    return "┠";
    else if(i==MAXIMUS-1)
    return "┨";
    else if(j==0)
    return "┯";
    else if(j==MAXIMUS-1)
    return "┷";
    //中间的空位
    return "┼";
}

//获得指定坐标交点位置左上格的样式,通过制表符来模拟光标的显示
char* getCurse(int i,int j)
{

    if(i==Cx)
       {
         if(j==Cy)
         return "┏";
         else if (j==Cy+1)
         return "┗";
       }
    else if(i==Cx+1)
       {
         if(j==Cy)
         return "┓";
         else if (j==Cy+1)
         return "┛";
       }
    //如果不在光标附近则为空
         return " ";
}

//向缓冲器写入字符串
void write(char* c)
{
    //字符串复制 (地址,地址),修改 wl、wp 处的字符为*c所指字符
    strcpy(buff[wl]+wp,c);
    //移至下一列  通过ln函数移动至下一行
    wp+=strlen(c);
}

//缓冲器写入位置 提行
void ln()
{
    //行数+1
    wl+=1;
    //列数归0
    wp=0;
}

//将缓冲器内容输出到屏幕
void Display()
{
//循环变量,中间文字信息的长度
//showText 在RunGame函数里写出
    int i,l=strlen(showText);
//算出中间文字信息居中显示所在的横坐标位置
    int Offset=MAXIMUS*2+2-l/2;
//如果位置为奇数,则移动到偶数,避免混乱
    if(Offset%2==1)
   {
      Offset--;
   }
//讲中间文字信息复制到缓冲器
//由于 没有 \0 可以顺利输出缓冲期中showText后的其余字节
    Copy(buff[MAXIMUS]+Offset,showText);
//如果中间文字长度为半角奇数,则补上空格,避免混乱
    if(l%2==1)
   {
      *(buff[MAXIMUS]+Offset+l)=0x20;   //空格的ASCLL码 为16进制 0x20
   }
//清理屏幕,准备写入
   system("cls");     //system("CLS")可以实现清屏操作。
//循环写入每一行
   for(i=0;i<MAXIMUS*2+1;i++)
   {
   printf("%s",buff[i]);    //buff[i] 在Print函数里写入
//写入完每一行需要换行
   if(i<MAXIMUS*2)
   printf("\n");
   }
}

//将整个棋盘算出并储存到缓冲器(即buff 二维字符数组,寄存各位置字符),然后调用Display函数显示出来
//Print 为 写入各位置buff字符 但并不输出 Display 显示
void Print()        //Print 之后 Display
{
//循环变量
    int i,j;
    wl=0;
    wp=0;
//写入出交点左上角的字符,因为需要打印棋盘右下角,所以可以横纵各多一次循环
for(j=0;j<=MAXIMUS;j++)                 //第j行  j为行数
   {
    for(i=0;i<=MAXIMUS;i++)             //第i列  i为列数
      {
         //写入左上角字符
         write(getCurse(i,j));          //判断i,j是否在光标位置 如果不是 getCurse函数返回“  ”填充棋盘
//如果是棋盘上下边缘则没有连接的竖线,用空格填充位置
         if(j==0||j==MAXIMUS)
           {
               if(i!=MAXIMUS)           // != 暂无特殊含义
               write("  ");             //最上 最下 各空一行
           }
//如果在棋盘中间则用竖线承接上下
         else
           {
              //左右边缘的竖线更粗
               if(i==0||i==MAXIMUS-1)
               write("┃");
          //中间的竖线
         else if(i!=MAXIMUS)
               write("│");
           }
      }
//如果是最后一次循环,则只需要处理边侧字符,交点要少一排
         if(j==MAXIMUS)
           {
               break;
           }
//提行开始打印交点内容
         ln();
      //用空位补齐位置      左侧一列 补为空
         write("  ");

//按横坐标循环正常的次数
    for(i=0;i<MAXIMUS;i++)
      {
//写入交点字符
         write(getStyle(i,j));
//如果不在最右侧则补充一个横线承接左右
         if(i!=MAXIMUS-1)
           {
              if(j==0||j==MAXIMUS-1)
                {
               //上下边缘的横线更粗
                   write("━");
                }
              else
                {
               //中间的横线
                   write("—");
                }
           }
      }
//写完一行后提行
    ln();
   }
//将缓冲器内容输出到屏幕
    Display();
}

//在当前光标位置走子,如果非空,则返回0表示失败
int Put()
{
    if(p[Cx][Cy]==0)
   {
    //改变该位置数据
    p[Cx][Cy]=Now;          //标记当前走子:走子位置写入1/2
    //返回1表示成功
    return 1;
   }
    else
   {
    return 0;
   }
}

//胜负检查,即判断当前走子位置有没有造成五连珠的情况
int Check()
{
//累计横竖正斜反斜四个方向的连续相同棋子数目
int w=1,x=1,y=1,z=1,i;
//向下检查
for(i=1;i<5;i++)if(Cy+i<MAXIMUS&&p[Cx][Cy+i]==Now)w++;else break;
//向上检查
for(i=1;i<5;i++)if(Cy-i>0&&p[Cx][Cy-i]==Now)w++;else break;
//若果达到5个则判断当前走子玩家为赢家
if(w>=5)return Now;

//向右检查
for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&p[Cx+i][Cy]==Now)x++;else break;
//向左检查
for(i=1;i<5;i++)if(Cx-i>0&&p[Cx-i][Cy]==Now)x++;else break;
//若果达到5个则判断当前走子玩家为赢家
if(x>=5)return Now;

//向右下检查
for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&Cy+i<MAXIMUS&&p[Cx+i][Cy+i]==Now)y++;else break;
//向左上检查
for(i=1;i<5;i++)if(Cx-i>0&&Cy-i>0&&p[Cx-i][Cy-i]==Now)y++;else break;
//若果达到5个则判断当前走子玩家为赢家
if(y>=5)return Now;

//向右上检查
for(i=1;i<5;i++)if(Cx+i<MAXIMUS&&Cy-i>0&&p[Cx+i][Cy-i]==Now)z++;else break;
//向左下检查
for(i=1;i<5;i++)if(Cx-i>0&&Cy+i<MAXIMUS&&p[Cx-i][Cy+i]==Now)z++;else break;
//若果达到5个则判断当前走子玩家为赢家
if(z>=5)return Now;

//若没有检查到五连珠,则返回0表示还没有玩家达成胜利
return 0;
}

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter