基于ncurse实现的俄罗斯方块

news2025/6/27 4:27:48

1. 需求分析

  1. 方块的类型
  2. 方块的变形
  3. 方块的消除
  4. 方块的存储
  5. 方块的移动
  6. 接受用户的输入

2. 概要设计

2.1 方块类型与变形

一共有七种,变换的方式如下。变换后的任意形状方块实际上可以存在一个4x4的矩阵中。
我们再压一下位,就可以存在16位中。
在这里插入图片描述

2.2 方块的消除

只需要检查一整行全部都是方块即可。

2.3 方块的移动

详见tinytetris

使用一个简单的循环计时来实现(do_ticks())。

2.4 接受用户输入

直接使用ncurse中的getch即可获得键盘、鼠标等输入。

3. 实现

#include <stdlib.h>
#include <ncurses.h>
#include <time.h>
#include <string.h>


#define MAP_ROWS 20
#define MAP_COLS 10
#define GAME_BEGIN_Y 4

#define GRID_WIDTH 2
#define GRID_HEIGH 2

#define WALL_WIDTH 1


enum mov_dir {
    HOLD = 0,
    LEFT,
    RIGHT,
    DOWN
};
uint dir_to[4][2] = {
    {0,0},
    {-1,0},
    {1, 0},
    {0, 1}
};



enum piece_block {
    I_BLOCK = 1,
    J_BLOCK,
    L_BLOCK,
    O_BLOCK,
    S_BLOCK,
    T_BLOCK,
    Z_BLOCK
};
uint8_t grid_map[MAP_ROWS][MAP_COLS];
char x;
char y;
uint8_t blk_idx;
uint8_t ticks = 0;

uint8_t next_blk_idx;
int scores = 0;

// 16bit
int block_magic[7][4] = {
  { 240,17476, 3840, 8738},
  { 113, 550, 1136, 802},
  { 116, 1570, 368,547},
  { 51, 51, 51, 51},
  { 54, 1122, 864, 561},
  { 114, 610, 624, 562},
  { 99, 612, 1584, 306}  
};


void gen_block( void )
{
    blk_idx = next_blk_idx;
    next_blk_idx = rand() % 28;
}
void gen_pos( void )
{
    x = rand() % 7;
    y = 0;
}

void gen_new_block( )
{
    gen_block(); 
    gen_pos();
}  



uint8_t transform( void )
{
   uint8_t pos = blk_idx & 0x03;
   pos = (pos + 1) % 4;
   
   return (blk_idx & 0xfc ) | pos;
}
void draw_block_cell( int cx, int cy, uint8_t idx) {
    
    	
    uint16_t magic = block_magic[idx >> 2][idx & 0x03];

    for (int i = 0; i < 16; ++i) {
       uint8_t x0 = i & 0x03;
       uint8_t y0 = i >> 2;
       
       int nx = cx + x0;
       int ny = cy + y0;

       if ( magic & (1 << i)) {
           attron(COLOR_PAIR( (idx >> 2) + 1));
	   if (nx >= 0 && ny >= 0)
	   	mvaddch( ny, nx, '#');
	   attroff(COLOR_PAIR( (idx >> 2) + 1));
       }
    }
   refresh();
}
void draw_grid_map(uint8_t x, uint8_t y)
{
    for ( int i = 0; i < MAP_ROWS; ++i) {
        for ( int j = 0; j < MAP_COLS; ++j) {
            if ( grid_map[i][j] ) {
                attron(COLOR_PAIR( grid_map[i][j] ));
	        mvaddch(i + y , j + x  , '#');
		attroff(COLOR_PAIR( grid_map[i][j]) );
	    }

	}
    } 
    refresh();
}



int check_hit( uint8_t idx, enum mov_dir dir)
{

    uint16_t magic = block_magic[ idx >> 2][ idx & 0x03];

    uint8_t nx = x + dir_to[dir][0];
    uint8_t ny = y + dir_to[dir][1];
    for ( int i = 0; i < 16; ++i) {
        uint8_t x0 = i & 0x03;
	uint8_t y0 = i >> 2;


	uint8_t cx = nx + x0;
	uint8_t cy = ny + y0;

	if ( (1 << i) & magic ) {
            
           if ( cx >= MAP_COLS || cx < 0 ) 
		   return 1;
           if ( cy >= MAP_ROWS + GAME_BEGIN_Y || cy < 0)
	           return 1;
	   if ( cy >= GAME_BEGIN_Y && grid_map[ cy - GAME_BEGIN_Y][ cx ]  )
		   return 1;
	}
    }

    return 0;
}

