丄帝De咗臂 发表于 2016-2-29 19:50:37

贪吃蛇

注:以下是学生做的贪吃蛇,所有都是学生自己研究的,什么都不要问我{:5_168:}

功能:菜单里有三个选项,开始游戏,亮度和最高分。游戏规则不多说,就是需要提醒一下是可以从墙的一边穿到另一边的。亮度可调0-15,默认是3,重启不会保存。最高分同样重启不会保存。材料:Arduino Romeo,XY摇杆,MAX7219 LED矩阵模块接线:MAX7219:DIN – 6CS – 5CLK – 4XY摇杆:X – A0Y – A1Z – A2// 矩阵引脚(数字)
#define CLK4
#define CS   5
#define DIN6

// XY摇杆引脚(模拟)
#define X    0
#define Y    1
#define SW   2

#define LEFT0
#define RIGHT 1
#define UP    2
#define DOWN3
#define BTN   4

#define PRESS   0
#define RELEASE 1

#define MAXLEN 64

#define MAIN    0
#define GAME    1
#define SCORE   2
#define SETTING 3
#define TOP    4

#define START   0
#define INTENSITY 1
#define HIGHSCORE 2

typedef struct{
byte x, y;
}position;

// (类似)循环队列操作
void in(position p); // 插入新位置
position out(); // 弹出最末尾的位置
position get(); // 获取第一个位置
position get2(); // 获取第二个位置
byte length(); // 获取当前长度

// 数字,字母位图
const short numbers = {
{7, 5, 5, 5, 7},
{2, 2, 2, 2, 2},
{7, 4, 7, 1, 7},   
{7, 4, 7, 4, 7},
{5, 5, 7, 4, 4},
{7, 1, 7, 4, 7},
{7, 1, 7, 5, 7},
{7, 4, 4, 4, 4},
{7, 5, 7, 5, 7},
{7, 5, 7, 4, 7}
}, chars = {
{4, 6, 9, 9, 9, 15, 9, 9},
{4, 7, 9, 9, 7, 9, 9, 7},
{4, 6, 9, 1, 1, 1, 9, 6},
{4, 7, 9, 9, 9, 9, 9, 7},
{4, 15, 1, 1, 7, 1, 1, 15},
{4, 15, 1, 1, 7, 1, 1, 1},
{4, 6, 9, 1, 13, 9, 9, 6},
{4, 9, 9, 9, 15, 9, 9, 9},
{3, 7, 2, 2, 2, 2, 2, 7},
{4, 8, 8, 8, 8, 9, 9, 6},
{4, 9, 9, 5, 3, 5, 9, 9},
{4, 1, 1, 1, 1, 1, 1, 15},
{4, 9, 15, 15, 9, 9, 9, 9},
{4, 9, 11, 11, 13, 13, 9, 9},
{4, 6, 9, 9, 9, 9, 9, 6},
{4, 7, 9, 9, 7, 1, 1, 1},
{4, 6, 9, 9, 9, 11, 13, 14},
{4, 7, 9, 9, 7, 3, 5, 9},
{4, 6, 9, 1, 6, 8, 9, 6},
{5, 31, 4, 4, 4, 4, 4, 4},
{4, 9, 9, 9, 9, 9, 9, 6},
{4, 9, 9, 9, 9, 6, 6, 6},
{4, 9, 9, 9, 9, 15, 15, 9},
{4, 9, 9, 6, 6, 6, 9, 9},
{5, 17, 17, 10, 4, 4, 4, 4},
{4, 15, 8, 4, 6, 2, 1, 15}
};

byte disp, tail, head, direction, buttons = 0, changing, timing, rsw = 0, flowleng, part = MAIN, status = START, intensity, highscore = 0;
short valx, valy, flowx;
unsigned long delays, times, rswt;
position snake, food;
char* flows;

void in(position p){
head = (head + 1) % MAXLEN;
snake = p;
}

position out(){
position rtn = snake;
tail = (tail + 1) % MAXLEN;
return rtn;
}

position get(){
return snake;
}

position get2(){
if (head) return snake;
return snake;
}

byte length(){
if (head >= tail) return head - tail + 1;
return MAXLEN + head - tail + 1;
}

// 输出数字
void printNum(byte num, short x, short y){
for(int i = 0; i < 5; i++){
    for(int j = 0; j < 3; j++){
      if (numbers & (1 << j)) disp = !0;
      else disp = 0;
    }
}
}

