基于STM32设计的UNO卡牌游戏(双人多人对战)

Posted DS小龙哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于STM32设计的UNO卡牌游戏(双人多人对战)相关的知识,希望对你有一定的参考价值。

一、前言

UNO扑克是一种起源于欧洲流行于全世界的牌类游戏。简单易学,不用动什么脑筋,适合各年龄层人士玩。

"UNO"扑克是世界上最大的玩具公司美国美泰玩具公司的代表作,在全球的销售量已超过十亿付!UNO是历史上最成功的玩具,风靡世界!在国外,酒吧和咖啡馆里,UNO是年轻人最喜欢的普及型游戏。

UNO纸牌已经风靡全球数十年,被誉为是世界上最好玩的纸牌游戏,据说由意大利一个理发师发明,其中UNO的版本众多,被加入许多新的功能,玩法更加刺激,而在此游戏中最考的是集中和反应,还有相互间的思维较量。

卡牌介绍
每副UNO牌包括:108张牌(76张数字牌,24张功能牌,8张万能牌)。其中,数字牌与功能牌有红、黄、蓝、绿4种颜色,万能牌没有颜色。

数字牌
数字牌共有10种(0、1、2、3、4、5、6、7、8、9),每种4个颜色(红、蓝、黄、绿),每种颜色的0各1张、1~9各2张,共计76张。计分时按数字大小计分。

功能牌
普通功能牌同样有四种颜色(红、蓝、黄、绿),共24张,分为禁止,反转,+2 三种,各8张。

游戏基础规则
1、决定庄家。庄家可以玩家自己决定。
2、每人发7张手牌。
3、翻开牌库顶第一张作为引导牌,庄家根据引导牌出牌,然后玩家打出的牌依次成为引导牌,即最后一张打出的牌是引导牌。
4、从庄家开始,大家轮流出牌,直至有一个人出完手上的牌。

游戏行为

出牌
出牌时,可以打出与引导牌同样颜色或图案(数字、功能)的牌,也可以使用万能牌。每次只能出1张牌。

抽牌
当无牌可出时,要从牌堆顶抽取1张牌。若这张牌可以出则可以立即打出,不能出牌则跳过。

宣言UNO
打出倒数第2张手牌时,需要宣言"UNO"(优诺),表示只剩1张手牌,这有些类似于斗地主的报单,但是在UNO中是必须做的。

事实上,以上只是UNO的其中一些规则,玩家们可以按照自己喜欢的玩法来游戏。

UNO双人玩法总结:

(1)牌堆里只有数字牌,数字牌共有10种(0、1、2、3、4、5、6、7、8、9),每种4个颜色(红、蓝、黄、绿),每种颜色的0各1张、1~9各2张,共计76张。计分时按数字大小计分。

(2)一局游戏限时3分钟,3分钟时间到达之后,谁剩下的牌少就为赢家

二、设计要求

项目名称:基于stm32的UNO游戏机设计

(1)研究如何使用若干个STM32开发板、配套的液晶屏以及无线通信设备实现一个UNO对弈游戏机;

(2)能够使用无线通信技术,实现短距离内的组网,实现多个STM32的联机功能;

(3)能够根据UNO游戏规则,为多位玩家提供对弈游戏服务;

(4)具有一定的动画,提高游戏体验。

设计要求总结:

(1)LCD屏上做3个界面:开始界面,游戏界面,结束界面。

(2)游戏设备做两台,采用ESP8266-WIFI模块实现通信,实现联机对弈。牌先打完的就赢了。根据游戏规则去玩。

设备A当做主机,使用ESP8266创建热点,并创建游戏房间。

设备B当做从机,使用ESP8266链接设备A的热点,加入游戏房间。

设备A与设备B都连接上之后,开始发牌,游戏开始。

三、硬件选型

3.1 STM32开发板

