play snake on linux
在写完超Low的windows上的贪吃蛇后
被人吐槽了几个方面:
1.界面真的Low,开始,结束,游戏中,都太简陋了...
2.每次都清屏在输出字符矩阵的解决方案...太晃眼了
3.一个BUG,为了解决贪吃蛇隔固定时间time移动一个单位的问题
我们写的是while(距上次移动时间 < time && 没有键盘方向键的读入);
于是我们惊喜的发现,只要一直摁方向键,就不必等待固定时间time
而是会直接下一步移动...手动加快贪吃蛇移动速度...
但是我们暂时并不想改进这个程序...毕竟怎么说还是能玩一玩的
于是ytz决定在自己的deepin系统上写一个能运行的贪吃蛇...
先想到的方法当然是直接把windows上的代码拿来改一改啊...
但很快我们就遇到了重重障碍!
1.我们使用了conio.h中的_kbhit函数来判断是否有键盘读入
然而linux系统下是没有conio.h这个库的...
百度了一下linux下也没有自带库函数有相同功能
于是我们就百度了一个手动实现_kbhit函数加进去
(这个实现原理不是很懂我是照抄的...)
2.conio.h中的getch函数同样需要替代品
这时候就有人指出明路,curses.h库里有啊
然后我们需要先安装这个库,在终端输入
sudo apt-get install libncurses5-dev
回车即可开始安装
然后编译时需要加入 -lncurses 命令
比如 g++ -o Snake -lncurses Snake.cpp
否则编译无法通过
3.啊,编译通过了!
我们愉快的运行一下吧!
运行出了一坨屎!
我们百度一下curses.h 这个库
发现是一个图形库,类似于大一学习C和C++的时候
老师提供的windows上的的第三方库ege.h
只不过curses的评价似乎比ege好一点2333
然后ege那个你懂的吧,开始运行进入图形界面后
各种函数失效,printf,scanf...
以及输出基本靠定位定点输出,\n,\t 什么的都会gg你懂的吧
4.于是我们把pritf全都换成printw
抛弃 \n \t 改用move来移动光标位置
然后只在游戏开始时使用一次清屏system("clear")
其它都使用 curses.h 里的 refresh 函数即可
(我们的 printw 是输出在逻辑屏幕上的,
refresh 函数用来同步逻辑屏幕和物理屏幕
物理屏幕即我们看到的屏幕
然后refresh函数表现的十分流畅的
最重要的是,它不晃眼!!!)
然后终于能正确打印出字符矩阵了!
5.汉字在终端显示成乱码
百度了很久没有找到解决方案
我们就退让一步,把汉字都改成了英文
然后调用noecho函数,让我们的按键不显示在屏幕上
尝试运行......发现我们造出来了一条瘫痪蛇...
不贪吃了瘫痪了...我们不摁下方向键它自己不动...
先把瘫痪蛇代码放上来吧
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <curses.h> #define map(pos) map[pos.x][pos.y] char map[][]; struct point {
int x, y; void _rand() {
x = rand() % + ;
y = rand() % + ;
} bool operator == (const point &a) const {
return x == a.x && y == a.y;
} }; int head, tail;
point snake[], food, next;
int dir, grade, length, uptime; inline int _kbhit() {
termios oldt, newt;
int ch, oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= -(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, );
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF) {
ungetc(ch, stdin);
return ;
}
return ;
} inline void find_food() {
do {
food._rand();
}while(map(food) != ' ');
map(food) = '*';
} inline void update() {
//system("clear");
for(int i = ;i < ;i ++) {
for(int j = ;j < ;j ++)
move(i + , + j * ), printw("%c ", map[i][j]);
if(i == ) move(i + , ), printw("Level:%d", grade);
if(i == ) move(i + , ), printw("length:%d", length);
if(i == ) move(i + , ), printw("Time_interval:");
if(i == ) move(i + , ), printw("%d ms", uptime);
}
refresh();
} inline bool GO() {
bool timeover = ;
double start = (double) clock() / CLOCKS_PER_SEC;
while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(_kbhit()));
if(timeover) dir = getch();
next = snake[head];
switch (dir) {
case 'w':next.x -= ;break;
case 's':next.x += ;break;
case 'a':next.y -= ;break;
case 'd':next.y += ;break;
default:
move(, );
printw("Game Over!");
return ;
}
if(!next.x || next.x == || !next.y || next.y == ) {
move(, );
printw("Game Over!");
return ;
}
if(map(next) != ' ' && !(next == food)) {
move(, );
printw("Game Over!");
return ;
}
if(length == ) {
move(, );
printw("Game Over!");
return ;
}
return ;
} int main() {
initscr();
clear();
curs_set();
srand();
for(int i = ;i <= ;i ++)
for(int j = ;j <= ;j ++)
map[i][j] = ' ';
for(int i = ;i < ;i ++)
map[i][] = map[][i] = map[][i] = map[i][] = '!';
map[][] = map[][] = 'o', map[][] = '@';
snake[] = (point){, };
snake[] = (point){, };
snake[] = (point){, };
head = , tail = , grade = , length = , uptime = ;
find_food(), dir = 'd'; move(, );
printw("Let's play snake!");
refresh();
double start;
for(int i = ;i >= ;i --) {
start = (double)clock() / CLOCKS_PER_SEC;
while((double)clock() / CLOCKS_PER_SEC <= start + );
if(i > ) {
//system("clear");
move(, );
printw("Enter the countdown:%d", i);
refresh();
}
else {
update();
}
} while() {
int tmp = GO();
refresh();
if(tmp) {
if(next == food) {
length ++;
if(length % == ) {
grade ++;
if(uptime >= ) uptime -= ;
}
map(next) = '@';
map(snake[head]) = 'o';
head = (head + ) % ;
snake[head] = next;
find_food(), update();
}
else {
map(snake[tail]) = ' ';
tail = (tail + ) % ;
map(next) = '@';
map(snake[head]) = 'o';
head = (head + ) % ;
snake[head] = next;
update();
}
}
else break;
}
getch();
endwin();
return ;
}
6.我们经过实验发现
问题出在getch和kbhit函数的配合上面
于是我们修改一下kbhit函数
在获得键盘读入之后不再把字符存入缓冲区
而是直接return回去就好了!
然后为了解决开始提到的第三个问题
我们在获得键盘读入之后
依旧等待固定时间间隔结束再让蛇移动即可!
然后为了解决第一个问题...
我加了个最终分数提示...
重新开始游戏我懒就没加...
至此我们的windows贪吃蛇魔改之linux版基本就完成了!
(我太懒了所以依然没有加注释
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <curses.h> #define map(pos) map[pos.x][pos.y] char map[][]; struct point {
int x, y; void _rand() {
x = rand() % + ;
y = rand() % + ;
} bool operator == (const point &a) const {
return x == a.x && y == a.y;
} }; int head, tail;
point snake[], food, next;
int dir, grade, length, uptime; inline int _kbhit() {
termios oldt, newt;
int ch, oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= -(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, );
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF) return ch;
return ;
} inline void find_food() {
do {
food._rand();
}while(map(food) != ' ');
map(food) = '*';
} inline void update() {
//system("clear");
for(int i = ;i < ;i ++) {
for(int j = ;j < ;j ++)
move(i + , + j * ), printw("%c ", map[i][j]);
if(i == ) move(i + , ), printw("Level:%d", grade);
if(i == ) move(i + , ), printw("length:%d", length);
if(i == ) move(i + , ), printw("Time_interval:");
if(i == ) move(i + , ), printw("%d ms", uptime);
}
refresh();
} inline bool GO() {
int ch;
bool timeover = ;
double start = (double) clock() / CLOCKS_PER_SEC;
while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(ch = _kbhit()));
if(timeover) {
while(timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0);
dir = ch;
}
next = snake[head];
switch (dir) {
case 'w':next.x -= ;break;
case 's':next.x += ;break;
case 'a':next.y -= ;break;
case 'd':next.y += ;break;
default:return ;
}
if(!next.x || next.x == || !next.y || next.y == ) return ;
if(map(next) != ' ' && !(next == food)) return ;
if(length == ) return ;
return ;
} int main() {
initscr();
noecho();
clear();
curs_set();
srand();
for(int i = ;i <= ;i ++)
for(int j = ;j <= ;j ++)
map[i][j] = ' ';
for(int i = ;i < ;i ++)
map[i][] = map[][i] = map[][i] = map[i][] = '!';
map[][] = map[][] = 'o', map[][] = '@';
snake[] = (point){, };
snake[] = (point){, };
snake[] = (point){, };
head = , tail = , grade = , length = , uptime = ;
find_food(), dir = 'd'; move(, );
printw("Let's play snake!");
refresh();
double start;
for(int i = ;i >= ;i --) {
start = (double)clock() / CLOCKS_PER_SEC;
while((double)clock() / CLOCKS_PER_SEC <= start + );
if(i > ) {
//system("clear");
move(, );
printw("Enter the countdown:%d", i);
refresh();
}
else {
update();
}
} while() {
int tmp = GO();
refresh();
if(tmp) {
if(next == food) {
length ++;
if(length % == ) {
grade ++;
if(uptime >= ) uptime -= ;
}
map(next) = '@';
map(snake[head]) = 'o';
head = (head + ) % ;
snake[head] = next;
find_food(), update();
}
else {
map(snake[tail]) = ' ';
tail = (tail + ) % ;
map(next) = '@';
map(snake[head]) = 'o';
head = (head + ) % ;
snake[head] = next;
update();
}
}
else {
move(, );
printw("Game Over!");
move(, );
printw("Your Final Score : %d", length);
break;
}
}
getch();
endwin();
return ;
}
感谢参考资料
5.在linux下面实现检测按键(Linux中kbhit()函数的实现)
play snake on linux的更多相关文章
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- linux下部署svn服务器
系统Linux debian 2.6.32-5-686 先安装svn工具:apt-get install subversion,耐心等待安装完成.安装完成后svn客户端.服务器都有了. 接者建立svn ...
- [转]Linux(centOS6.5)下SVN的安装、配置及开机启动
1.检查是否已安装 rpm -qa subversion 如果要卸载旧版本: yum remove subversion 2.安装 yum install subversion PS:yum inst ...
- linux系统Centos环境下搭建SVN服务器及权限配置
linux系统Centos环境下如何搭建SVN服务器以及svnserve.conf.authz.passwd配置文件详细介绍 至于svn的概念,这里就不做详细阐述了,可以自行百度.简单来讲就是一个 ...
- linux的一些软件基本安装
买了个腾讯云的服务器,开始玩起来了,先装环境吧. JAVA安装 安装个java yum -y install java-1.7.0-openjdk* 查看java版本 java -version 可以 ...
- [转] linux nc命令
[From] https://blog.csdn.net/freeking101/article/details/53289198 NC 全名 Netcat (网络刀),作者是 Hobbit & ...
- linux(centeros)svn的安装
SVN linux搭建svn服务器参考:http://www.cnblogs.com/chaichuan/p/3758173.htmlSubversion(SVN) 是一个开源的版本控制系統, 也就是 ...
- Linux 内核概述 - Linux Kernel
Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...
- 死磕内存篇 --- JAVA进程和linux内存间的大小关系
运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...
随机推荐
- 数据库登陆失败原因: 未与信任 SQL Server 连接相关联
解决方案:用户 'sa' 登录失败.原因: 未与信任 SQL Server 连接相关联. 问题简述: 用户 'sa' 登录失败.原因: 未与信任 SQL Server 连接相关联. 说明: 执行当前 ...
- vue学习记录(一)—— vue开发调试神器vue-devtools安装
网上有些贴子少了至关重要的一步导致我一直没装上, 切记!!install后还需build,且install和build都在vue-devtools文件夹内执行 github下载地址 点击跳转 具体步骤 ...
- sql数据库中常用连接
很简单的知识点,今天有点搞不清楚左外连接,右外连接:详见以下: --表stu id name 1, Jack 2, Tom 3, Kity 4, nono --表exam id grade 1, 56 ...
- 【NOIP练习赛】开车
[NOIP练习赛]T2.开车 Description 老司机小 Q 要在一个十字路口指挥车队开车,这个十字路口可 以描述为一个 n*n 的矩阵,其中第 2 行到第 n-1 行都各有一道横向车 道,第 ...
- [Luogu 1052] noip 05 过河
[Luogu 1052] noip 05 过河 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是 ...
- 快速搭建Hadoop及HBase分布式环境
本文旨在快速搭建一套Hadoop及HBase的分布式环境,自己测试玩玩的话ok,如果真的要搭一套集群建议还是参考下ambari吧,目前正在摸索该项目中.下面先来看看怎么快速搭建一套分布式环境. 准备 ...
- 【转】Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...
- CF830A/831D Office Keys
思路: 问题的关键在于对钥匙按照位置排序之后,最终选择的n个钥匙一定是其中的一个连续的区间. 实现: #include <iostream> #include <cstdio> ...
- MySQL数据库的三大范式定义,作用—------你所期待的最佳答案
第一范式:确保每列的原子性. 如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式. 例如:顾客表(姓名.编号.地址.……)其中"地址"列还可 ...
- JS——滚动条
1.核心思想与之前的拖拽盒子是一样的 2.完全将鼠标在盒子中的坐标给滚动条是错的,因为这样会使滚动条顶部立刻瞬间移动到鼠标位置 3.必须在鼠标按下事件时记住鼠标在滚动条内部的坐标,再将鼠标在盒子中的坐 ...