基于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卡牌游戏(双人多人对战)的主要内容,如果未能解决你的问题,请参考以下文章