刚学完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");
}