// 获取字母对应序号
byte getIndex(char c){
if ((c >= 'a') && (c <= 'z')) return c - 97; // 'a' = 97
if ((c >= 'A') && (c <= 'Z')) return c - 65; // 'A' = 65
if (c >= 26) return -1;
return c;
}

// 输出字母
byte printChar(char c, short x, short y){
if ((c >= '0') && (c <= '9')){
    printNum(c - 48, x, y); // '0' = 48
    return 3;
}
c = getIndex(c);
if (c == -1) return 0;
for(int i = 0; i < 7; i++){
    if (i + y > 7) break;
    if (i + y < 0) continue;
    for(int j = 0; j < chars; j++){
      if (j + x > 7) break;
      if (j + x < 0) continue;
      if (chars & (1 << j)) disp = !0;
      else disp = 0;
    }
}
return chars;
}

// 打印字符串
void printString(char* s, short x, short y){
for(int i = 0; i < strlen(s); i++){
    x += printChar(s, x, y + ((s >= '0') && (s <= '9'))) + 1;
    if (x > 7) return;
}
}

// 流动显示字符串
void flowString(char* s){
flowleng = strlen(s);
for(int i = 0; i < strlen(s); i++){
    char c = getIndex(s);
    if (c != -1) flowleng += chars;
}
flowx = 0;
flows = s;
timerContinue(2);
}

// 停止显示流动字符串
void stopFlowing(){
timerPause(2);
}

// 生成食物
void newFood(){
byte num = random(64 - length()); // 能生成的位置只有 64 - length() 个
if ((food.x != get().x) || (food.y != get().y)){ // 初始化时可能排除上一次食物的位置
    disp = 0;
    update(food.y);
}
for(int i = 0; i < 8; i++)
    for(int j = 0; j < 8; j++)
      if (!disp){ // 身体不能生成食物
      if (num) num--;
      else{
          food.x = i;
          food.y = j;
          disp = 1;
          update(food.y);
          return;
      }
      }
}

// 向矩阵模块输出一个字节的数据
void writeByte(byte data){
for(int i = 0; i < 8; i++){
    digitalWrite(DIN, (data << i) & 0x80);
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);
}
}

// 修改矩阵模块指定寄存器的值
void writeReg(byte addr, byte data){
digitalWrite(CS, LOW);
writeByte(addr);
writeByte(data);
digitalWrite(CS, HIGH);
}

// 刷新指定行
void update(byte row){
byte t = 0;
for(int i = 0; i < 8; i++) if (disp) t |= 128 >> i;
writeReg(row + 1, t);
}

// 刷新全屏幕
void update(){
for(int i = 0; i < 8; i++){
    update(i);
}
}

// 清屏
void clrscr(){
for(int i = 0; i < 8; i++) for(int j = 0; j < 8; j++) disp = 0;
}

// 重绘
void repaint(){
clrscr();
for(int i = tail; i != head; i = (i + 1) % MAXLEN) disp.x].y] = 1;
}

// 初始化地图
void initMap(){
clrscr();
for(int i = 0; i < 4; i++){
    disp = 1;
    snake.x = i + 2;
    snake.y = 3;
}
tail = 0;
head = 3;
direction = 1 << RIGHT;
newFood();
update();
timerContinue(0);
timerContinue(1);
}

// 获取前进方向
byte getDirection(){
byte d1 = 255, d2 = 255;
for(int i = 0; i < 4; i++) if (direction & (1 << i)){
    if (d1 == 255) d1 = i;
    else {
      d2 = i;
      break;
    }
}
if (d2 == 255) return d1;
changing = !changing; // 如果摇杆掰到角落上就两个轮流来(ノへ ̄、)
if (changing) return d2;
return d1;
}

// 注册计时器
void timerRegister(byte id, unsigned long time){
delays = time;
times = millis();
timing = 0;
}

// 计时器暂停
void timerPause(byte id){
timing = 0;
}

// 计时器继续
void timerContinue(byte id){
times = millis();
timing = !0;
}

// 返回计时器状态
bool timerTiming(byte id){
return timing;
}