int check_remove ( void )
{
   
    for ( int i = MAP_ROWS  - 1; i > -1; --i) {
	int need_remove = 1;
        for ( int j = 0; j < MAP_COLS; ++j ) {
	    if ( !grid_map[i][j]) {
	        need_remove = 0;
		break;
	    }
	}
        if ( need_remove )
		return 1;
    }

    return 0;

}
int remove_line( int *line_cnt)
{
    int lowest_line = -1;
    for ( int i = MAP_ROWS - 1; i > -1; --i) {
        int need_rm = 1;
	for ( int j = 0; j < MAP_COLS; ++j) {
            if ( !grid_map[i][j]) {
	        need_rm = 0;
		break;
	    }
	}


	if ( need_rm ) {
	    if ( -1 == lowest_line)
		    lowest_line = i;
            ++*line_cnt;
	    for (int k = 0; k < MAP_COLS; ++k)
		    grid_map[i][k] = 0;
	}
    }

    // 1 line: 10 score
    // 2 line: 20 + 5
  scores +=  10 * (*line_cnt) + 5 * (*line_cnt - 1 );  
  return lowest_line; 
}


void grids_fall( int lowest_line, int line_ct )
{
  /*
  for (int i = lowest_line; i >= line_ct; --i) {
      for ( int j = 0; j < MAP_COLS; ++j)
	      grid_map[i][j] = grid_map[i - line_ct][j];
  }
*/
  memmove(grid_map + line_ct, grid_map, sizeof(uint8_t) * (lowest_line + 1 - line_ct) * MAP_COLS);
  memset( grid_map, 0, sizeof(int)*MAP_COLS * line_ct );

  /* 
    for ( int i = 0; i < MAP_COLS; ++i) {
        
        int mx = 23;
	while ( mx > 3 && grid_map[mx][i])
		--mx;
	int lowest_ept_grid = mx;
	while ( mx > 3 && !grid_map[mx][i])
		--mx;
	int lowest_grid = mx;

	int dis = lowest_ept_grid - lowest_grid;

	while ( lowest_grid > 3) {
            grid_map[ lowest_grid + dis][i] = grid_map[ lowest_grid][i];
	    grid_map[ lowest_grid][i] = 0;
	    --lowest_grid;
	}

    }
*/
}

void draw_current_block( void )
{

    // x_p = MAP_COLS + 3 
    // y_p = 2
    /*
    uint16_t magic = block_magic[ blk_idx >> 2][ blk_idx & 0x03];

    for (int i = 0; i < 16; ++i ) {
        uint8_t x0 = i & 0x03;
        uint8_t y0 = i >> 2;

	if ( (1 << i) & magic ) {
            attron( COLOR_PAIR((blk_idx >> 2) + 1));
            mvaddch( 2 + y0, MAP_COLS + 4 + x0, '$');
	    attroff( COLOR_PAIR( (blk_idx >> 2) + 1));
	}
    }*/
   
   mvprintw( 6, MAP_COLS + 4, "blk:%2d", blk_idx );
   mvprintw( 7, MAP_COLS + 4, "%2d,%2d", x, y);
   mvprintw(14, MAP_COLS + 4, "scores: %d", scores);
   mvprintw(17, MAP_COLS + 4, "w for transform");
   mvprintw(18, MAP_COLS + 4, "asd for dir");
   mvprintw(19, MAP_COLS + 4, "p for quik put");
   mvprintw(20, MAP_COLS + 4, "q for quit");
   draw_block_cell( MAP_COLS + 4, 2, blk_idx);
   draw_block_cell( MAP_COLS + 4, 10, next_blk_idx);
}
  
void draw_status_bar( void )
{
    /*
   draw_block_cell( MAP_ROWS + 4, 2, blk_idx);
   
   mvprintw( 6, MAP_COLS + 4, "%2d,%2d", blk_idx >> 2, blk_idx & 0x03 );
   mvprintw( 7, MAP_COLS + 4, "%2d,%2d", x, y);

   draw_block_cell( MAP_ROWS + 4, 9, next_blk_idx);
  */
    draw_current_block();
}
void do_remove( void )
{

     int rm_cnt = 0;
     if ( check_remove() )
     {
        
	int lowest_rm_line = remove_line( &rm_cnt );

        draw_frame();	
        grids_fall( lowest_rm_line, rm_cnt );
        draw_frame();
     }



}
void clear_map_area( void )
{
      for ( int j = 0;j < MAP_ROWS + 1; ++j)
	      mvhline(j, 0, ' ', MAP_COLS + 2);
}


