天天看点

C++在控制台上实现2048游戏

刚学完C++基础,想找一个项目练练手,萌发写一个2048的游戏的想法,既然有想法,就是干!!自己第一次独立完成一个项目(也不是全部是独立的,开始之前参考过代码),虽然只有几百行代码,但这是一个开始,朝着自己梦想一步一个脚印。fighting!!
    首先在网上找了一个2048的项目,http://www.jb51.net/article/51111.htm,用这个代码运行了一下,大概有了整体的认识,并且粗略的看了一下模块。在这个程序中,有一些bug:
    ①和我们平常玩的2048不同,这个游戏生成的随机数是2的次方,我们一般只会生成2和4;
    ②游戏中每一次移动后生成新数的位置是第一个空格,降低游戏的体验;
    ③游戏只有两个相邻的大小相同的数才会结合,中间存在0的话,不会结合;其实是由于每次移动只会移动一格导致的;
    ④游戏中含有零,看起来不舒服;
    虽然有这些问题,但是这个程序给了我整体的思路,让我能够自己接下来走自己的路。
           

接下来是自己做项目遇到的一些问题和解决方法,最后附上自己的源代码

一、控制台如何从键盘上获得方向键
    之前简单的以为方向键就是简单的ASCII码,too young too simple。  http://blog.csdn.net/feilong911hao/article/details/42081967,看完这篇博客之后弄明白。之前问过很多人,各种搜也没找到,感谢博主和万能的CSDN。  
    总结:①方向键有两个字节,第一个字节不是ASCII码,后面一个字节才是
         ②使用getch()没有回显的从键盘输入。
           

代码:

//返回值
//  1   上   72
//  2   下   80
//  3   左   75
//  4   右   77
int get_direction()
{
    char c1,c2;
    int ret = ;
    c1 = getch();
    if (!isascii(c1))
    {
        c2 = getch();
        switch(c2)
        {
        case : ret = ;break;
        case : ret = ;break;
        case : ret = ;break;
        case : ret = ;break;
        default: break;
        }
    }
    return ret;
}
           
二、随机数的生成
    使用srand()和rand(),srand()使用time(0)当前的时间当做随机数种子,避免伪随机数;
    http://wenku.baidu.com/link?url=XkB5uCfIkKL_LyBqEoM5ueoxl29mwXtEyfAzGSwfHsgGRB3-N9eR0O0EFoElSa1-YISWXb1mevClbOLytYDhFMoJrnrkhnlxK-brQsslt4O 
           

代码:

//随机产生一个-n-的数字
int rand_pro_s(int n)
{
    srand(time());
    int k = rand()%n;
    return k;
}
           
三、移动(难点)
    将移动分为两步,第一步是合并相邻两个相同的数字(中间可以含有0);第二步,方格里面的数字全部向移动的方向移动,去除中间的零。
    第一步:之前没有理清思想,只合并了相邻的两个数字,没有考虑含有0的情况。思路:遍历每一行或者列,k来存放每一个非零的数,遇见一个非零,如果和k相等,那么就将这个数变为2k,另外的数变为0.
    第二步:使用一个k来作为记录非零的个数,然后遍历。
           

代码

void up_dir()
{
    //完成第一步
    int i,j;
    for (i=; i<; i++)
    {
        /*for (j=0; j<3;)
        {
            if (array[j][i] == array[j+1][i])
            {
                array[j][i] = array[j][i] + array[j+1][i];
                array[j+1][i] = 0;
                j += 2;
                continue;
            }
            j++;
        }*/
        int k = ;
        int x = ; 
        int y = ;
        for (j=; j<; j++)
        {
            if (k==array[j][i] && k!=)
            {
                //array[x][y] = 2*array[x][y];
                //array[x][y] = 2*k;
                array[x][y] = *k;
                array[j][i] = ;
                k = ;
                continue;
            }
            if (array[j][i]!=)
            {
                k = array[j][i];
                //此处的bug是x和y分别应该存储j和i
                x = j;
                y = i;
            }
        }

    }

    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j<; j++)
        {
            //if (array[j][i])//此处有bug,假如k和j相等
            if (array[j][i])
            {
                if (k != j)
                {
                    array[k][i] = array[j][i];
                    array[j][i] = ;
                }
                k++;
            }
        }
    }
    insert_num();
    system("cls");//清屏
    display();
}
           
四、如何随机插入一个数(难点)
    使用剩余空格数N,并且将空格的坐标记录下来,生成1-N的随机数n,并且在第n个空格插入2或者4.
    tips:①vector<vector<int>> v(16 ,vector<int>());vector中含有vector不能定义为空
          ②插入数字的时候需要判断空格数

五、如何判定游戏结束
    看是否能够水平移动或者竖直移动,在移动操作的函数中修改,不改变二维数组的值即可
           