// 计时器事件
void timerAction(byte id){
if (id == 0){ // 蛇该动了
    byte drtn = getDirection();
    position np = get();
    // 穿墙判定不解释
    if (drtn == LEFT){
      if (np.x) np.x--;
      else np.x = 7;
    } else if (drtn == RIGHT){
      if (np.x == 7) np.x = 0;
      else np.x++;
    } else if (drtn == UP){
      if (np.y) np.y--;
      else np.y = 7;
    } else if (drtn == DOWN){
      if (np.y == 7) np.y = 0;
      else np.y++;
    }
    if ((np.x == food.x) && (np.y == food.y)){ // 吃到东西了
      disp = 1;
      update(np.y);
      in(np);
      newFood();
    } else {
      position last = out();
      disp = 0;
      if (disp){ // 咬到自己了
      byte score = length() - 3;
      timerPause(0);
      timerPause(1);
      part = SCORE;
      if (score > highscore) highscore = score;
      clrscr();
      printNum(score / 10, 0, 1);
      printNum(score % 10, 4, 1);
      update();
      }else{ // 没咬到就挪一下
      in(np);
      disp = !0;
      update(np.y);
      update(last.y);
      }
    }
} else if (id == 1){ // 食物闪一下
    disp = !disp;
    update(food.y);
} else if (id == 2){ // 字符串挪一下
    clrscr();
    printString(flows, -flowx, 0);
    update();
    flowx++;
    if (flowx == flowleng) flowx = -8;
}
}

// 按钮按下事件
void buttonPressed(byte id){
if (part == GAME){
    if (id == BTN){
    } else { // 游戏中变方向,同时还要防止头转180°这种奇葩情况。。
      position s1 = get(), s2 = get2();
      if ((id == LEFT) && ((get().x - get2().x == 1) || (get2().x - get().x == 7))) return;
      if ((id == RIGHT) && ((get2().x - get().x == 1) || (get().x - get2().x == 7))) return;
      if ((id == UP) && ((get().y - get2().y == 1) || (get2().y - get().y == 7))) return;
      if ((id == DOWN) && ((get2().y - get().y == 1) || (get().y - get2().y == 7))) return;
      direction = buttons;
      if (buttons & !(1 << id)) changing = 0;
    }
}
}

// 按钮释放事件
void buttonReleased(byte id){
if (part == MAIN){
    if (id == BTN){ // 主菜单选择了什么东西
      stopFlowing();
      if (status == START){
      part = GAME;
      initMap();
      } else if (status == INTENSITY){
      part = SETTING;
      clrscr();
      printNum(intensity / 10, 0, 1);
      printNum(intensity % 10, 4, 1);
      update();
      } else if (status == HIGHSCORE){
      part = TOP;
      clrscr();
      printNum(highscore / 10, 0, 1);
      printNum(highscore % 10, 4, 1);
      update();
      }
    } else if ((id == LEFT) || (id == UP)){ // 换选项,下同其实这最好用数组,但是只有三个就没写。。
      if (status == START){
      status = HIGHSCORE;
      flowString("highscore");
      } else if (status == INTENSITY){
      status = START;
      flowString("start");
      } else if (status == HIGHSCORE){
      status = INTENSITY;
      flowString("intensity");
      }
    } else {
      if (status == START){
      status = INTENSITY;
      flowString("intensity");
      } else if (status == INTENSITY){
      status = HIGHSCORE;
      flowString("highscore");
      } else if (status == HIGHSCORE){
      status = START;
      flowString("start");
      }
    }
} else if (part == GAME){
    if (id == BTN){ // 暂停
      if (timerTiming(0)) timerPause(0);
      else timerContinue(0);
    } else {
      if (direction & !(1 << id)) direction &= !(1 << id);
    }
} else if (part == SCORE){
    if (id == BTN){ // 回主菜单
      part = MAIN;
      flowString("start");
    }
} else if (part == SETTING){
    if (id == BTN){ // 还是回主菜单
      part = MAIN;
      flowString("intensity");
    } else{ // 改亮度
      if ((id == LEFT) || (id == UP)){
      if (intensity != 0) intensity--;
      } else {
      if (intensity != 15) intensity++;
      }
      writeReg(0xA, intensity);
      printNum(intensity / 10, 0, 1);
      printNum(intensity % 10, 4, 1);
      update();
    }
} else if (part == TOP){
    if (id == BTN){ // 仍然是回主菜单
      part = MAIN;
      flowString("highscore");
    }
}
}