void draw_block ( void )
{

   draw_block_cell( x + 1, y - GAME_BEGIN_Y , blk_idx );
   refresh();
}


void place_block( void )
{
    uint16_t magic = block_magic[ blk_idx >> 2 ][ blk_idx & 0x03];

    for ( int i = 0; i < 16; ++i ) {
        uint8_t x0 = i & 0x03;
	uint8_t y0 = i >> 2;

	if ( (1 << i) & magic ) {
            grid_map [ y+y0 - GAME_BEGIN_Y ] [ x+x0 ] = (blk_idx >> 2) + 1;
	}
    }

}
void init_ncurse_color( void )
{
    start_color( );
    for (int i = 1; i < 8; ++i ) {
        init_pair(i, i, i);
    }
}

void draw_grid( int x, int y, int color_pair_id )
{ 
 
    
}
void init_ncurse_settings( void )
{
    initscr();
    init_ncurse_color();
    noecho( );
    timeout(0);
    curs_set(0);
    resizeterm(44, 30);

}
void init_rand_generator( void )
{
    srand( time(NULL) );
    next_blk_idx = rand() % 28; 
}

int check_end( void )
{
    uint16_t magic = block_magic[ blk_idx >> 2][blk_idx & 0x03];


    for ( int i = 0; i < 16; ++i ) {
        uint8_t x0 = i & 0x03;
	uint8_t y0 = i >> 2;
       
	uint8_t nx = x + x0;
	uint8_t ny = y + y0;
	if ( (1<<i) & magic ) {
            if ( ny < GAME_BEGIN_Y)
		    return 1;
	}

    }
    return 0;
}


int do_tick( void )
{
   
    if ( ++ticks > 30) {
       ticks = 0;
       if ( check_hit(blk_idx, DOWN) ) {
             if (check_end( ))
		    return 1;
	     place_block();
	     do_remove();
	     gen_new_block(); 
	     draw_frame();
             return 0;
       }
       ++y;
    }
    return 0;
}
int process_input( int ch )
{
    
    switch ( ch )
    {
	    case 'a':
	    case 'A':
		    if ( !check_hit(blk_idx, LEFT) ) {
		        --x;
		    }
		    break;
	    case 'd':
            case 'D':
		    if ( !check_hit(blk_idx, RIGHT) ) {
                       ++x;
		    }
		    break;
	    case 's':
	    case 'S':
                    if ( !check_hit(blk_idx, DOWN ) ) {
                       ++y;
		       break;
		    }
		    if ( check_end( ) ) {
		      return 1;
		    }
		    place_block();
		    do_remove();
		    gen_new_block();
		    break;
	    case 'w':
	    case 'W':
		    if ( !check_hit(transform(), HOLD)) {
		        blk_idx = transform();
		    }
		    break;
	    case 'p':
            case 'P':
		    while ( !check_hit(blk_idx, DOWN)) {
		        ++y;
		    }
		    if ( check_end() )
			return 1;
		    place_block();
		    do_remove();
		    gen_new_block();
		    break;
            case 'q':
            case 'Q':
		    return 1;
		    break;
            default:
		    break;
    }

    return 0;
}
void draw_wall( void )
{

   // 0 col and MAP_COLS + 1 col
   // MAP_ROWS row is wall

   attron(COLOR_PAIR( 3 ));	
   mvvline( 0, 0, ' ', MAP_ROWS );
   mvvline( 0, MAP_COLS + 1, ' ', MAP_ROWS);
   mvhline( MAP_ROWS, 0, ' ',MAP_COLS + 2);
   attroff( COLOR_PAIR(3));

   /*
   for ( int i = 0; i < MAP_ROWS + 1; ++i) {
      for ( int j = 0; j < MAP_COLS + 2; ++j) {
          if ( i == 0 && (j == 0 || j == MAP_COLS + 1)) {
	      attron( COLOR_PAIR(2));
              mvaddch(i, j, '#');
	      attroff( COLOR_PAIR(2));
	  }

	  if ( i == MAP_ROWS || j == 0 || j == MAP_COLS + 1){
              attron( COLOR_PAIR(2) );
              mvaddch(i, j, '#');
	      attroff( COLOR_PAIR(2));
	  }
      }
   }
*/
    refresh();
}
void draw_frame( void )
{
   clear();
   draw_wall( );
   draw_grid_map( 1, 0);
   draw_block( );
   draw_status_bar(); 
}
void test_all_blk_show ( void )
{
    int cx = 0;
    int cy = 0;

    for ( int i = 0; i < 7; ++i,cy += 6) {
         cx = 0;
    	 for ( int j = 0; j < 4; ++j, cx += 6) {
             
	     draw_block_cell( cx, cy, i * 4 + j);
	}
    }

}


