Linux控制台版本号2048
在Github上看到一个荷兰人写的linux控制台版的2048,用的C语言。感觉非常有意思。
原网址在这里。
读了一下他的源代码,感觉写的不错。就厚着脸皮加了一些中文凝视,源代码例如以下:
/*
============================================================================
Name : 2048.c
Author : Maurits van der Schee
Description : Console version of the game "2048" for GNU/Linux
============================================================================
*
Note by Zhengmingpei,China
Time:2014.10.13
Contact:http://Zhengmingpei.github.com
Email:yueyawanbian@gmail.com
*/ #define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include <signal.h> #define SIZE 4
uint32_t score=0;
uint8_t scheme=0; // 依据value获取相应的颜色,将包括设置终端颜色的字符串复制给color
void getColor(uint16_t value, char *color, size_t length) {
// 声明三个颜色数组。用一维数组,但每一个奇数位和偶数位组成一个前后景色
// 后两个数组分别相应程序的启动选项"blackwhite","bluered"
uint8_t original[] = {8,255,1,255,2,255,3,255,4,255,5,255,6,255,7,255,9,0,10,0,11,0,12,0,13,0,14,0,255,0,255,0};
uint8_t blackwhite[] = {232,255,234,255,236,255,238,255,240,255,242,255,244,255,246,0,248,0,249,0,250,0,251,0,252,0,253,0,254,0,255,0};
uint8_t bluered[] = {235,255,63,255,57,255,93,255,129,255,165,255,201,255,200,255,199,255,198,255,197,255,196,255,196,255,196,255,196,255,196,255};
uint8_t *schemes[] = {original,blackwhite,bluered};
uint8_t *background = schemes[scheme]+0;
uint8_t *foreground = schemes[scheme]+1;
if (value > 0) while (value >>= 1)
// value不断右移一位。直到值变为0。实现每一个二进制一个不同的颜色
{
if (background+2<schemes[scheme]+sizeof(original)) {
background+=2;
foreground+=2;
}
}
//linux下终端及字体颜色设置语句的字符串
snprintf(color,length,"\033[38;5;%d;48;5;%dm",*foreground,*background);
} // 绘制数据板,数据板共3×4行。7×4列
void drawBoard(uint16_t board[SIZE][SIZE]) {
int8_t x,y;
// \033[m:关闭全部属性
char color[40], reset[] = "\033[m";
// \033[H:调整光标位置
printf("\033[H"); printf("2048.c %17d pts\n\n",score); //数据板共3×4行。7×4列
for (y=0;y<SIZE;y++) {
//首行打印空白
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
printf(" ");
//reset 重置。避免对非数据板部分造成影响
printf("%s",reset);
}
printf("\n");
//次行打印数字,数字居中
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
if (board[x][y]!=0) {
char s[8];
//此处注意,是board[x][y]而不是yx
snprintf(s,8,"%u",board[x][y]);
int8_t t = 7-strlen(s);
printf("%*s%s%*s",t-t/2,"",s,t/2,"");
} else {
printf(" · ");
}
printf("%s",reset);
}
printf("\n");
//末行打印空白
for (x=0;x<SIZE;x++) {
getColor(board[x][y],color,40);
printf("%s",color);
printf(" ");
printf("%s",reset);
}
printf("\n");
}
printf("\n");
printf(" ←,↑,→,↓ or q \n");
//疑似回车
printf("\033[A");
} // 查找一维数组中x左側待合并数的坐标,stop为检查点
int8_t findTarget(uint16_t array[SIZE],int8_t x,int8_t stop) {
int8_t t;
//若x为第一个数,左边无数。直接返回x
if (x==0) {
return x;
}
//遍历x左边的坐标
for(t=x-1;t>=0;t--) {
//合并算法:
//1.t处的数不为0且与x处的数不相等,返回t+1
//2.t处的数不为0且与x处的数相等,返回t
//3.t处的数为0。依据stop推断是否向前查找。防止多次合并
if (array[t]!=0) {
if (array[t]!=array[x]) {
// merge is not possible, take next position
return t+1;
}
return t;
} else {
// we should not slide further, return this one
if (t==stop) {
return t;
}
}
}
// we did not find a
return x;
} //对一维数组进行移动
bool slideArray(uint16_t array[SIZE]) {
bool success = false;
//声明当前位置。待合并的位置。检查点
int8_t x,t,stop=0; for (x=0;x<SIZE;x++) {
if (array[x]!=0) {
t = findTarget(array,x,stop);
// 假设待合并的位置与当前位置不相等,进行移动或者合并
// if target is not original position, then move or merge
if (t!=x) {
// 假设待合并的位置不是0,右移检查点stop
// if target is not zero, set stop to avoid double merge
if (array[t]!=0) {
score+=array[t]+array[x];
stop = t+1;
}
array[t]+=array[x];
array[x]=0;
success = true;
}
}
}
return success;
} //旋转数据板。向右旋转90度。这样能够用一个方向的数组移动间接控制四个方向的移动
void rotateBoard(uint16_t board[SIZE][SIZE]) {
int8_t i,j,n=SIZE;
uint16_t tmp;
//环形旋转,先外而内,先左后右
for (i=0; i<n/2; i++){
for (j=i; j<n-i-1; j++){
tmp = board[i][j];
board[i][j] = board[j][n-i-1];
board[j][n-i-1] = board[n-i-1][n-j-1];
board[n-i-1][n-j-1] = board[n-j-1][i];
board[n-j-1][i] = tmp;
}
}
} //向上移动数据板
bool moveUp(uint16_t board[SIZE][SIZE]) {
bool success = false;
int8_t x;
for (x=0;x<SIZE;x++) {
//对每一列做移动或者合并处理。
//这里是列而不是行,与前面的输出顺序有关
success |= slideArray(board[x]);
//仅仅要有一列成功,就成功
}
return success;
} // 左移:向右旋转90度,向上合并。再旋转3个90度
bool moveLeft(uint16_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
return success;
} // 下移:向右旋转2个90度。向上合并,再旋转2个90度
bool moveDown(uint16_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
rotateBoard(board);
return success;
} // 右移:向右旋转3个90度,向上合并。再旋转1个90度
bool moveRight(uint16_t board[SIZE][SIZE]) {
bool success;
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
success = moveUp(board);
rotateBoard(board);
return success;
} bool findPairDown(uint16_t board[SIZE][SIZE]) {
bool success = false;
int8_t x,y;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE-1;y++) {
if (board[x][y]==board[x][y+1]) return true;
}
}
return success;
} // 计算数据板是否已满
int16_t countEmpty(uint16_t board[SIZE][SIZE]) {
int8_t x,y;
int16_t count=0;
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
if (board[x][y]==0) {
count++;
}
}
}
return count;
} // 检查游戏是否结束
bool gameEnded(uint16_t board[SIZE][SIZE]) {
bool ended = true;
// 假设有空位,未结束
if (countEmpty(board)>0) return false;
// 横向检查,有相等相邻数。未结束
if (findPairDown(board)) return false;
rotateBoard(board);
// 旋转一次,纵向检查。有相等相邻数,未结束
if (findPairDown(board)) ended = false;
rotateBoard(board);
rotateBoard(board);
rotateBoard(board);
return ended;
} // 随机重置数据板
void addRandom(uint16_t board[SIZE][SIZE]) {
// 全局变量,是否已初始化
static bool initialized = false;
// x,y 坐标
int8_t x,y;
// r 随机位置,len 全部为空的数据板数据长度
int16_t r,len=0;
// n 随机数据, list 全部为空的数据板位置
uint16_t n,list[SIZE*SIZE][2]; if (!initialized) {
srand(time(NULL));
initialized = true;
} // 找出数据板上全部为空的坐标
for (x=0;x<SIZE;x++) {
for (y=0;y<SIZE;y++) {
if (board[x][y]==0) {
list[len][0]=x;
list[len][1]=y;
len++;
}
}
} // 假设有为空的情况,才填充数据
if (len>0) {
r = rand()%len;
x = list[r][0];
y = list[r][1];
n = ((rand()%10)/9+1)*2;
board[x][y]=n;
}
} // 设置输入模式,在行缓冲和无缓冲中切换
void setBufferedInput(bool enable) {
static bool enabled = true;
static struct termios old;
struct termios new; if (enable && !enabled) {
// restore the former settings
tcsetattr(STDIN_FILENO,TCSANOW,&old);
// set the new state
enabled = true;
} else if (!enable && enabled) {
// get the terminal settings for standard input
tcgetattr(STDIN_FILENO,&new);
// we want to keep the old setting to restore them at the end
old = new;
// disable canonical mode (buffered i/o) and local echo
new.c_lflag &=(~ICANON & ~ECHO);
// set the new settings immediately
tcsetattr(STDIN_FILENO,TCSANOW,&new);
// set the new state
enabled = false;
}
} int test() {
uint16_t array[SIZE];
uint16_t data[] = {
0,0,0,2, 2,0,0,0,
0,0,2,2, 4,0,0,0,
0,2,0,2, 4,0,0,0,
2,0,0,2, 4,0,0,0,
2,0,2,0, 4,0,0,0,
2,2,2,0, 4,2,0,0,
2,0,2,2, 4,2,0,0,
2,2,0,2, 4,2,0,0,
2,2,2,2, 4,4,0,0,
4,4,2,2, 8,4,0,0,
2,2,4,4, 4,8,0,0,
8,0,2,2, 8,4,0,0,
4,0,2,2, 4,4,0,0
};
uint16_t *in,*out;
uint16_t t,tests;
uint8_t i;
bool success = true; tests = (sizeof(data)/sizeof(data[0]))/(2*SIZE);
for (t=0;t<tests;t++) {
in = data+t*2*SIZE;
out = in + SIZE;
for (i=0;i<SIZE;i++) {
array[i] = in[i];
}
slideArray(array);
for (i=0;i<SIZE;i++) {
if (array[i] != out[i]) {
success = false;
}
}
if (success==false) {
for (i=0;i<SIZE;i++) {
printf("%d ",in[i]);
}
printf("=> ");
for (i=0;i<SIZE;i++) {
printf("%d ",array[i]);
}
printf("expected ");
for (i=0;i<SIZE;i++) {
printf("%d ",in[i]);
}
printf("=> ");
for (i=0;i<SIZE;i++) {
printf("%d ",out[i]);
}
printf("\n");
break;
}
}
if (success) {
printf("All %u tests executed successfully\n",tests);
}
return !success;
} void signal_callback_handler(int signum) {
printf(" TERMINATED \n");
setBufferedInput(true);
printf("\033[?25h");
exit(signum);
} int main(int argc, char *argv[]) {
uint16_t board[SIZE][SIZE];
char c;
bool success; if (argc == 2 && strcmp(argv[1],"test")==0) {
return test();
}
if (argc == 2 && strcmp(argv[1],"blackwhite")==0) {
scheme = 1;
}
if (argc == 2 && strcmp(argv[1],"bluered")==0) {
scheme = 2;
} // 33[?25l 隐藏光标
// 33[2J 清屏
// 33[H 设置光标位置
printf("\033[?25l\033[2J\033[H"); // register signal handler for when ctrl-c is pressed
signal(SIGINT, signal_callback_handler); // 将数据清为0
memset(board,0,sizeof(board));
// 加入两次随机数,由于初始化时产生2个随机数
addRandom(board);
addRandom(board);
// 绘制数据板
drawBoard(board);
// 禁用缓存输入,终端支持按字符读取且不回显
setBufferedInput(false);
// 游戏主循环
while (true) {
c=getchar();
switch(c) {
case 97: // 'a' key
case 104: // 'h' key
case 68: // left arrow
success = moveLeft(board); break;
case 100: // 'd' key
case 108: // 'l' key
case 67: // right arrow
success = moveRight(board); break;
case 119: // 'w' key
case 107: // 'k' key
case 65: // up arrow
success = moveUp(board); break;
case 115: // 's' key
case 106: // 'j' key
case 66: // down arrow
success = moveDown(board); break;
default: success = false;
}
//合并成功,则又一次绘制
if (success) {
drawBoard(board);
usleep(150000);
addRandom(board);
drawBoard(board);
if (gameEnded(board)) {
printf(" GAME OVER \n");
break;
}
}
// 假设输入是 q 的话。打开行缓冲,显示光标
if (c=='q') {
printf(" QUIT? (y/n) \n");
while (true) {
c=getchar();
if (c=='y'){
setBufferedInput(true);
printf("\033[?25h");
exit(0);
}
else {
drawBoard(board);
break;
}
}
}
if (c=='r') {
printf(" RESTART? (y/n) \n");
while (true) {
c=getchar();
if (c=='y'){
memset(board,0,sizeof(board));
addRandom(board);
addRandom(board);
drawBoard(board);
break;
}
else {
drawBoard(board);
break;
}
}
}
}
setBufferedInput(true); printf("\033[? 25h"); return EXIT_SUCCESS;
}
Linux控制台版本号2048的更多相关文章
- 哪个 Linux 内核版本号是 “稳定的”? | Linux 中国
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/79329760 https://mmbi ...
- 实用Linux控制台命令
实用Linux控制台命令 screen 例如用Xshell连接 服务器 screen -ls 列出当前用户所有的screen screen 回车直接创建新的screen screen -S scree ...
- 向linux内核版本号添加字符/为何有时会自动添加“+”号
转载:http://blog.csdn.net/adaptiver/article/details/7225980 1. 引子 编译2.6.35.7 kernel版本的时候发现,“2.6.35.7 ...
- 查询linux发行版本号方法总结
了解Linux发行版本的版本号是一项非常重要的事情,大多数软件对系统的版本都有要求,发行版本号与软件不匹配,软件将无法安装或者无法使用.这边集合市面上流行的Linux发行版本版本号查询方法.有了这 ...
- 查看linux系统版本号命令
一.查看内核版本号命令: 1) [root@SOR_SYS ~]# cat /proc/version Linux version 2.6.18-238.el5 (mockbuild@x86-012. ...
- 向linux内核版本号添加字符/为何有时会自动添加"+"号或者"xxx-dirty"【转】
本文转载自:https://blog.csdn.net/kangear/article/details/17020835 原文地址:http://blog.csdn.net/adaptiver/art ...
- 怎样通过Qt编写C/C++代码查询当前Linux的版本号?
遇到一个问题:如题. 我的开发环境是:嵌入式ARM + Linux系统 + Qt 4.5 + C/C++ 现在需要查询 当前Linux系统的版本号. 问题: 1)Qt 4.5 提供怎样的API来获取? ...
- Linux 控制台
shell shell命令分为两种:分别是内部命令和外部命令. 内部命令:在安装的时候嵌入系统内核. 外部命令:以文件的形式存在. 可以使用type命令查看是内部命令还是外部命令. Linux中,默认 ...
- Linux 控制台/终端/tty/shell
一.简介 使用linux已经有一段时间,却一直弄不明白这几个概念之间的区别.这些概念本身有着非常浓厚的历史气息,随着时代的发展,他们的含义也在发生改变,它们有些已经失去了最初的含义,但是它们的名字却被 ...
随机推荐
- 使用ZeppelinHub来存储和展示ZeppelinNoteBook
0.序 说实在的这个功能太赞了 在一开始接触的时候不知道有这个功能,我尝试做一下配置,发现非常的棒. 棒的原因有两点: 可以在随时随地有互联网的地方访问自己的ZeppelinHub来查看Zeppeli ...
- dotnet core 发布配置(测试数据库和正式数据库自动切换)
一.起源 在进行项目开发时,常常要求开发环境,测试环境及正式环境的分离,并且不同环境运行的参数都是不一样的,比如监听地址,数据库连接信息等.当然我们把配置信息保存到一个文件中,每次发布的时候,可以先修 ...
- Oracle 当输入参数允许为空时
场景: 有一个存储过程p_test 带有多个输入参数code.name.number p_test(code IN VARCHAR2,nameIN VARCHAR2,number IN VARCHAR ...
- C# 多线程系列(一)
线程是怎样工作的 1.多线程由一个线程调度器来进行内部管理,一个功能是CLR常常委托给操做系统. 一个线程调度器确保所有激活的线程在执行期间被合适的分配,等待或者阻塞的线程(比如,一个独占锁或者等待用 ...
- 全局设置border-box
全局设置 border-box 很好,更符合我们通常对一个「盒子」尺寸的认知.,其次它可以省去一次又一次的加加减减,它还有一个关键作用——让有边框的盒子正常使用百分比宽度.但是使用了 border-b ...
- Android Unresolved Dependencies
在Android Studio的开发中,在软件中集成了ButterKnife插件,另外需要集成ButterKnife的jar包.因为本地没有现成的,所以在module的build.gradle文件中添 ...
- shell脚本操作mysql数据库,使用mysql的-e参数可以执行各种sql的(创建,删除,增,删,改、查)等各种操作
mysql -hhostname -Pport -uusername -ppassword -e 相关mysql的sql语句,不用在mysql的提示符下运行mysql,即可以在shell中操作m ...
- javascrip this指向问题深入理解
在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 1. 一般用处 对 ...
- BZOJ [POI2004]PRZ 状压DP_二进制_骚操作
二进制就是比谁更sao... Code: #include <bits/stdc++.h> #define setIO(s) freopen(s".in"," ...
- 记录:Ubuntu下升级Python从2.x到3.x
一.安装Python3 在Ubuntu中的终端输入:sudo apt-get install python3 提示资源被锁住,可能有另外一个程序在占用此资源. 解决方法:输入以下指令解锁资源 sudo ...