解析一个C语言俄罗斯方块游戏,包你看了就会

最近在看俄罗斯方块的游戏,看到一个大神写的俄罗斯方块代码,非常非常优秀,拿出来解析给大家看看,也希望大家自己尝试运行试试,从中能得到一些启发。

#先了解下俄罗斯方块的几个形状

一共分成 7 形状,有的形状有 4种状态,有的形状有1种状态。

不管是多少种状态,一个方块需要一个2个字节来存储,也就是16bit来保存一个方块的信息。

基于上面的理论,我们可以使用4x4的数组来保存方块的信息。

注意,下面代码中的 "■" 占用的是2个字节。

#写代码来显示这个方块

方块数据,这两个数组其实是两种游戏模式的方块信息,我们只需要分析一种就可以了。

int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};

我们分析这段代码

/***********擦除显示*************/
int Display(int x, int y, int CAC, int Mode)
{for(j=0;j<=3;j++){P[j]=CAC&0xF, CAC>>=4;if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}}return 0;
}

P[j]=CAC&0xF 取到的是 4个bit,然后通过判断这 4个bit决定输出方块的位置。

我们拿 0这个方块的数据0x4859 对应的二进制 0B0100100001011001来做个例子。

0100  ----> 对应坐标(1,0)
1000  ----> 对应坐标(2,0)
0101  ----> 对应坐标(1,1)
1001  ----> 对应坐标(2,1)

通过这个坐标,我们会输出这样一个方块

再举个例子

我们拿 Z这个方块的数据0x0156 对应的二进制 0B0000000101010110来做个例子。

0000  ----> 对应坐标(0,0)
0001  ----> 对应坐标(0,1)
0101  ----> 对应坐标(1,1)
0110  ----> 对应坐标(1,2)

显示方块没有问题了

上面的代码中,如果mode等于0的话,也就是把某个方块从显示中擦除掉。

我们用devc++写的程序,需要知道几个细节。

#构建固定行列的窗口

正常情况,我们显示cmd是一个默认值的黑框「如下图」。

实际上,我们可以用代码来固定cmd的框框大小。

默认代码输出

示例代码:

#include   <conio.h>
#include   <stdio.h>
#include  <stdlib.h>
#include    <time.h>
#include <Windows.h>/**********Main主函数***********/
int main()
{system("color F1&mode con cols=35 lines=25");getchar();return 0;
}

程序输出:

#延迟函数的设计

跟其他不同的是,这个程序的延迟没有使用usleep,我觉得这也是非常值得称赞的地方,不过空跑cpu,哈哈,是有那么一点调皮。

把延迟放到键值获取的函数中。

/**********按键获取**************/
int Getkey(int N,int T)
{int start=clock();if(KEY_V==115){return 115;}do{if(kbhit()){KEY_V=(int)(getch());if(KEY_V<97){KEY_V+=32;}return KEY_V;}for(i=0;i<=N;i++);}while((clock()-start)<T);dy=1;return -1;
}

就先简单讲解下这些,至于得分机制,碰撞检测,消除的代码,还没有想好怎么讲明白,需要再花时间剖析剖析。


#试玩一下

#源码