int main( int argc, char *argv[])
{
    
    init_ncurse_settings( );
    init_rand_generator( );
    gen_new_block(); 
   

    int ch;
    int is_end = 0;
    while ( !do_tick() )
    {
	usleep( 10000 );
	int ch = getch();
        if ( process_input( ch ) )
		break;
      
        draw_frame();	
    }


    endwin();

    return 0;
}

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

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

相关文章

python之静态服务器程序开发

文章目录 Python静态Web服务器开发Web静态服务器初识搭建Python自带的静态Web服务器静态Web服务器返回固定页面数据静态Web服务器返回指定页面数据静态Web服务器多任务版静态Web服务器面向对象开发静态Web服务器命令行启动动态绑定端口号 Python静态Web服务器开发 Web静态服务…

求集合的笛卡尔乘积

求集合的笛卡尔乘积 一&#xff1a;【实验目的】二&#xff1a;【实验内容】三&#xff1a;【实验原理】四&#xff1a;代码实现&#xff1a; 一&#xff1a;【实验目的】 通过编实现给定集合A和B的笛卡尔积CAA,DAB,EBA,FAAB,GA(A*B&#xff09;. 二&#xff1a;【实验内容】…

Linux系统常用指令大全(图文详解)

目录 前言 一、UNIX的登录与退出 1、登录 &#xff08;1&#xff09;执行格式&#xff1a; &#xff08;2&#xff09;步骤 2、退出 二、UNIX命令格式 三、常用命令 1、目录操作 &#xff08;1&#xff09;显示目录文件 ls &#xff08;2&#xff09;建新目录 …

PgSQL技术内幕-Analyze做的那些事-pg_stat_all_tables

PgSQL技术内幕-Analyze做的那些事-pg_stat_all_tables pg_stat_all_tables视图中记录有analyze信息&#xff0c;比如何时做的analyze、表元组个数&#xff08;活元组、死元组&#xff09;等。重启后发现该视图中表的统计信息重置不见了&#xff0c;发生了什么&#xff1f; 1、p…

纵观手机市场,手机即鏖战全面屏

9月13日&#xff0c;在相继发布Apple TV、Apple Watch 和iPhone 8/8 Plus之后&#xff0c;当大家都以为苹果新品发布会临近结束之时&#xff0c;苹果前CEO史蒂夫乔布斯的这句经典名言再现屏幕&#xff0c;iPhone X终于揭开了神秘面纱。 “One more thing”。 9月13日&#xff…

Java每日一题:26. 删除有序数组中的重复项

删除有序数组中的重复项 分析&#xff1a; 数组是有序的&#xff0c;因此重复的元素会相邻 每次取出两个数进行比较&#xff0c;因此&#xff0c;需要有两个变量去存储每次取出的值 采用双指针方法&#xff1a; 指针p和q&#xff0c; pnum1&#xff0c;qnum2 p和q进行比较&…

【服务器能干什么】搭建一个短网址平台,可以查看数据详情!

昨天在 YouTube 上看到又一个搭建自己短网址的视频教程&#xff0c;用的是开源的 polr&#xff0c;但是按照步骤一步步搭建下来&#xff0c;最后一步都会出现 顺哥轻创 PLAINTEXT Whoops, looks like something went wrong百度、谷歌查了一圈也没找到有效的解决方法。&#x…

1.2 全加器

处理1个进位的加法 根据半加器可知,结果和进位只有1种情况:要么有结果要么有进位因此全加器可由2个半加器组合而成通过一个半加器输出的结果和进位再次由另一个半加器输出2个半加器的进位通过或门输出