程序所有的代码

#include <iostream>
#include <cstdlib>  //rand(),time()
#include <ctime>    
#include <conio.h>  //getch()
#include <ctype.h>  //
#include <vector>
#include <math.h>   //pow
#include <iomanip>  //setw

using namespace std;

int array[][] = {};//申请全局数组作为方格里面的数


//0—3的随机数产生
int rand_pro()
{
    srand(time());
    int k = rand()%;
    return k;
}

////////////////////////////////////界面函数////////////////////////////////////////////////////////////
//之前的界面
void display1()
{
    int i,j;
    for (i=; i<; i++)
    {
        for(j=; j<; j++)
        {
            cout<<array[i][j]<<"    ";
        }
        cout<<endl;
    }
    cout<<"----------------------------------"<<endl;
}

//界面美化,不显示0,显示外框
void display2()
{
    int i,j;
    for (i=; i<; i++)
    {
        for(j=; j<; j++)
        {
            if (array[i][j] == )
            {
                cout<<" "<<"    ";
            }
            else
                cout<<array[i][j]<<"    ";
        }
        cout<<endl;
    }
    cout<<"----------------------------------"<<endl;
}

void display() //显示棋盘  
{  
    cout<<setw()<<"X2048 by ziyunmumu"<<endl;  
    cout<<setw()<<" |-----------------------|"<<endl;  
    for(int i=;i<=;i++)   
    {  
        cout<<setw()<<"";  
        for(int j=;j<=;j++)  
        {  
            //SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED); 
            if (array[i][j] == )
            {
                cout<<setw()<<"|"<<setw()<<" ";  
            }
            else
                cout<<setw()<<"|"<<setw()<<array[i][j];  

            if(j==)  
            {  
                cout<<setw()<<"|"<<endl;  
                cout<<setw()<<" |-----------------------|"<<endl;  
            }  
        }  
    }  
}  
////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////新游戏的开始////////////////////////////////////////////////////////////
void new_game()
{
    //初始化内容
    int i,j;
    for(i=;i<;i++)
    {
        for (j=;j<;j++)
        {
            array[i][j] = ;
        }
    }

    int m = rand_pro();
    int n = rand_pro();
    array[m][n] = ;
    display();
}
////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////
//随机产生一个0-n-1的数字
int rand_pro_s(int n)
{
    srand(time());
    int k = rand()%n;
    return k;
}

//随机产生一个1-n的数字
int rand_pro_ss(int n)
{
    srand(time());
    int k = rand()%n+;
    return k;
}

//统计0的个数,并且通过一个vector来存放坐标
//p需要判断
vector<vector<int>> zero_num(int* p)
{
    int i,j;
    int k = ;

    vector<vector<int>> v( ,vector<int>());

    for (i=; i<; i++)
    {
        for (j=; j<; j++)
        {
            if (array[i][j] == )
            {
                v[k].push_back(i);
                v[k].push_back(j);
                k++;
            }
        }
    }
    *p = k;
    return v;
}


//随机插入一个数字,通空格的个数随机插入
//返回值为true就说明成功插入,否则未能插入
bool insert_num()
{
    int k = ;
    vector<vector<int>> v;
    v = zero_num(&k);

    if (k>)
    {
        int m = rand_pro_s(k);
        int x = v[m][];
        int y = v[m][];
        array[x][y] = pow(,rand_pro_ss());
        return true;
    }
    return false;
}



////////////////////////////////////获得键盘方向////////////////////////////////////////////////////////////
//获取方向
//返回值
//  1   上   72
//  2   下   80
//  3   左   75
//  4   右   77
int get_direction()
{
    char c1,c2;
    int ret = ;
    c1 = getch();
    if (!isascii(c1))
    {
        c2 = getch();
        switch(c2)
        {
        case : ret = ;break;
        case : ret = ;break;
        case : ret = ;break;
        case : ret = ;break;
        default: break;
        }
    }
    return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////移动操作////////////////////////////////////////////////////////////
//上下左右操作
//操作分为两步,首先将相邻的相同数字加起来,然后去除空格
//操作完之后需要插入数字

//之前分为两步的想法有问题,假如是两个相同数字之间有0的话,也是可以结合的

void up_dir()
{
    //完成第一步
    int i,j;
    for (i=; i<; i++)
    {
        /*for (j=0; j<3;)
        {
            if (array[j][i] == array[j+1][i])
            {
                array[j][i] = array[j][i] + array[j+1][i];
                array[j+1][i] = 0;
                j += 2;
                continue;
            }
            j++;
        }*/
        int k = ;
        int x = ; 
        int y = ;
        for (j=; j<; j++)
        {
            if (k==array[j][i] && k!=)
            {
                //array[x][y] = 2*array[x][y];
                //array[x][y] = 2*k;
                array[x][y] = *k;
                array[j][i] = ;
                k = ;
                continue;
            }
            if (array[j][i]!=)
            {
                k = array[j][i];
                //此处的bug是x和y分别应该存储j和i
                x = j;
                y = i;
            }
        }

    }

    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j<; j++)
        {
            //if (array[j][i])//此处有bug,假如k和j相等
            if (array[j][i])
            {
                if (k != j)
                {
                    array[k][i] = array[j][i];
                    array[j][i] = ;
                }
                k++;
            }
        }
    }


    insert_num();
    system("cls");//清屏
    display();
}