#include   <conio.h>
#include   <stdio.h>
#include  <stdlib.h>
#include    <time.h>
#include <Windows.h>/**********初始化参数************/
int i,j,N,T,F,J,X,Y,dx,dy,  KEY_V,  Cache1,Cache2,NU,NI,RU,RI,  P_X,P_Y,POS_H_MAX,  LEVEL=1,SCORE=0,  P[4],  POINT_V[12][22],  MARK[21],  FLAG[5]={0,0,0,1,0};
int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};/**********光标位置函数**********/
void Pos(int x,int y)
{COORD pos;HANDLE hOutput;pos.X=2*x;pos.Y=y;hOutput=GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOutput,pos);
}void HideCursor()
{CONSOLE_CURSOR_INFO cursor_info={1,0};SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}/**********初始化界面************/
void CreatUI()
{int i,j,BOUNDARY;printf("┏━━━━━━━━━━┓\n");for(j=1;j<=20;j++) {if     (j==3){printf("┃          ┃LEVEL:1\n");}else if(j==5){printf("┃          ┃SCORE:0\n");}else if(j==7){printf("┃          ┃NEXT   \n");}else         {printf("┃          ┃\n");}}printf("┗━━━━━━━━━━┛\n");printf(" CopyRight@2016~2018 BY HAPPY\n");for(j=1;j<=21;j++){for(i=0;i<=11;i++){BOUNDARY=i*(i-11)*(j-21);if(BOUNDARY==0){POINT_V[i][j]=1;}else{POINT_V[i][j]=0;}                }}
}/**********按键获取**************/
int Getkey(int N,int T)
{int start=clock();if(KEY_V==115){return 115;}do{if(kbhit()){KEY_V=(int)(getch());if(KEY_V<97){KEY_V+=32;}return KEY_V;}for(i=0;i<=N;i++);printf("%d\n",clock()-start);}while((clock()-start)<T);dy=1;return -1;
}/***********块体转置*************/
int Rote(int S, int I)
{return (F==0)?TGM[S][(I+4)%4]:SRS[S][(I+4)%4];
}/***********擦除显示*************/
int Display(int x, int y, int CAC, int Mode)
{for(j=0;j<=3;j++){P[j]=CAC&0xF, CAC>>=4;if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}}return 0;
}/***********固化块体*************/
int DoBlocks()
{//~~~游戏结束if(Y<2){Pos(1,22);printf("GAME OVER!");exit(0);}//~~~固化块体POS_H_MAX=0, FLAG[3]=1;for(j=0;j<=3;j++){P_X=(P[j]>>2)+X,P_Y=(P[j]&0x3)+Y;if(POS_H_MAX<P_Y){POS_H_MAX=P_Y;}POINT_V[P_X][P_Y]=1;}//~~~关卡得分for(j=Y;j<=POS_H_MAX;j++){FLAG[2]=1;for(i=1;i<=10;i++){if(POINT_V[i][j]==0){FLAG[2]=0;}}if(FLAG[2]){SCORE+=10,MARK[j]=1;if(SCORE==400){SCORE=0,LEVEL+=1,T-=100;FLAG[4]=1;        }}}//~~~极品消行for(j=20;j>=5;j--){if(FLAG[4]){for(i=1;i<=10;i++){POINT_V[i][j]=0;Pos(i,j);printf(" ");}}else if(MARK[j]){MARK[j]=0,J=j-1;for(N=1;N<=3;N++){if(MARK[J]){J--;}}MARK[J]=1;for(i=1;i<=10;i++){Pos(i,j);if(POINT_V[i][j]=POINT_V[i][J]){printf("■");}else{printf(" ");}}}}FLAG[4]=0;return 0;
}/***********碰撞检测*************/
int CheckCollision()
{                                for(j=0;j<=3;j++){P_X=(P[j]>>2)+X+dx,P_Y=(P[j]&0x3)+Y+dy;if(POINT_V[P_X][P_Y]){if(dx!=0){return 1;}if(dy){DoBlocks();Pos(12,3);printf("LEVEL:%-3d",LEVEL);Pos(12,5);printf("SCORE:%-3d",SCORE);return 2;}if(KEY_V==119){FLAG[0]=1;}}}return 0;
}
unsigned 
/***********循环核心*************/
int GameCycle(int N, int T, int F)
{srand((unsigned)time(NULL));RU=rand()%7,RI=(rand()%4);while(1){if(FLAG[3]){Display(12,8,Rote(RU,RI),0);X=4,Y=1, NU=RU,NI=RI, RU=rand()%7,RI=(rand()%4), FLAG[3]=0,KEY_V=0;Display(12,8,Rote(RU,RI),1);Display(X, Y,Rote(NU,NI),1);}dx=0,dy=0;        KEY_V=Getkey(N,T);if(KEY_V==119){NI++;Display(X,Y,Rote(NU,NI),2);}//旋Welse if(KEY_V==115){dy= 1;}//下Selse if(KEY_V==97 ){dx=-1;}//左Aelse if(KEY_V==100){dx= 1;}//右Delse if(KEY_V==112){getch(); }//暂停Pelse if(KEY_V==113){return 0;}//退出Qif(dx!=0 || dy!=0 || KEY_V==119){if(!CheckCollision()){if(FLAG[0]){NI--,FLAG[0]=0;Display(X,Y,Rote(NU,NI),0);}else if(KEY_V==119){Display(X,Y,Rote(NU,NI-1),0);}else{Display(X,Y,Rote(NU,NI),0);}Display(X+dx,Y+dy,Rote(NU,NI),1);X+=dx,Y+=dy;}}}return 0;
}/**********Main主函数***********/
int main()
{system("color F0&mode con cols=35 lines=25");HideCursor();CreatUI();GameCycle(10,800,1);return 0;
}

#后记

里面涉及的很多东西,我都没有完全总结出来,作者的巧妙非常令我佩服,后面应该还会有文章分析。

大家有什么想法或者自己的解读,可以留言,欢迎一起讨论,共同进步,探索代码里面的奥秘和乐趣。

方块消除

准备下一个方块

得分机制

Game Over 判断

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

认识星座、八大行星的观察

1. 北斗七星 北斗七星属于大熊座&#xff0c;春夏可见&#xff1b;2. 八大行星 太阳系的八大行星基本位于同一轨道面&#xff0c;如以地球轨道面为基准&#xff0c;相互间轨道倾角的差距甚至不到5度&#xff08;不包括水星&#xff09;。太阳系各大行星公转平面与地球黄道平面的…

ERP项目需要持续的呵护

ERP项目需要持续的呵护<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />众所周知&#xff0c;实施ERP是一持续优化的过程&#xff0c;而在这优化完善的过程中&#xff0c;单靠一次实施上线是不够的&#xff0c;尤其是在ERP上线后…

Datawha组队——Pandas(下)综合练习(打卡)

import pandas as pd import numpy as np import missingno as msno import matplotlib.pyplot as plt plt.rcParams[font.sans-serif][SimHei] #用来正常显示中文标签 plt.rcParams[axes.unicode_minus]False #用来正常显示负号#读取数据 df pd.read_csv(端午粽子数据.csv) d…

Linux C高级编程——文件操作之库函数

Linux C高级编程——文件操作之库函数 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的 ——为什么要设计标准I/O库&#xff1f; 直接使用API进行文件访问时&#xff0c;需要考虑许多细节问题 例如&#xff1a;read、write时&#xff0c;缓冲区的大小该如…

【转】spring之任务调度

由于现在大部分的系统都是采用了spring&#xff0c;所以所有的例子都结合spring来构建&#xff0c;目前主要的任务调度分为三种&#xff1a; Java自带的java.util.Timer类&#xff0c;这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执…

测试内存对齐对运行速度的影响

我们知道内存对齐是为了方便CPU工作&#xff0c;但是对齐和不对齐差异有多大呢&#xff1f;我自己也没有实际测试过&#xff0c;今天就运行个代码测试看看。1、1字节对齐的时候#include "stdio.h"#pragma pack(1) struct test { char x1; short x2; float x3; …

连接池的问题

看到关于连接池的问题&#xff0c;觉得很有用&#xff0c;摘录到自己博客上 NET 连接池救生员 防止可淹没应用程序的池溢出 William Vaughn 大多数 ADO.NET 数据提供程序使用连接池&#xff0c;以提高围绕 Microsoft 断开连接的 .NET 结构构建的应用程序的性能。应用程序首先打…

Datawhale-零基础入门NLP-新闻文本分类Task01

参考&#xff1a; https://www.jianshu.com/p/56061b8f463a 统计自然语言处理 宗成庆&#xff08;第二版&#xff09; 文本自动分类简称文本分类(text categorization),是模式识别与自然语言处理密切结合的研究课题.传统的文本分类是基于文本内容的,研究如何将文本自动划分为…

fprintf/fscanf函数分析

fprintf/fscanf函数分析 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 fprintf/fscanf函数与printf/scanf区别&#xff1a;printf/scanf专门针对标准输入输出流&#xff0c;fprintf/fscanf函数可用于任意流&#xff0c;包括输入输出流。 1、fprintf …

华为海选开发者状元?还送14件豪礼?

华为云全年最大 最大 最大开发者庆典活动来啦&#xff01;这次庆典没别的&#xff0c;就是&#xff1a;好玩&#xff01;刺激&#xff01;让你拿奖拿到怀疑人生&#xff01;贺岁就要有贺岁的样子~赶紧来看看华为云为开发者们准备了怎样的新年惊喜好玩的在这里&#xff01;上学的…

国际:如何识别真正的程序员

如何识别优秀的程序员&#xff1f;并不是像听起来那么容易。在这儿工作经验的作用是很有限的&#xff0c;因为伟大的程序员不一定要‘正式’的工作经历来证明他们的伟大。 1&#xff0c;激情。 我曾经遇到许多“职业程序员”&#xff0c;他们从事IT是因为觉得这是一种职业&…

Datawhale-零基础入门NLP-新闻文本分类Task02

Task01里边对赛题进行了分析,接下来进行数据读取与数据分析&#xff0c;通过使用Pandas库完成数据读取和分析操作。 1 数据读取 由赛题数据格式可知&#xff0c;可通过read_csv读取train_set.csv数据&#xff1a; import pandas as pd import numpy as np import matplotlib…

一步步分析-C语言如何面向对象编程

这是道哥的第009篇原创一、前言在嵌入式开发中&#xff0c;C/C语言是使用最普及的&#xff0c;在C11版本之前&#xff0c;它们的语法是比较相似的&#xff0c;只不过C提供了面向对象的编程方式。虽然C语言是从C语言发展而来的&#xff0c;但是今天的C已经不是当年的C语言的扩展…

Linux C高级编程——目录操作

Linux C目录操作 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 Linux思想“一切皆文件”&#xff0c;目录也是文件&#xff0c;只是存储的内容有别于普通文件。目录文件中存储的该目录下所有的文件及子目录文件的信息&#xff0c;inode节点。 一、打开…

利用open***建立桥接***[zt]

利用open***建立桥接***http://blog.chinaunix.net/u/7667/showart_30753.html本文介绍利用open***建立桥接***的一种简单方法&#xff0c;使用的服务器为debian GNU/Linux sarge,使用apt-get dist-upgrade更新到最新&#xff0c;内核2.4.27-1-686&#xff0c;未重新编译内核&a…

c复习过程随笔四

使用scanf函数输入数据&#xff1a;一般形式&#xff08;格式控制&#xff0c;地址表列&#xff09; 格式控制中可以包含普通字符 格式控制和printf函数所遵循的格式相似 使用scanf应注意的问题&#xff1a; &#xff08;1&#xff09;格式控制后面应该是变量地址&#xff0c;而…

Datawhale-零基础入门NLP-新闻文本分类Task03

文本是不定长度的&#xff0c;文本表示成计算的能够运算的数字或向量的方法称为词嵌入&#xff08;Word Embedding&#xff09;。词嵌入是将不定长的文本转换成定长的空间中。为了解决将原始文本转成固定长度的特征向量问题&#xff0c;scikit-learn提供了以下方法&#xff1a;…

Linus 在圣诞节想提前放假做了这些解释,哈哈哈

最近在 lkml.org 上看到Linus发布的一个信息&#xff0c;挺有意思的&#xff0c;我看了内容&#xff0c;然后根据自己的理解展示给大家看看&#xff0c;如果有不对的地方欢迎指正。好的&#xff0c;5.10内核发布了我真希望在圣诞节来的最后一个星期没有那么多破事&#xff0c;现…

文件流、目录流、文件描述符总结

文件流、目录流、文件描述符总结 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 内核为使当前进程与进程打开的文件建立联系&#xff0c;在进程PCB&#xff08;一个结构体task_struct&#xff09;中使用一个成员来指向关于打开文件列表的结构体struct …

eleemnt-ui修改主题颜色

饿了吗的element-ui使用的是淡蓝色的主题&#xff0c;有时候我们可以自定义主题&#xff0c;官方的文档给我们提供了如何修改主题&#xff0c;介绍的很详细&#xff0c;自己试验过后&#xff0c;觉得很不错&#xff0c;一方面怕忘记&#xff0c;一方面写一写。 方法一是在线生成…