C语言实现俄罗斯方块游戏

news2024/11/13 4:28:57

文章目录

    • 1 前言
    • 2 游戏截图
    • 3 源代码

1 前言

本文介绍的是我空闲时间用C语言写的一个俄罗斯方块游戏,整个程序只有一个文件,实现了基本的游戏功能,但还是有些缺陷,希望有心之士能够继续完善,感谢各位!

2 游戏截图

请注意!a、d、s、w都是小写

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3 源代码

/*
author: New_Teen
time: 2023.11.2 20:14 周四
IDE: visual studio 2019
俄罗斯方块
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <stdbool.h>
#include <conio.h>

#define BOARD_CUBE ' '
#define WIDTH 15
#define HEIGHT 15
#define FLUSH_RATE 200

char curr_cube;
int curr_square;
int curr_x, curr_y;
int score = 0;
int start_number = 0;
char board[HEIGHT][WIDTH];
int tmp[4][2];
int square[5][4][4] = { {{1,0,0,0},
                        {1,0,0,0},
                        {1,0,0,0},
                        {1,0,0,0}},

                        {{1,0,0,0},
                        {1,1,1,0},
                        {0,0,0,0},
                        {0,0,0,0}},

                        {{0,1,0,0},
                        {1,1,1,0},
                        {0,0,0,0},
                        {0,0,0,0}},

                        {{1,1,0,0},
                        {1,1,0,0},
                        {0,0,0,0},
                        {0,0,0,0}},

                        {{0,1,0,0},
                        {1,1,0,0},
                        {1,0,0,0},
                        {0,0,0,0}} };

void init_board(); //初始化游戏面板
void clear_board(); //清空控制台
void delay_board(); //延时
void print_board(); //打印游戏面板
void init_square(); //初始化方块
void drop_square(); //方块默认下落
void update_board(); //更新游戏面板
bool is_collision_left(); //检测方块左侧是否碰撞
bool is_collision_right(); //检测方块右侧是否碰撞
bool is_collision_bottom(); //检测方块下方是否碰撞
bool is_collsion_rotate(); //检测方块是否可以旋转
bool is_game_over(); //检测游戏是否结束
void rotate(); //旋转方块
void rush_board(); //消除成层方块(得分)
void start_game(); //游戏开始面板

int main() {
    srand((unsigned)time(NULL)); //根据程序执行时间产生随机数种子
    start_game();
    init_board();
    while (1) {
        init_square();
        if (is_game_over())
            break;
        for (;;) {
            if (!is_collision_bottom())
                drop_square();
            else
                break;

            if (_kbhit()) {
                char ch = _getch();
                if (ch == 'a' && !is_collision_left())
                    curr_x--;
                else if (ch == 'd' && !is_collision_right())
                    curr_x++;
                else if (ch == 's' && !is_collision_bottom())
                    curr_y++;
                else if (ch == 'w' && !is_collsion_rotate())
                    rotate();
            }

            update_board();
            rush_board();
            print_board();
            delay_board();
            clear_board();
        }
    }
    printf("Game over!!\nYour score is %d\n", score);
    return 0;
}

void init_board() {
    for (int i = 0; i < HEIGHT; ++i)
        for (int j = 0; j < WIDTH; ++j)
            board[i][j] = BOARD_CUBE;
}

void clear_board() {
    system("cls");
}

void delay_board() {
    Sleep(FLUSH_RATE);
}

void print_board() {
    for (int i = 0; i < HEIGHT; ++i) {
        for (int j = 0; j < WIDTH; ++j)
            putchar(board[i][j]);
        puts("#");
    }
    puts("################");
    printf("score: %d\n", score);
}

void init_square() {
    curr_cube = "ABCDEFGH"[rand()%8];
    start_number == 1 ? curr_square = 0 : curr_square = rand() % 5;
    curr_x = WIDTH / 2, curr_y = -4;
    int count = 0;
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < 4; ++j)
            if (square[curr_square][i][j]) {
                tmp[count][0] = curr_y + i;
                tmp[count][1] = curr_x + j;
                count++;
            }
}

void drop_square() {
    curr_y++;
}

void update_board() {
    //清除之前位置的方块
    int row, col;
    for (int i = 0; i < 4; ++i) {
        row = tmp[i][0];
        col = tmp[i][1];
        if (row >= 0 && row < HEIGHT
            && col >= 0 && col < WIDTH)
            board[row][col] = BOARD_CUBE;
    }

    int count = 0;
    //画出现在位置的方块,并更新tmp
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            row = curr_y + i;
            col = curr_x + j;
            if (square[curr_square][i][j]) {
                tmp[count][0] = row;
                tmp[count][1] = col;
                count++;
                if (row >= 0 && row < HEIGHT
                    && col >= 0 && col < WIDTH)
                    board[row][col] = curr_cube;
            }
        }
    }
}

bool is_collision_left() {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (square[curr_square][i][j]) {
                //检测方块与面板左墙壁的碰撞
                if (curr_x + j <= 0)
                    return true;
                //检测方块与左侧方块的碰撞
                if (j == 0) {
                    if (board[curr_y + i][curr_x + j - 1] != BOARD_CUBE)
                        return true;
                }
                else {
                    if (!square[curr_square][i][j - 1] && 
                        board[curr_y + i][curr_x + j - 1] != BOARD_CUBE)
                        return true;
                }
            }
    return false;
}

bool is_collision_right() {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (square[curr_square][i][j]) {
                //检测方块与面板右墙壁的碰撞
                if (curr_x + j >= WIDTH-1)
                        return true;
                //检测方块与右侧方块的碰撞
                if (j == 3) {
                    if (board[curr_y + i][curr_x + j + 1] != BOARD_CUBE)
                        return true;
                }
                else {
                    if (!square[curr_square][i][j + 1] &&
                        board[curr_y + i][curr_x + j + 1] != BOARD_CUBE)
                        return true;
                }
            }
    return false;
}

bool is_collision_bottom() {
    //找到当前方块的最下层
    int low = -99;
    for (int i = 0; i < 4; i++) 
        for (int j = 0; j < 4; j++)
            if (square[curr_square][i][j] == 1)
                low = i;
    //如果达到面板最底部
    if(curr_y + low >= HEIGHT-1)
        return true;

    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            //考虑方块下面有方块的情况
            if (i < 3) {
                if (square[curr_square][i][j] && !square[curr_square][i + 1][j]
                    && (curr_y + i + 1 >=0) && board[curr_y + i + 1][curr_x + j] != BOARD_CUBE)
                    return true;
            }
            else {
                if (square[curr_square][i][j] && (curr_y + i + 1 >= 0) 
                    && board[curr_y + i + 1][curr_x + j] != BOARD_CUBE)
                    return true;
            }
        }
    }
    return false;
}

bool is_collsion_rotate() {
    int middle[4][4] = { 0 };
    //暂存旋转后方块
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < 4; ++j)
            middle[i][j] = square[curr_square][j][i];
    //旋转后方块是否与面板已有方块重叠
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < 4; ++j)
            if (!square[curr_square][i][j] && middle[i][j]
                && board[curr_y + i][curr_x + j] != BOARD_CUBE)
                return true;
    return false;
}

void rotate() {
    //清除之前位置的方块
    int row, col;
    for (int i = 0; i < 4; ++i) {
        row = tmp[i][0];
        col = tmp[i][1];
        if (row >= 0 && row < HEIGHT
            && col >= 0 && col < WIDTH)
            board[row][col] = BOARD_CUBE;
    }

    //旋转方块
    int rotate_tmp[4][4] = { 0 };
    memcpy(rotate_tmp, square[curr_square], 16 * sizeof(int));
    for (int i = 0; i < 4; ++i)
        for (int j = 0; j < 4; ++j)
            square[curr_square][i][j] = rotate_tmp[j][i];
    
    int count = 0;
    //画出现在位置的方块,并更新tmp
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            row = curr_y + i;
            col = curr_x + j;
            if (square[curr_square][i][j]) {
                tmp[count][0] = row;
                tmp[count][1] = col;
                count++;
                if (row >= 0 && row < HEIGHT
                    && col >= 0 && col < WIDTH)
                    board[row][col] = curr_cube;
            }
        }
    }
}

bool is_game_over() {
   for (int j = 0; j < WIDTH; ++j)
       if (board[0][j] != BOARD_CUBE)
           return true;          
    return false;
}

void rush_board() {
    int count, floor;
    floor = 0;
    for (int i = HEIGHT - 1; i >= 0; --i) {
        count = 0;
        for (int j = 0; j < WIDTH; ++j)
            if (board[i][j] != BOARD_CUBE)
                count++;
        if (count == WIDTH)
            floor++;
        else
            break;
    }

    //得到floor后,面板下移floor层
    if (floor != 0) {
        for (int i = HEIGHT - 1 - floor; i >= 0; --i)
            for (int j = 0; j < WIDTH; ++j)
                board[i + floor][j] = board[i][j];
        score += floor;
    }
}

void start_game() {
    printf("----------------俄罗斯方块----------------\n");
    printf("\n游戏规则:a:左移  d:右移  s:下移  w:旋转\n");
    printf("\n输入1:全程长条方块     输入2:经典随机方块\n");
    while (start_number != 1 && start_number != 2) {\
        printf("\n请输入序号(1或者2): ");
        scanf_s("%d", &start_number);
    }
    clear_board();
}

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

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

相关文章

redis rdb aof

appendonly yes # appendfsync always appendfsync everysec # appendfsync no E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit appendonly.aof

如何实现异步通知的重试机制

工作中经常要和第三方做对接&#xff0c;比如支付、电子合同等系统。操作成功之后&#xff0c;第三方会发送异步的通知&#xff0c;返回最终的处理结果&#xff0c;使用异步而不是使用同步通知&#xff0c;是为了加快系统响应速度&#xff0c;防止线程阻塞。任务处理完成后通过…

力扣 upper_bound 和 lower_bound

&#x1f468;‍&#x1f3eb; 34. 在排序数组中查找元素的第一个和最后一个位置 &#x1f338; AC code 2023版 class Solution {public int[] searchRange(int[] nums, int target) {int[] res { -1, -1 };if(nums.length 0)return res;int l 0;int r nums.length - 1;…

GD32F303RCT6不小心将读保护开启,导致后续程序烧不进去的解决办法

这里写自定义目录标题 错误现象判断读保护开启的方法用JLink-commander查看选项字节地址处的值 解锁读保护 错误现象 用j-flash v7.68b软件通过ARM仿真器设置接口为SWD烧录编译好的目标.bin文件&#xff0c;第一次烧录成功&#xff0c;后面再也烧录不进&#xff0c;出先现象 如…

【Unity实战】最全面的库存系统(一)

文章目录 先来看看最终效果前言定义物品定义人物背包物品插槽数据拾取物品物品堆叠绘制UI移动拖拽物品选中物品跟随鼠标移动背包物品交换物品拆分物品物品堆叠完结先来看看最终效果 前言 它又来了,库存系统我前面其实一句做过很多次了,但是这次的与以往的不太一样,这个将是…

UI设计一定不能错过的4款常用工具

虽然设计审美很重要&#xff0c;但软件只是一种工具&#xff0c;但就像走楼梯和坐电梯到达顶层一样&#xff0c;电梯的效率显然更高&#xff0c;易于使用的设计工具也是如此。让我们了解一下UI设计的主流软件&#xff0c;以及如何选择合适的设计软件。 即时设计 软件介绍 即…

蓝桥白皮书16.0版——1、STEMA 考试综述

目 录 ​​​​​​​STEMA 考试综述 STEMA 考试组别 STEMA 试题数量与计分点 STEMA 考试时长 STEMA 考试成绩计算与发布 STEMA 考试成绩计算 STEMA 考试分数区间 STEMA 考试成绩百分比 STEMA 考试命题原则 STEMA 考试范围 科技素养组考试范围 推荐初级考生阅读 &#xff1…

leetcode刷题 - SQL - 简单

目录 1. 175组合两个表 左外连接 2. 181. 超过经理收入的员工 3. 182. 查找重复的电子邮箱 4. 196. 删除重复的电子邮箱 5. 197. 上升的温度 日期作差 6. 511. 游戏玩法分析 I 7. 577. 员工奖金 null条件运算 8. 584. 寻找用户推荐人 9. 586. 订单最多的客户 10. 595. 大的国家…

频谱仪超外差和零中频架构

文章目录 超外差结构零中频结构接收机结构发射机结构 优缺点对比附录相关词汇多次变频的形象解释 参考文献 频谱仪的本质就是一个超宽带、超宽调谐范围、高动态范围的通信接收机&#xff0c; 频谱仪的原理即通信接收机的原理。 遇到高频率高带宽谐波成分复杂的通信信号的话&am…

uniapp:人脸识别功能,已测试,直接复制源码,支持H5,APP安卓。不依赖任何js,SDK。

先看效果图H5: APP效果图: H5:H5端代码用.html实现,uniapp打包H5拉相机有问题,不多赘述。 时间原因没有适配,直接用的px单位, 注意:本地无法测试,必须传到线上之后,通过https访问才能正常开启摄像头!!! <!DOCTYPE html> <html lang="en">…

python二进制文件转nc(以PIOMAS海冰厚度数据为例)

一、数据下载 数据网址Polar Science Center PIOMAS Variables on Model Grid (uw.edu) 以其中的海冰厚度数据为例进行转化 点击下载需要的年份&#xff1a; 首先要明白&#xff0c;二进制文件是4个字节按顺序依次存储所有数据&#xff0c;因此heff.H1979是没有记录对应的地…

CN考研真题知识点二轮归纳(5)

本轮的最后一贴&#xff0c;真题中涉及计网的部分彻底总结完&#xff01;后期的3轮总结可能会上一些大题&#xff0c;比如路由转发、子网划分什么的&#xff0c;以及重点的背诵内容~ 上期目录&#xff1a; CN考研真题知识点二轮归纳&#xff08;4&#xff09;https://jslhyh32…

19.10 Boost Asio 同步文件传输

在原生套接字编程中我们介绍了利用文件长度来控制文件传输的方法&#xff0c;本节我们将采用另一种传输方式&#xff0c;我们通过判断字符串是否包含goodbye lyshark关键词来验证文件是否传输结束了&#xff0c;当然了这种传输方式明显没有根据长度传输严谨&#xff0c;但使用这…

创新工具箱!重塑手机页面原型设计体验

在2024年&#xff0c;随着移动设备的普及和用户对移动体验的要求不断提升&#xff0c;手机页面原型设计工具变得越来越重要。在这篇文章中&#xff0c;我将为您推荐几款在2024年非常流行且值得一试的手机页面原型设计工具。 Pixso Pixso是一款基于云端的协作设计工具&#xf…

输入几个数,分别输出其中的奇数和偶数

这个问题我们只需要设计几个循环嵌套在一起就可以解决&#xff0c;话不多说&#xff0c;我们直接上代码 目录 1.运行代码 2.运行结果 1.运行代码 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h>int main() {int arr[10] {1,2,3,4,5,6,…

【蓝桥杯基础题】星期一

👑专栏内容:蓝桥杯刷题⛪个人主页:子夜的星的主页💕座右铭:前路未远,步履不停目录 一、题目描述二、题目分析三、代码汇总1、C++代码2、Java代码四、总结求解闰年一、题目描述 题目链接:星期一 整个20世纪(1901年1月1日至2000年12月31日之间),一共有多少个星期一…

kvm--网桥搭建

确定需要桥接的网卡 查看ip地址&#xff1a; 这里使用网卡enp125s0f2做桥接网卡 修改网卡配置 操作系统&#xff1a;kylinv10SP1(Tercel) 查看网卡配置文件&#xff1a; cd /etc/sysconfig/network-scripts/ ls 备份网卡文件 cp ifcfg-enp125s0f2 ifcfg-enp125s0f2.bak …

AutoDL 云/本地部署 百川2、GLM2

AutoDL 云上部署 百川2、GLM2 AutoDL 云上部署 百川2、GLM2配置环境体验常见问题huggingface访问不了&#xff0c;使用学术资源加速大文件上传&#xff0c;百度、阿里网盘都可CUDA 空间不足系统盘空间不足省钱绝招软件源 本地部署 百川2、GLM2根据显存大小选模型拉取docker镜像…

Linux编译器vim的使用

文章目录 vim基本概念vim的常用三种模式vim三种模式的相互转换 vim命令模式下的命令集移动光标删除文字剪切/删除复制替换撤销和恢复跳转至指定行 vim底行模式下的命令集 vim基本概念 vim是Linux下的一个多模式的编译器 简单来说就是写代码的工具 不提供编译调试等功能 有语法…

警惕!当心AI诈骗!

本文参照材料有&#xff1a; 鄂尔多斯新闻公众号、澎湃新闻网、搜孤新闻、腾讯网等 AI换脸诈骗实例&#xff08;就发生在近期&#xff09; 事例一&#xff1a; 近期 “AI换脸”新型诈骗频发和你视频对话的可能不是本人&#xff01; 近日&#xff0c;东胜市民李女士遭遇了冒充…