主控CPU采用STM32F103RCT6,这颗芯片包括48 KB SRAM、256 KB Flash、2个基本定时器、4个通用
定时器、2个高级定时器、51个通用IO口、5个串口、2个DMA控制器、3个SPI、2个I2C、1个USB、1个
CAN、3个12位ADC、1个12位DAC、1个SDIO接口,芯片属于大容量类型,配置较高,整体符合硬件选
型设计。当前选择的这款开发板自带了一个1.4寸的TFT-LCD彩屏,可以显示当前传感器数据以及一些运
行状态信息。

3.2 LCD屏-1.44寸

3.3 ESP8266 WIFI

(1)模块采用串口(LVTTL)与MCU(或其他串口设备)通信,内置TCP/IP协议栈,能够实现串口与WIFI之
间的转换
(2)模块支持LVTTL串口,兼容3.3V和5V单片机系统
(3)模块支持串口转WIFI STA、串口转AP和WIFI STA+WIFIAP的模式,从而快速构建串口-WIFI数据传
输方案

3.4 电容矩阵键盘

四、实现思路

4.1 流程思路分析

如果需要源码,可以在这里下载:
https://download.csdn.net/download/xiaolong1126626497/85896198

这里有视频演示:

基于STM32设计的卡牌游戏uno双人对战

代码分为了两个工程,分别是从机和主机。

主机的初始化里,先完成ESP8266的初始化,配置ESP8266为AP+TCP服务器模式,创建一个热点,然后开启TCP服务器,等待从机加入。这里的从机就是玩家,玩家加入房间之后才可以进行游戏过程。

从机的初始化里,先完成ESP8266的初始化,配ESP8266为STA+TCP客户端模式,去连接指定的热点,也就是主机创建的热点,连接上之后就接着去连接服务器,连接成功,主机就进入下一步,界面上提示请按下#号按键开始游戏。这时候从机也是处于等待状态,当主机按下#号按键之后,主机就会开始洗牌Uno_shuffle(),洗牌两次之后再开始发牌 Uno_DealCard(User,num),发牌完成后会在串口上打印详细的信息,发来信息,然后从牌堆里取出引导牌,接在在主机的LCD界面上完成引导牌显示、对家牌数量,定时器时间、用户ID等信息,给对家发送的牌信息都会存放在static struct clinet_InitCard clinet_init_info结构体里。下面就调用ESP8266_ServerSendData函数将这个clinet_init_info结构体发送出去;当从机收到clinet_init_info结构体之后,解析结构体数据,完成LCD屏界面绘制显示,从机数据解析处理完毕会向主机发送USER_OK指令,表示从机已经准备好可以开始游戏,主机收到USER_OK指令之后,向串口打印游戏正式开始,接着主机界面上就会提示请出牌,然后按下矩阵键盘上的按键选择牌开始出牌。牌的选择是依靠矩阵键盘上的一排排数字,当做牌的下标键,选中拍界面上也会绘制出这张牌的信息,牌选中之后,按下#号按键即可出牌。出牌后,会发送牌的信息给从机完成交互,发送函数:ESP8266_ServerSendData(0,(u8 *)&clinet_info,sizeof(clinet_info))。开始游戏过程中,从机主机之间交互的数据传输就一直使用clinet_info这个结构体变量来存放,实现双方的数据交互通信。从机收到主机发来的clinet_info信息之后,就可以选牌,界面上会显示对家出的牌,也会显示对家牌剩余数量,在串口上也会提示当前可以出的牌是哪些牌,可以在串口调试助手上查看打印的信息。从机选择牌之后,也是和主机一样按下#号按键出牌,然后再发送clinet_info结构体给主机,这样,游戏就开始正确运行了。

在主机上还有时间显示,3分钟时间到达,就算牌没有出完,游戏也会自动结束,在界面上会提示游戏结束,显示玩家的ID,谁赢了输了。

功能牌:

/*
P -- 禁止牌
R --反转
L --加2
N --普通牌
U --万能牌
*/
const char card_func[]='P','R','L','N','U';//牌功能信息

牌堆初始化:

