基于树莓派RP2040实现游戏“华容道”的移植项目
一、项目介绍
这里是我参加“硬禾学堂2022寒假在家练”活动所完成的项目,我选择的是设计或移植一款经典游戏——华容道,利用LCD屏显示,通过按键和四线遥感来控制游戏。
二、游戏介绍
经典游戏华容道是古老的中国游戏,以其变化多端、百玩不厌的特点与魔方、独立钻石棋一起被国外智力专家并称为“智力游戏界的三个不可思议”。 “华容道”有一个带二十个小方格的棋盘,代表华容道。棋盘下方有一个两方格边长的出口,是供曹操逃走的。棋盘上共摆有十个大小不一样的棋子,它们分别代表曹操、张飞、赵云、马超、黄忠和关羽,还有四个卒。棋盘上仅有两个小方格空着,玩法就是通过这两个空格移动棋子,用最少的步数把曹操移出华容道
三、思路分析
在设计华容道游戏时,我将棋盘分为了横向是四个,纵向是五个这样一个四×五的矩阵,共有二十个元素。
随后呢,我设计了七种棋子,包含曹操、关羽、黄忠、马超、张飞、赵云以及小兵。其中,关羽是一个横向的(在X轴占两个位置,Y轴站一个位置,即一个二×一的矩阵),黄忠、赵云、张飞、马超是纵向的(即一个一×二的矩阵)。
这里我的初始开局设计的是华容道里的经典站位——横刀立马即将曹操防止在1256的位置,黄忠放置在04的位置……(可在代码中进行调整),在棋盘上17、18的位置上,我定义了两个洞(hole),整个棋盘上只有洞可以上下左右地移动,有可能会是一个洞移动,有可能是两个洞同时移动(前一个受后一个地影响)
在键盘上接收到上下左右地命令后,首先需要判断是否可以移动,如果不能移动,则返回False,然后在main程序中设置蜂鸣器的响声来提示无效。
那么如果可以移动的话,则将洞和允许移动地方块进行交换。在交换的过程中,如果是兵(1*1矩阵)则直接进行交换。如果是马超等两个色块的纵向矩阵,则需要考虑它的移动方向,纵向移动时,洞需要移动两格,而角色对应的每一个色块只需要移动一格;横向移动时则需要考虑另一个洞是不是在移动路径上,如果在移动路径上,那么就允许移动,两个洞和色块同时进行交换,完成移动。而对于关羽这种两个色块的横向矩阵,那么情况则相反。
不停地接受命令进行位置的交换,直到最后曹操的位置到达13141718的位置时,则游戏成功,界面出现success图标。
四、软件流程图
五、软硬件介绍
1、学习平台
采用树莓派Pico核心芯片RP2040,双核ARM Cortex M+内核,可以运行到133MHZ,264KB内存,性能强大,具有高度灵活的可编程IO口,可用于高速数字接口,支持外部4路模拟信号输入,内部采用率高达500Ksps、12位精度,支持MicroPython、C、C++编程
2、开发语言
MicroPython是Python 3编程语言的一种精简而有效的实现,其中包括Python标准库的一小部分的子集,并且经过优化可在微控制器和受限环境中运行。MicroPython拥有许多高级功能,例如交互式提示,任意精度整数,闭包,列表解析,生成器,异常处理等。同时它又足够紧凑,可以在256k的代码空间和16k的RAM中运行。MicroPython的目标是与普通Python尽可能兼容,从而使您可以轻松地将代码从桌面传输到微控制器或嵌入式系统。
3、开发工具
Thonny 由爱沙尼亚的 Tartu 大学开发,它采用了不同的方法,其调试器是专为学习和教学编程而设计的。该软件基于python内置图形库tkinter开发,体积小巧,界面直观,支持语法着色、代码自动补全、debug等强劲功能,并具备了一个友好的IDE,并提供了几个有用的学习工具,所有这些都打包成一个直观的GUI,可以使人更快的熟悉Python编程语言。
六、重点代码介绍
1、程序框架
2、main.c
import uos
import machine
import st7789 as st7789
from fonts import vga1_16x32 as font2
from machine import Pin, PWM
import time
from sanguo import HRDChess
spi_sck = machine.Pin(2)
spi_tx = machine.Pin(3)
width = 240
height = 240
res = 0
dc = 1
class DispImage():
# 初始化
def __init__(self):
spi0 = machine.SPI(0, baudrate=4000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
self.disp = st7789.ST7789(spi0, width, height, reset=machine.Pin(res, machine.Pin.OUT),
dc=machine.Pin(dc, machine.Pin.OUT), xstart=0, ystart=0, rotation=0)
self.disp.fill(st7789.CYAN)
# 载入图片
self.imagedic = {}
for role in ['caocao', 'huangzhong', 'zhaoyun', 'zhangfei', 'machao', 'bing', 'guanyu']:
self.imagedic[role] = open(role + ".bmp", 'rb')
def readImgToDiso(self, pos):
# 绘制两个色块
if chess.canmovehole == 0:
hx = int(chess.hole1 % 4) # 转为二维坐标
hy = int(chess.hole1 / 4)
self.disp.fill_rect(hx * 60, hy * 48, 60, 48, st7789.GREEN)
hx = int(chess.hole2 % 4) # 转为二维坐标
hy = int(chess.hole2 / 4)
self.disp.fill_rect(hx * 60, hy * 48, 60, 48, st7789.CYAN)
else:
hx = int(chess.hole2 % 4) # 转为二维坐标
hy = int(chess.hole2 / 4)
self.disp.fill_rect(hx * 60, hy * 48, 60, 48, st7789.GREEN)
hx = int(chess.hole1 % 4) # 转为二维坐标
hy = int(chess.hole1 / 4)
self.disp.fill_rect(hx * 60, hy * 48, 60, 48, st7789.CYAN)
role = chess.findRoleByPos(pos)
# print(role)
if role == None:
return
elif role == 'bing': # 兵
img = self.imagedic[role]
hx = int(pos % 4) # 转为二维坐标
hy = int(pos / 4)
img.seek(0)
for column in range(0, 48):
buf = img.read(120)
self.disp.blit_buffer(buf, hx * 60, hy * 48 + column, 60, 1)
return
elif role == 'caocao':
pos = chess.chess[role][0]
width = 2
higth = 2
elif role in ['huangzhong', 'zhaoyun', 'zhangfei', 'machao']:
pos = chess.chess[role][0]
width = 1
higth = 2
elif role in ['guanyu']:
pos = chess.chess[role][0]
width = 2
higth = 2
img = self.imagedic[role]
img.seek(0)
hx = int(pos % 4) # 转为二维坐标
hy = int(pos / 4)
for column in range(0, higth * 48):
buf = img.read(width * 60 * 2)
self.disp.blit_buffer(buf, hx * 60, hy * 48 + column, width * 60, 1)
#显示胜利字样
def dispSuccess(self):
self.disp.text(font2,"SUCCESS!",55,110,color=st7789.RED, background=st7789.CYAN)
pwm = PWM(Pin(23))
pwm.freq(600)
controlH = machine.ADC(3) # 横向控制
controlV = machine.ADC(2) # 纵向控制
keyA = machine.Pin(5, machine.Pin.IN)
def keyaction():
adc = controlH.read_u16()
if keyA.value() == 0:
return 4
if adc < 12000: # 左
return 2
if adc > 50000: # 右
return 3
adc = controlV.read_u16()
if adc < 12000: # 上
return 0
if adc > 50000: # 下
return 1
return 5
3、sanguo.py
import sys
class HRDChess:
def __init__(self):
self.chess = {}
def reset(self):
self.chess = {'caocao': [1, 2, 5, 6], 'huangzhong': [0, 4], 'zhaoyun': [3, 7], 'zhangfei': [8, 12],
'guanyu': [9, 10], 'machao': [11, 15], 'bing': [13, 14, 16, 19]}
self.hole1 = 17 # 两个洞的位置
self.hole2 = 18
self.canmovehole = 0
self.changebox = [n for n in range(20)]
def action(self, act):
if act == 4: # 交换 可移动hole
self.canmovehole = (self.canmovehole + 1) % 2
return True,self.hole1
actecho1 = self.__checkMove(act, self.hole1)
actecho2 = self.__checkMove(act, self.hole2)
if actecho1 == False and actecho2 == False:
return False,None
if actecho1 == True and actecho2 == True:
if self.canmovehole == 0:
move = self.hole1
else:
move = self.hole2
elif actecho1 == False and actecho2 == True:
move = self.hole2
self.canmovehole = 1
else:
move = self.hole1
self.canmovehole = 0
newpos=self.__moveChess(act, move)
return True,newpos
七、项目结果及展示
1、开始界面
2、游戏中
3、游戏结束
八、未来展望
当前设计的游戏只有横刀立马这一关,在未来的学习及研究中,可以再进行丰富优化,改变初始位置,设置不同难度的多种游戏关卡,从而提高游戏的可玩性。