void setup() {
pinMode(CLK, OUTPUT);
pinMode(CS, OUTPUT);
pinMode(DIN, OUTPUT);
intensity = 3;
writeReg(0x9, 0); // Decode Mode
writeReg(0xA, intensity); // Intensity
writeReg(0xB, 7); // Scan Limit
writeReg(0xC, 1); // Shutdown
writeReg(0xF, 0); // Display Test
update();
timerRegister(0, 1000); // move timer
timerRegister(1, 200);// food twinkle
timerRegister(2, 100);// flow string
flowString("start");
}

void loop() {
// 循环检测计时器
for(int i = 0; i < 3; i++) if ((timing) && (millis() - times >= delays)){
    timerAction(i);
    times += delays;
}
// 循环检测摇杆
if (analogRead(X) == 1023){
    if (!(buttons & (1 << RIGHT))){
      buttons |= 1 << RIGHT;
      buttonPressed(RIGHT);
    }
} else if (analogRead(X) == 0){
    if (!(buttons & (1 << LEFT))){
      buttons |= 1 << LEFT;
      buttonPressed(LEFT);
    }
} else if (buttons & (1 << RIGHT)){
    if (analogRead(X) < 923){
      buttons &= !(1 << RIGHT);
      buttonReleased(RIGHT);
    }
} else if (buttons & (1 << LEFT)){
    if (analogRead(X) >= 100){
      buttons &= !(1 << LEFT);
      buttonReleased(LEFT);
    }
}
if (analogRead(Y) == 1023){
    if (!(buttons & (1 << UP))){
      buttons |= 1 << UP;
      buttonPressed(UP);
    }
} else if (analogRead(Y) == 0){
    if (!(buttons & (1 << DOWN))){
      buttons |= 1 << DOWN;
      buttonPressed(DOWN);
    }
} else if (buttons & (1 << UP)){
    if (analogRead(Y) < 923){
      buttons &= !(1 << UP);
      buttonReleased(UP);
    }
} else if (buttons & (1 << DOWN)){
    if (analogRead(Y) >= 100){
      buttons &= !(1 << DOWN);
      buttonReleased(DOWN);
    }
}
// 循环检测按钮
if (buttons & (1 << BTN)){
    if (analogRead(SW)){
      if (rsw){
      if (micros() - rswt > 1000){ // 防止按钮按下时出现有时认为没有按下的时候,认为用户连续按下了多次
          buttons &= !(1 << BTN);
          rsw = 0;
          buttonReleased(BTN);
      }
      } else {
      rsw = 1;
      rswt = micros();
      }
    } else rsw = 0;
} else if (!analogRead(SW)){
    buttons |= 1 << BTN;
    buttonPressed(BTN);
}
}
效果图:


大连林海 发表于 2016-2-29 20:54:30

超牛逼的 学生

dsweiliang 发表于 2016-3-1 08:22:26

Romeo BLE多合一控制器好用么,我也想入一块

丄帝De咗臂 发表于 2016-3-1 09:07:38

dsweiliang 发表于 2016-3-1 08:22
Romeo BLE多合一控制器好用么,我也想入一块

果断好用

源代码 发表于 2016-3-1 10:48:19

你的学生好腻害{:5_132:}

孙毅 发表于 2016-3-1 16:10:10

张禄老师是大学老师?!
好腻害的学生!

丄帝De咗臂 发表于 2016-3-1 17:20:17

孙毅 发表于 2016-3-1 16:10
张禄老师是大学老师?!
好腻害的学生!

高中老师,学生比我厉害

iooops 发表于 2016-3-1 17:22:58

啊!

iooops 发表于 2016-3-1 17:23:08

棒!

孙毅 发表于 2016-3-1 21:48:51

棒!

mickey 发表于 2016-3-2 17:12:14

用Arduino UNO + 输入扩展板V2.0 + LCD12864 显示屏扩展板做的话,效果会更好。

丄帝De咗臂 发表于 2016-3-2 17:13:56

mickey 发表于 2016-3-2 17:12
用Arduino UNO + 输入扩展板V2.0 + LCD12864 显示屏扩展板做的话,效果会更好。

谢谢指导

20060606 发表于 2020-8-20 05:15:06

好创意,赞一个
页: [1]
查看完整版本: 贪吃蛇