void down_dir()
{
    //完成第一步
    int i,j;
    for (i=; i<; i++)
    {
        /*for (j=3; j>0;)
        {
            if (array[j][i] == array[j-1][i])
            {
                array[j][i] = array[j][i] + array[j-1][i];
                array[j-1][i] = 0;
                j -= 2;
                continue;
            }
            j--;
        }*/
        int k = ;
        int x = ; 
        int y = ;
        for (j=; j>=; j--)
        {
            if (k==array[j][i] && k!=)
            {
                array[x][y] = *k;
                array[j][i] = ;
                k = ;
                continue;
            }
            if (array[j][i]!=)
            {
                k = array[j][i];
                x = j;
                y = i;
            }
        }
    }

    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j>=; j--)
        {
            if (array[j][i])
            {
                if (k != j)
                {
                    array[k][i] = array[j][i];
                    array[j][i] = ;
                }
                k--;
            }
        }
    }

    insert_num();
    system("cls");//清屏
    display();
}


void left_dir()
{
    int i,j;
    for (i=; i<; i++)
    {
        int k = ;
        int x = ; 
        int y = ;
        for (j=; j<; j++)
        {
            if (k==array[i][j] && k!=)
            {
                array[x][y] = *k;
                array[i][j] = ;
                k = ;
                continue;
            }
            if (array[i][j]!=)
            {
                k = array[i][j];
                x = i;
                y = j;
            }
        }

    }

    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j<; j++)
        {
            if (array[i][j])
            {
                if (k != j)
                {
                    array[i][k] = array[i][j];
                    array[i][j] = ;
                }
                k++;
            }
        }
    }

    insert_num();
    system("cls");//清屏
    display();
}


void right_dir()
{
    //完成第一步
    int i,j;
    for (i=; i<; i++)
    {
        int k = ;
        int x = ; 
        int y = ;
        for (j=; j>=; j--)
        {
            if (k==array[i][j] && k!=)
            {
                array[x][y] = *k;
                array[i][j] = ;
                k = ;
                continue;
            }
            if (array[i][j]!=)
            {
                k = array[i][j];
                x = i;
                y = j;
            }
        }
    }

    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j>=; j--)
        {
            if (array[i][j])
            {
                if (k != j)
                {
                    array[i][k] = array[i][j];
                    array[i][j] = ;
                }
                k--;
            }
        }
    }

    insert_num();
    system("cls");//清屏
    display();
}
////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////游戏结束////////////////////////////////////////////////////////////
//判断游戏结束
//将求解最大值和判断2048糅合在一起
bool iswin()
{
    int i,j;
    for (i=; i<; i++)
    {
        for (j=; j<; j++)
        {
            if (array[i][j] == )
            {
                return true;
            }
        }
    }
    return false;
}


//构造函数can_up...,不能改变全局变量的值
//只需要两个方向,水平或者垂直;
bool can_ver()
{
    int i,j;
    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j<; j++)
        {
            if (k==array[j][i] && k!=)
            {
                return true;
            }
            if (array[j][i]!=)
            {
                k = array[j][i];
            }
        }
    }
    return false;
}

bool can_hor()
{
    int i,j;
    for (i=; i<; i++)
    {
        int k = ;
        for (j=; j>=; j--)
        {
            if (k==array[i][j] && k!=)
            {
                return true;
            }
            if (array[i][j]!=)
            {
                k = array[i][j];
            }
        }
    }
    return false;
}

//怎么做到提前预判已经不能左右移动了
bool islose()
{
    int k = ;
    zero_num(&k);
    if (k>)
    {
        return false;
    }
    if (can_ver() || can_hor())
    {
        return false;
    }
    return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////



void main()
{
    new_game();
    int dir = ;
    while()
    {
        if (iswin())
        {
            cout<<"you win"<<endl;
            break;
        }
        if (islose())
        {
            cout<<"you lose"<<endl;
            break;
        }
        dir = get_direction();
        switch(dir)
        {
        case : up_dir();break;
        case : down_dir();break;
        case : left_dir();break;
        case : right_dir();break;
        default:break;
        }
    }

    system("pause");
}