Python 安装Vue依赖包发生异常:npm ERR! notsup Required: {“node“:“^18.17.0 || >=20.5.0“}

异常&#xff1a; 原因&#xff1a;node和npm要求升级为高版本 解决&#xff1a;重新安装node环境 &#xff08;1&#xff09; 官网下载Node.js &#xff08;2&#xff09;双击安装node.js &#xff08;3&#xff09;运行查看

STK Components 二次开发-创建卫星

1.卫星数据 可以用stk 里面自带的 参数帮助文档。 也可以自己下载 CelesTrak: Current GP Element Sets 这里你所需要的最新卫星数据全有。 其实创建需要的就是卫星的二根数。 给定二根数也可以。 读取数据库中的卫星数据 这个接口优先下载最新的。 var tleList TwoL…

宝塔面板安装搭建DiscuzQ论坛教程与小程序上架发布后的展示效果

DiscuzQ论坛小程序上架发布后的展示效果&#xff1a; 1、需要用到的环境&#xff1a; php7.2 mysql5.7或者MariaDB 10.2(我安装用的mysql8.0) php除了必要的一些扩展外&#xff0c;还需要启用readlink、symlink函数等&#xff0c;具体看官方说明&#xff0c;安装的时候也会提醒…

MidJourney笔记(3)-Prompts

MidJourney的Prompts介绍 MidJourney的Prompts是MidJourney的核心之一,这也是我们后续使用MidJourney过程中最重要的工作内容,根据生成的图片,不断的优化我们的Prompts内容。 那Prompts的中文意思是提示的意思。 Prompts的提示语有很多,最基础的用法就是: /imagine prompt…

【数据结构实验】查找(一)基于散列表的查找算法

文章目录 1. 引言2. 实验原理2.1 散列表2.2 线性探测法2.3 冲突解决 3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现3.3 代码整合 4. 实验结果 1. 引言 本实验将通过C语言实现基于散列表的查找算法 2. 实验原理 …

Unity 场景切换

Unity场景切换可使用以下方法&#xff1a; 1、SceneManager.LoadScene()方法&#xff1a; using UnityEngine.SceneManagement;// 切换到Scene2场景 SceneManager.LoadScene("Scene2"); 2、使用SceneManager.LoadSceneAsync()方法异步加载场景&#xff0c;异步加载…

Python pandas对表格进行整行整列筛选、删除或修改,对特定值进行修改

Pandas库的使用 Pandas库&#xff1a;从入门到应用(二)–行列数据读写 Python数据处理工具 ——Pandas&#xff08;数据的预处理&#xff09; Pandas库有两个数据类型: Series, DataFrame Series 索引 一维数据DataFrame 行列索引 二维数据 DataFrame类型 DataFrame类型…

VMware虚拟机网络配置详解

vmware为我们提供了三种网络工作模式&#xff0c;它们分别是&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09; 打开vmware虚拟机&#xff0c;我们可以在选项栏的“编辑”下的…

U盘报错无法访问文件或目录损坏且无法读取

使用电脑打开U盘的部分文件时提示无法访问&#xff0c;文件或目录损坏且无法读取 报错内容如下图&#xff1a; 因为我这个U盘是那种双接口的 Type-C和USB&#xff0c;前段时间被我摔了一下 看网上说这种双接口的U盘USB接口容易坏掉 尝试在手机上使用OTG打开&#xff0c;先测试…

二叉树详讲(一)---完全二叉树、满二叉树、堆

1.树的概念及其结构 1.1树的概念 树是一种非线性数据结构&#xff0c;是一种种抽象数据类型&#xff0c;旨在模拟具有树状结构的节点之间的层次关系。一颗树由诺干个点和诺干条边组成。每棵树只有一个根节点&#xff0c;根节点向下延申又有子节点和叶子节点&#xff0c;叶子节…

Linux 命令vim(编辑器)

(一)vim编辑器的介绍 vim是文件编辑器&#xff0c;是vi的升级版本&#xff0c;兼容vi的所有指令&#xff0c;同时做了优化和延伸。vim有多种模式&#xff0c;其中常用的模式有命令模式、插入模式、末行模式&#xff1a;。 (二)vim编辑器基本操作 1 进入vim编辑文件 1 vim …

Kotlin学习——kt里的集合List,Set,Map List集合的各种方法之Int篇

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…