学习于牛客网,小有改动:
https://www.nowcoder.com/project/index/8
一、依赖库:
linux:
apt-get install libncurses5-dev
Mac:
brew install ncurses
windows:
-
安装编译器MinGW
下载mingw-w64-install.exe 5.0.4版本,解压到本地目录,
然后把C:\mingw64\bin 加入到系统设置的路径里,
打开命令行控制台输入g++,确认有这个命令以保证安装是成功。 -
编译pdcurses库
下载pdcurses后解压到C:\pdcurs36目录,
命令行控制台cd到 C:\pdcurs36\wincon目录,运行 mingw32-make 命令编译pdcurses库,
编译成功后目录下有多个demo的exe文件以及一个pdcurses.a文件,这个文件是库文件。 -
下载地址
https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/
https://sourceforge.net/projects/pdcurses/files/pdcurses/3.6/pdcurs36.zip/download
二、开发要点:
一次只能合并相邻的两个数字
每次合并的时候,合并方向优先级高
判断游戏胜利或者失败
每次合并以后随机新出4的概率10%
三、编译命令:
windows上:
g++ 2048.cpp C:\pdcurs36\wincon\pdcurses.a -I C:\pdcurs36\ -o 2048
// 当所需文件在当前文件夹内
g++ 2048.cpp pdcurses.a -I . -o 2048
linux上:
g++ 2048.cpp -l ncurses -o 2048
四、开发步骤:
引入curses库
绘制游戏界面
游戏状态切换
重启初始化游戏
向左移动
向其他方向移动
游戏胜负判定
五、拓展功能:
分数实时记录
最高分记录
游戏保存与读取
界面友好显示
刷新个数设置(内部)
六、生成代码:
#include <iostream>
#include <vector>
#include <string>
#include <curses.h>
#include <cstdlib>
#include <cstdio>
#include <ctime>
using namespace std;
//格子横纵数目和宽度
#define N 4
#define WIDTH 5
//设置初始化新数字个数
#define STNUM 1
//设置游戏胜利条件
#define TARGET 2048
//状态表
#define WIN 0
#define LOSE 1
#define NORM 2
#define QUIT 3
//2048类定义
class Game2048
{
public:
Game2048() {
setData();
}
//获取游戏状态
int getStatus() {
return status;
}
//获取用户输入,设置状态
void getChar() {
char ch = getch();
if (ch>='a' && ch<='z')
ch = ch - 32;
//上下左右移动进行游戏
if (status == NORM) {
//状态更新
bool update = false;
if (ch == 'A') {
update = moveLeft();
} else if (ch == 'S') {
rotate();
update = moveLeft();
rotate();
rotate();
rotate();
} else if (ch == 'D') {
rotate();
rotate();
update = moveLeft();
rotate();
rotate();
} else if (ch == 'W') {
rotate();
rotate();
rotate();
update = moveLeft();
rotate();
}
if (update) {
randNew(STNUM);
if (isOver()) {
status = LOSE;
}
}
}
//退出和刷新
if (ch == 'Q')
status = QUIT;
else if (ch == 'R')
restart();
else if (ch == 'N')
dataSave();
else if (ch == 'M')
dataLoad();
}
//绘制游戏界面
void draw() {
clear(); //清理屏幕
const int xoffset = 5; //设置横偏移量
const int yoffset = 40; //设置纵偏移量
//绘制偶数行,全置为‘-’后再部分替换为‘+’
for (int i = 0; i <= N; i++) {
for (int j = 0; j < N*WIDTH; j++) {
mvaddch(2*i + xoffset, j + yoffset, '-');
}
for (int j = 0; j <= N; j++) {
mvaddch(2*i + xoffset, j*WIDTH + yoffset, '+');
}
}
//绘制奇数行,数字右对齐
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
mvaddch(2*i + 1 + xoffset, j*WIDTH + yoffset, '|');
drawNum(2*i + 1 + xoffset, (j+1)*WIDTH + yoffset, data[i][j]);
}
mvaddch(2*i + 1 + xoffset, N*WIDTH + yoffset, '|');
}
//绘制游戏操作方法及得分
string strOperate = "W(UP),S(DOWN),A(LEFT),D(RIGHT),R(RESTART),N(SAVE),M(LOAD),Q(QUIT)";
mvprintw(2*N + 3 + xoffset, N/2*WIDTH + yoffset - strOperate.length()/2, strOperate.data());
//绘制得分及最高分
mvprintw(N/2 + xoffset, yoffset/4, (" max: "+to_string(max)).data());
mvprintw(3*N/2 + xoffset, yoffset/4, ("score: "+to_string(score)).data());
//绘制状态信息
string strStatus;
if (status == WIN)
strStatus = "YOU WIN";
if (status == LOSE)
strStatus = "YOU LOSE";
mvprintw(2*N + 6 + xoffset, N/2*WIDTH + yoffset - strStatus.length()/2, strStatus.data());
//初次提醒用户输入R
if (status != NORM) {
string strStart = "PRESS R TO START !";
mvprintw(2*N + 7 + xoffset, N/2*WIDTH + yoffset - strStart.length()/2, strStart.data());
start = false;
}
}
private:
int data[N][N];
int save[N][N];
int status = -1;
int score = 0;
int Score = 0;
int max = 0;
bool start = true;
//类初始化时游戏界面数字
void setData() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
data[i][j] = 0;
save[i][j] = 0;
}
}
}
//右对齐绘制数字
void drawNum(int x, int y, int num) {
while (num > 0){
mvaddch(x, --y, num%10+'0');
num = num / 10;
}
}
//重新开始游戏设置
void restart() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
data[i][j] = 0;
}
}
//超出范围自动设置为1
if (STNUM>=N*N || STNUM<0)
randNew(1);
else
randNew(STNUM);
score = 0;
status = NORM;
}
//随机产生任意个数字
bool randNew(int num) {
for (int cnt = 0; cnt < num; cnt++) {
vector <int> emptyPos;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (data[i][j] == 0)
emptyPos.push_back(i*N+j);
}
}
if (emptyPos.size() == 0)
return false;
int pos = emptyPos[rand()%emptyPos.size()];
data[pos/N][pos%N] = (rand()%10==0 ? 4:2);
}
return true;
}
//游戏左移操作
bool moveLeft() {
int compare[N][N];
for (int i = 0; i < N; i++) {
int cur = 0; //比较值
int pos = 0; //存放位置
for (int j = 0; j < N; j++) {
compare[i][j] = data[i][j];
//从不为0的位置开始
if (data[i][j] == 0)
continue;
//从cur不为0开始比较求和
if (cur == 0) {
cur = data[i][j];
}else {
if (data[i][j] == cur) {
data[i][pos] = cur<<1;
score += data[i][pos];
cur = 0;
if (data[i][pos] == TARGET)
status = WIN;
if (max <= score)
max = score;
}else {
data[i][pos] = cur;
cur = data[i][j];
}
//存放位置右移一位
pos++;
}
//已经向左添加,此位置重设为0
data[i][j] = 0;
}
//比较值不为0则放入
if (cur != 0)
data[i][pos] = cur;
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (compare[i][j] != data[i][j])
return true;
}
}
return false;
}
//顺时针旋转90度
void rotate() {
int tempdata[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
tempdata[i][j] = data[N-j-1][i];
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
data[i][j] = tempdata[i][j];
}
}
}
//判断游戏是否结束
bool isOver() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (data[i][j] == 0)
return false;
if (i+1<N && data[i][j]==data[i+1][j])
return false;
if (j+1<N && data[i][j]==data[i][j+1])
return false;
}
}
return true;
}
//记录当前游戏记录
void dataSave() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
save[i][j] = data[i][j];
}
}
Score = score;
}
//读取之前游戏记录
void dataLoad() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
data[i][j] = save[i][j];
}
}
score = Score;
status = NORM;
}
};
// 初始化及设置ncurses
void initialize() {
initscr(); //初始化ncurses
cbreak(); //设置按键直接交互
noecho(); //设置按键不回显
curs_set(0); //设置光标不可见
srand(time(NULL)); //设置随机数
}
// 恢复ncurses状态
void shutdown() {
endwin(); //恢复终端机状态
}
int main() {
initialize();
Game2048 game;
do {
game.draw();
game.getChar();
}while(game.getStatus() != QUIT);
shutdown();
}