void Uno_Init(struct player *pls,u8 cnt)

  u8 i;
  //初始化数字牌:yellow
  for(i=0;i<19;i++)
  
    cards[i].clr=yellow;//颜色
    cards[i].function=card_none;//功能
    cards[i].num=(i==0)?0:(i%10+i/10);//数字
  
  //初始化数字牌:red
  for(;i<19*2;i++)
  
    cards[i].clr=red;//颜色
    cards[i].function=card_none;//功能
    cards[i].num=(i==19)?0:((i-19)%10+(i-19)/10);//数字
  
  //初始化数字牌:red
  for(;i<19*3;i++)
  
    cards[i].clr=green;//颜色
    cards[i].function=card_none;//功能
    cards[i].num=(i==19*2)?0:((i-19*2)%10+(i-19*2)/10);//数字
    
  //初始化数字牌:blue
  for(;i<19*4;i++)
  
    cards[i].clr=blue;//颜色
    cards[i].function=card_none;//功能
    cards[i].num=(i==19*3)?0:((i-19*3)%10+(i-19*3)/10);//数字
    
  //+2
  for(i=76;i<84;i++)
  
    cards[i].clr = setClr(i%4); 
    cards[i].function=card_plus2;//加2
    cards[i].num=-1;//-1表示功能牌
  
  //反转
  for(i=84;i<92;i++)
  
    cards[i].clr = setClr(i%4); 
    cards[i].function=card_reverse;//反转
    cards[i].num=-1;//-1表示功能牌
    
  //禁止
  for(i=92;i<100;i++)
  
    cards[i].clr = setClr(i%4); 
    cards[i].function=card_prohibit;//禁止
    cards[i].num=-1;//-1表示功能牌
  
  //万能牌
  for(i=100;i<108;i++)
  
    cards[i].clr =noColor; 
    cards[i].function=card_universal;//万能
    cards[i].num=-2;//-2表示万能牌
    
  for(i=0;i<cnt;i++)
  
    pls[i].num=0;//玩家卡牌张数清0
  
  pd.num=0;//牌堆的牌清0
  used.num=0;//已出牌数清0

牌堆洗牌:

void Uno_shuffle(void)

  u16 ran1,ran2,i;
  struct card temp;
  for(i=0;i<UNO_NUMBER*2;i++)//洗两次
  
    ran1=rand()%UNO_NUMBER;
    ran2=rand()%UNO_NUMBER;
    temp=cards[ran1];
    cards[ran1]=cards[ran2];
    cards[ran2]=temp;
  

发牌:

发牌:每人7张牌
pls --保存玩家卡牌信息
cnt --玩家个数
*/
void Uno_DealCard(struct player *pls,u8 cnt)

  u8 i,j;
  for(i=0;i<cnt;i++)//
  
    for(j=0;j<7;j++)//每个玩家7张牌
    
        pls[i].cds[j]=cards[7*i+j];
    
    pls[i].num=7;
  
  for(i=0;i<UNO_NUMBER-7*cnt;i++)//剩下的牌信息
  
    pd.cds[i]=cards[7*cnt+i];
  
  pd.num=i;//剩下的牌张数

摸牌和出牌:

/***********************从牌堆中摸牌*********************
**函数功能:从牌堆中取牌
**pls --保存玩家牌信息
**cnt --要取的牌张数
**id  --玩家id
**返回值:0xff  --摸牌失败
**         0    --正常摸牌,当前牌不可出
**        其它值  --当前摸的牌可出
**********************************************************/
u8 Uno_takeCard(struct player *pls,u8 cnt,u8 id)

  u8 i=0; 
  if(pd.num<cnt)return 0xff;//剩余牌不足
  struct card temp; 
  for(i=0;i<cnt;i++)
  
    pls[id].cds[pls[id].num]=pd.cds[pd.num-1];//从剩余牌堆中取牌
    temp=pd.cds[pd.num-1];//保存当前牌信息
    pls[id].num++;//牌张数+1
    pd.num--;//剩余牌-1
  
  if(cnt==1)//当摸一张牌时,判断当前牌是否可出
  
    u8 cnt=pls[id].num;
    if(Uno_JudgeCard(&temp)==0)
    
      return cnt-1;//若当前牌可出,则返回当前牌下标
    
  
  return 0;

