2021.12创客杯大一组题目 基于51的贪吃蛇小游戏
如图所示为原理图,@小华同学画的
测试出的点阵相应引脚,很乱……
话不多说直接上代码,添加了一些注释
#include "reg52.h"
#include "stdlib.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit KEY_IN_1 = P2^7; //上
sbit KEY_IN_2 = P2^4; //下
sbit KEY_IN_3 = P2^6; //左
sbit KEY_IN_4 = P2^5; //右
u8 T0RH = 0;
u8 T0RL = 0;
u8 keysta[4] = {1,1,1,1};
u8 FoodPosition[2] = {4,5}; //初始化果子
u8 snake_x[32] = {0},snake_y[32] = {0}; //蛇身最大长度
u8 snakelong = 3; //全局变量蛇长
u8 flag_x = 1; //初始化移动方向,共有四种情况 1,0 -1,0 0,1 0,-1
u8 flag_y = 0;
u8 flag_g = 1; //初始化果子标志位
u8 flag_c = 0; //吃到自己身体游戏结束
u8 flag_m = 1; //蛇身移动位
u8 score = 0;//吃果分数
u8 code mtable[10]={0x01,0xb7,0x42,0x12,0x34,0x18,0x08,0xb3,0x80,0x10};//控制数码管显示0-9
void ConfigTimer0(unsigned int ms)//定时器配置,设置成1ms进入一次中断
{
unsigned long tmp;
tmp = 12000000/12;
tmp = (tmp*ms)/1000;
tmp = 65535 - tmp;
T0RH = (unsigned char)(tmp>>8);
T0RL = (unsigned char)tmp;
EA = 1;
TMOD &= 0XF0;
TMOD |=0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1; //T0中断使能
TR0 = 1;
}
void delay(u16 i)//没有任何技术含量的延时函数
{
while(i)
{
i--;
}
}
//点阵坐标显示,因为引脚太乱了只能如此,一共64个坐标
void leddot(u8 snake_x,u8 snake_y)
{
switch(snake_x){
case 1:
{
switch(snake_y){
case 1 :P1 = 0xea;P3 = 0x2c ;break;
case 2 :P1 = 0xe9;P3 = 0x2c ;break;
case 3 :P1 = 0xcb;P3 = 0x2c ;break;
case 4 :P1 = 0xeb;P3 = 0x0c ;break;
case 5 :P1 = 0xab;P3 = 0x2c ;break;
case 6 :P1 = 0xeb;P3 = 0x24 ;break;
case 7 :P1 = 0xeb;P3 = 0x28 ;break;
case 8 :P1 = 0xe3;P3 = 0x2c ;break;
default:break;
}
break;
}
case 2:
{
switch(snake_y){
case 1 :P1 = 0x6e;P3 = 0x2c ;break;
case 2 :P1 = 0x6d;P3 = 0x2c ;break;
case 3 :P1 = 0x4f;P3 = 0x2c ;break;
case 4 :P1 = 0x6f;P3 = 0x0c ;break;
case 5 :P1 = 0x2f;P3 = 0x2c ;break;
case 6 :P1 = 0x6f;P3 = 0x24 ;break;
case 7 :P1 = 0x6f;P3 = 0x28 ;break;
case 8 :P1 = 0x67;P3 = 0x2c ;break;
default:break;
}
break;
}
case 3:
{
switch(snake_y){
case 1 :P1 = 0x6a;P3 = 0xac ;break;
case 2 :P1 = 0x69;P3 = 0xac ;break;
case 3 :P1 = 0x4b;P3 = 0xac ;break;
case 4 :P1 = 0x6b;P3 = 0x8c ;break;
case 5 :P1 = 0x2b;P3 = 0xac ;break;
case 6 :P1 = 0x6b;P3 = 0xa4 ;break;
case 7 :P1 = 0x6b;P3 = 0xa8 ;break;
case 8 :P1 = 0x63;P3 = 0xac ;break;
default:break;
}
break;
}
case 4:
{
switch(snake_y){
case 1 :P1 = 0x7a;P3 = 0x2c ;break;
case 2 :P1 = 0x79;P3 = 0x2c ;break;
case 3 :P1 = 0x5b;P3 = 0x2c ;break;
case 4 :P1 = 0x7b;P3 = 0x0c ;break;
case 5 :P1 = 0x3b;P3 = 0x2c ;break;
case 6 :P1 = 0x7b;P3 = 0x24 ;break;
case 7 :P1 = 0x7b;P3 = 0x28 ;break;
case 8 :P1 = 0x73;P3 = 0x2c ;break;
default:break;
}
break;
}
case 5:
{
switch(snake_y){
case 1 :P1 = 0x6a;P3 = 0x2d ;break;
case 2 :P1 = 0x69;P3 = 0x2d ;break;
case 3 :P1 = 0x4b;P3 = 0x2d ;break;
case 4 :P1 = 0x6b;P3 = 0x0d ;break;
case 5 :P1 = 0x2b;P3 = 0x2d ;break;
case 6 :P1 = 0x6b;P3 = 0x25 ;break;
case 7 :P1 = 0x6b;P3 = 0x29 ;break;
case 8 :P1 = 0x63;P3 = 0x2d ;break;
default:break;
}
break;
}
case 6:
{
switch(snake_y){
case 1 :P1 = 0x6a;P3 = 0x6c ;break;
case 2 :P1 = 0x69;P3 = 0x6c ;break;
case 3 :P1 = 0x4b;P3 = 0x6c ;break;
case 4 :P1 = 0x6b;P3 = 0x4c ;break;
case 5 :P1 = 0x2b;P3 = 0x6c ;break;
case 6 :P1 = 0x6b;P3 = 0x64 ;break;
case 7 :P1 = 0x6b;P3 = 0x68 ;break;
case 8 :P1 = 0x63;P3 = 0x6c ;break;
default:break;
}
break;
}
case 7:
{
switch(snake_y){
case 1 :P1 = 0x6a;P3 = 0x2e ;break;
case 2 :P1 = 0x69;P3 = 0x2e ;break;
case 3 :P1 = 0x4b;P3 = 0x2e ;break;
case 4 :P1 = 0x6b;P3 = 0x0e ;break;
case 5 :P1 = 0x2b;P3 = 0x2e ;break;
case 6 :P1 = 0x6b;P3 = 0x26 ;break;
case 7 :P1 = 0x6b;P3 = 0x2a ;break;
case 8 :P1 = 0x63;P3 = 0x2e ;break;
default:break;
}
break;
}
case 8:
{
switch(snake_y){
case 1 :P1 = 0x6a;P3 = 0x3c ;break;
case 2 :P1 = 0x69;P3 = 0x3c ;break;
case 3 :P1 = 0x4b;P3 = 0x3c ;break;
case 4 :P1 = 0x6b;P3 = 0x1c ;break;
case 5 :P1 = 0x2b;P3 = 0x3c ;break;
case 6 :P1 = 0x6b;P3 = 0x34 ;break;
case 7 :P1 = 0x6b;P3 = 0x38 ;break;
case 8 :P1 = 0x63;P3 = 0x3c ;break;
default:break;
}
break;
}
default:break;
}
}
void appearsnake() //显示蛇身
{
u8 i = 0;
for(i=0;i<snakelong;i++) //snakelong 是用来控制数组的全局关键变量
{
if((snake_x[i] != 0)||(snake_y[i] != 0))
{
leddot(snake_x[i],snake_y[i]);
delay(300); //延时,一次只显示一个点,利用视觉停留
P1 = 0x6b;
P3 = 0x2c;
}
}
}
void keyaction(u8 dat) //控制蛇头上下左右移动
{
if(dat == 0) //上
{
if(flag_x != -1){
flag_x = 1;
flag_y = 0;
}
}
else if(dat == 1) //下
{
if(flag_x != 1){
flag_x = -1;
flag_y = 0;
}
}
else if(dat == 2) //左
{
if(flag_y != -1){
flag_x = 0;
flag_y = 1;
}
}
else if(dat == 3) //右
{
if(flag_y != 1){
flag_x = 0;
flag_y = -1;
}
}
}
void keydriver()
{
u8 i;
static u8 backup[4] = {1,1,1,1};
for(i = 0;i<4;i++)
{
if(keysta[i] != backup[i])
{
if(backup[i]!=0) //上一次是弹起状态,则执行响应动作
keyaction(i);//这里才是执行动作的部分
backup[i] = keysta[i];//更新按键状态
}
}
}
void KeyScan()//按键检测函数,软件消抖
{
u8 i = 0;
static u8 keybuf[4] = {0xFF,0xFF,0xFF,0xFF};
keybuf[0] = (keybuf[0]<<1)|KEY_IN_1;
keybuf[1] = (keybuf[1]<<1)|KEY_IN_2;
keybuf[2] = (keybuf[2]<<1)|KEY_IN_3;
keybuf[3] = (keybuf[3]<<1)|KEY_IN_4;
for(i=0;i<4;i++)
{
if(keybuf[i] == 0x00)
keysta[i] = 0; //按键按下
else if(keybuf[i] == 0xFF)
keysta[i] = 1; //按键弹起
}
}
void appearguo()
{
u8 i,m,n;
do{
flag_g = 0;
m = rand()%8+1; //显示下一个果子
n = rand()%8+1;
for(i=0;i<snakelong;i++)
{
if((m == snake_x[i])&&(n == snake_y[i])) //如果果子和蛇身有重复,则flag_g置1,再生成一次
flag_g = 1;
}
}while(flag_g);
FoodPosition[0] = m;
FoodPosition[1] = n;
}
void move()//蛇移动,改变蛇的坐标
{
u8 i;
for(i = 0;i<snakelong-1;i++)
{
snake_x[i] = snake_x[i+1];
snake_y[i] = snake_y[i+1];
}
snake_x[i] += flag_x;
snake_y[i] += flag_y;
if(snake_x[snakelong-1]>8) //穿墙判断
snake_x[snakelong-1] = 1;
else if(snake_x[snakelong-1]<1)
snake_x[snakelong-1] = 8;
if(snake_y[snakelong-1]>8)
snake_y[snakelong-1] = 1;
else if(snake_y[snakelong-1]<1)
snake_y[snakelong-1] = 8;
flag_m = 0;
}
void chiguo()
{
if((snake_x[snakelong-1]) == FoodPosition[0] && (snake_y[snakelong-1]) == FoodPosition[1])
{
TR0 = 0;
snakelong++;
score++;
P0 = mtable[score];
snake_x[snakelong-1] = FoodPosition[0]+flag_x;
snake_y[snakelong-1] = FoodPosition[1]+flag_y;
appearguo();
TR0 = 1;
}
}
void eatself()
{
u8 i;
for(i = 3;i<snakelong-1;i++)//判断蛇是否吃到自己的身体
{
if(snake_x[snakelong-1] == snake_x[i]&&snake_y[snakelong-1] == snake_y[i])
{
flag_c = 1;
break;
}
}
}
int main()
{
snake_x[0] = 1;
snake_x[1] = 2;
snake_x[2] = 3;
snake_y[0] = 1;
snake_y[1] = 1;
snake_y[2] = 1;//初始化蛇身
P0 = mtable[0];
ConfigTimer0(1);//定时器0开始计时
while(1)
{
if(!flag_c) //蛇身移动位
{
appearsnake();
keydriver();
if(flag_m) //蛇身死亡位
{
move();
chiguo();
eatself();
delay(300);
}
}
}
}
void Timer0() interrupt 1 //刷新点阵,按键
{
static u16 index = 0;
static u16 count = 0;
TH0 = T0RH;
TL0 = T0RL;
KeyScan();
index++;
count++;
if( index >= 400)//蛇移动的速度
{
flag_m = 1;
index = 0;
}
if(!flag_c&&count>30)//果子显示频率
{
count = 0;
leddot(FoodPosition[0],FoodPosition[1]);
}
}
稍微简单写了点,建议学弟学妹们先去学习一下C语言,数码管和点阵的显示原理以及单片机的定时器等,然后能更好的理解程序。另外希望大家保持一颗热爱的心,不管比赛结果如何,都能继续继续努力下去,唯有时间不会辜负你