/*********************出牌**************************
**函数功能:玩家出牌
**pls --保存玩家牌信息
**index --要出的牌的下标
**id  --玩家id
**返回值:0 -- 出牌成功
**        1 --出牌失败
**      
******************************************************/
u8 Uno_PlayCard(struct player *pls,u8 index,u8 id)

  if(index>=pls[id].num)return 1;//超出手牌范围
  /*判断是否能出*/
  if(Uno_JudgeCard(&(pls[id].cds[index])))return 1;//所选牌不能出
  /*判断是否为功能牌*/
  struct card temp;
  temp=pls[id].cds[index];
  if(temp.function != card_universal ) //当出的牌不为万能牌时
  
     guidecare_info=temp;  /*保存当前出的牌,作为下次出牌的引导牌*/
  
  /*将要出的牌放到已出牌堆中*/
  used.cds[used.num]=temp;
  used.num++;//已出牌数量+1
  /*将后面牌网前移动*/
  u8 i=0;
  for(i=index;i<pls[id].num-1;i++)
  
    pls[id].cds[i]=pls[id].cds[i+1];
  
  pls[id].num--;//玩家牌数量-1
  return 0;

4.2 使用的相关数据结构

#ifndef _UNO_H
#define _UNO_H
#include "stm32f10x.h"
#include <stdlib.h>
#include <stdio.h>
#include "oled.h"
#include "key.h"
#include <string.h>
#include "delay.h"
#include "esp8266.h"
extern unsigned char HZ_FONT_16[][32];
extern int uno_time;
#define LCD_W 127
#define LCD_H 127
#define UNO_NUMBER 108 //牌张数
#define PP 4 //最高玩家数
/*牌颜色*/
enum color

  yellow=0,
  red,
  green,
  blue,
  noColor,//没有颜色
  err//出错
; 
/*卡片功能*/
enum Type

  card_prohibit=0,//禁止牌
  card_reverse,//反转
  card_plus2,//加2
  card_none,//普通牌
  card_universal//万能牌
;
/*每一张卡片结构体信息*/
struct card

  enum color clr;//颜色
  enum Type function;//卡片功能
  int num;//卡片数字
;
/*玩家手头卡片信息*/
struct player

  int num;//牌数
  struct card cds[50];//手头卡片张数
;
struct paidui

  int num;//牌数
	struct card cds[UNO_NUMBER];
;
/*主从机之间交互信息*/
struct card_clinet

  struct card card_guide;//引导牌信息
  u8 id_num;//玩家剩余手牌张数
  u8 user_id;//玩家ID
  u8 pd_remain;//剩余牌堆数量
;
/*初始化客户端结构体信息*/
struct clinet_InitCard

  struct player user_card;//玩家卡牌信息
  struct paidui pd;//牌堆信息
  u8 user_id;//客户端ID
;

extern enum color colorNow;//牌颜色
extern enum Type CardType;//卡片功能
extern struct card cards[UNO_NUMBER];//每一张卡片结构体信息
extern struct paidui pd,used; //pd-牌堆,used-已出
extern struct card guidecare_info;//引导牌信息
void Uno_Init(struct player *pls,u8 cnt);//初始化牌堆
void Uno_shuffle(void);//洗牌
void Uno_DealCard(struct player *pls,u8 cnt);//发牌
u8 Uno_gameStart(u8 num);//开始游戏
#endif

以上是关于基于STM32设计的UNO卡牌游戏(双人多人对战)的主要内容,如果未能解决你的问题,请参考以下文章

双人五子棋对战(需要EasyX图像库)

双人对战的球类游戏ios源代码项目

Java五子棋课程设计

基于SpringBoot+MyBatis 五子棋双人对战

JAVA面向对象编程课程设计——多人扫雷对战

C++游戏game | 井字棋游戏坤坤版(配资源+视频)赋源码,双人对战