R实现迷宫小游戏

  • 效果图
  • 缘起
  • R的图形API
  • DFS函数生成迷宫
  • 数据结构
  • 代码解析
  • 后话
  • 附录:完整代码

效果图

缘起

刚装了Ubuntu系统,发现里面有自带的扫雷等小游戏。最近又疯狂使用R,忽然有一个念头,R做游戏如何呢?是的,刚开始我想做的是扫雷,后面发现这里面存在一个很大的问题,故放弃了,具体原因我后面会讲。我便在网上查了一下,发现还真有人用R写 贪吃蛇游戏,便花费了一晚上也简单写了个游戏试试。

R的图形API

只有4个:

  • setGraphicsEventHandlers, 注册图形事件,包括键盘事件和鼠标事件(按下,释放,移动)。
  • getGraphicsEvent, 启动图形事件监听器,开始监听。
  • setGraphicsEventEnv, 设置图形设备和环境空间,默认为当前图形设备和当前环境空间。
  • getGraphicsEventEnv, 获取图形设备,默认为当前图形设备。

我只用到了第二个,这方面的知识可参考这篇 文章,很有用!

DFS函数生成迷宫

学过数据结构的小伙伴一定很熟悉 dfs(): 广度优先搜索算法
这里生成迷宫的思想是这样的,首先看如下图,黄色的地方代表 通路,灰色代表 围墙,最外侧的四面围墙是不可拆卸的,而相邻黄色格子间的通路是可以 打通的(对应代码里的connect()函数),然后利用 DFS()实现图的遍历,把一个个黄点看成独立的结点,进行搜索,生成连接路径,把 连接路径上的墙推倒(由灰色转为黄色)。是的,你可能意识到的,用DFS生成这样的迷宫会有如下两点:

  • 每两个初始的黄色结点必有通路,因为是遍历
  • 这样生成的迷宫岔路较少(相比其他生成方法)

数据结构

前面也说到,视初始的黄色结点为 图的节点,我们用变量 block_map 进行保存,大小的变量为 size
最终生成图形黄色的部分实际上有两种:一是 初始结点 ,二是dfs()运行中 推倒的墙 (墙倒了形成了路)。我们的 block_map 只会记录初始结点,我们需要另外任命一个变量 maze_map 来记录究竟哪里是路哪里是墙,它的大小记为 size2,可以简单知道 size2 = 2 * size - 3

代码解析

输入参数为:size, cex_set(后面解释cex_set)
切记:0表示路,1表示墙

  • 初始化数据结构
size2 = 2*size -3block_map = matrix(0, size, size)maze_map = matrix(1, size2, size2)for(i in 1:(size-2)){for(j in 1:(size-2)){maze_map[2*i, 2*j]=0}}# 四面的围墙block_map[1, ] = 1block_map[size, ] = 1block_map[, 1] = 1block_map[, size] = 1
  • 使用move简便 “上下左右”的操作
  move=list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1))

使用时应用 move[[i]][j] 这样的形式,list数据类型就是会麻烦点

  • dfs() 前的准备
    in_map() 用于判断操作是否越界
    connect() 用于拆墙
    neighbor_count() 用于计算该节点周围有几面墙
in_map <- function(x, y){return((1<=x) && (x<=size) && (1<=y) && (y<=size))}
connect <- function(x, y, xx, yy){maze_map[(x+xx)-2, (y+yy)-2] <<- 0
}
neighbor_count <- function(x, y){temp = 0for(i in 1:4){if(in_map(x + move[[i]][1], y + move[[i]][2])){if(block_map[x + move[[i]][1], y + move[[i]][2]] == 1){temp = temp + 1}}}return(temp)}
  • 实施 dfs()
    看不懂的话直接上网上搜,网上有些说得很清楚。DFS()算法展开来讲会很多,这里略过。但我提一个很容易犯的错误作用域,如果这里把 block_map[xx, yy] <<- 1 错写成 block_map[xx, yy] <- 1 和 block_map[xx, yy] = 1,代码都会失效,问题在哪呢?—— 问题在于在 函数体 内你对 block_map 所作的修改并 没法传出去 !!具体可见这篇 文章
 dfs <- function(x, y){print(c(x, y, neighbor_count(x, y)))if(neighbor_count(x, y) == 4){return}direction = c(FALSE, FALSE, FALSE, FALSE)while(neighbor_count(x, y) < 4){temp = -1while(temp == -1 || direction[temp] == TRUE ){temp = sample(1:4, 1)}xx=x+move[[temp]][1]yy=y+move[[temp]][2]if(in_map(xx, yy) && block_map[xx, yy] == 0){block_map[xx, yy] <<- 1connect(x, y, xx, yy)dfs(xx, yy)direction[temp]=TRUEif(neighbor_count(x, y) == 4){return}}}return}
  • 开始画图,这里的配色你可以自己后期改,配色的话可以参考这个
windows()
plot(0,0,xlim=c(0, 10),ylim=c(0, 10),type='n',xaxs="i", yaxs="i")
for(i in 1:10){points(i - 0.5, i - 0.5, col = i, pch = 15, cex = 2.5)
}

正式画图
now.x() 和 now.y() 记录现在的位置,dest.x() 和 dest.y() 记录终点位置

  windows()plot(0,0,xlim=c(0, size2),ylim=c(0, size2),type='n',xaxs="i", yaxs="i")for(i in 1:(size2 -1)){abline(h=i,col="gray60") # 水平线abline(v=i,col="gray60") }abline(h=size2)abline(v=size2)for(i in 1:size2){for(j in 1:size2){if(maze_map[i, j]==1){points(i-0.5, j-0.5, col = 8, pch = 15, cex = cex_set)}else{points(i-0.5, j-0.5, col = 7, pch = 15, cex = cex_set)}}}now.x = 2now.y = 2dest.x = size2 - 1dest.y = size2 - 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)points(dest.x-0.5, dest.y-0.5, col = 6, pch = 15, cex = cex_set)
  • 写键盘事件
    判断是否越界 及 是否为路
    特别注意:now.y <<- now.y - 1
 if(K == "down"){if(now.y > 2 && maze_map[now.x, now.y-1] == 0){points(now.x-0.5, now.y-0.5, col = 7, pch = 15, cex = cex_set)now.y <<- now.y - 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)}}
  • 绑定键盘事件
getGraphicsEvent(onKeybd = keydown)
  • cex_set
    因为生成不同规模大小的地图,颜色填充的可能不好,要么没有填充完区域的大部份,要么超过区域到影响正常游戏操作,所以留了个cex_set设置点的大小,让使用者自己调节,实话实说,这是一处败笔,但我也懒得改了。
    (2022/7/28: 实际上可以用函数polygon,它可以很好地填充颜色,但我懒)

后话

为啥我没法做扫雷呢?因为获取到的鼠标位置和图像的位置对不上,这个问题我至今未解决。

附录:完整代码

maze <- function(size, cex_set = 2.5){# 1是墙体,0是通路size2 = 2*size -3block_map = matrix(0, size, size)maze_map = matrix(1, size2, size2)for(i in 1:(size-2)){for(j in 1:(size-2)){maze_map[2*i, 2*j]=0}}block_map[1, ] = 1block_map[size, ] = 1block_map[, 1] = 1block_map[, size] = 1move=list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1))in_map <- function(x, y){return((1<=x) && (x<=size) && (1<=y) && (y<=size))}connect <- function(x, y, xx, yy){maze_map[(x+xx)-2, (y+yy)-2] <<- 0}neighbor_count <- function(x, y){temp = 0for(i in 1:4){if(in_map(x + move[[i]][1], y + move[[i]][2])){if(block_map[x + move[[i]][1], y + move[[i]][2]] == 1){temp = temp + 1}}}return(temp)}dfs <- function(x, y){print(c(x, y, neighbor_count(x, y)))if(neighbor_count(x, y) == 4){return}direction = c(FALSE, FALSE, FALSE, FALSE)while(neighbor_count(x, y) < 4){temp = -1while(temp == -1 || direction[temp] == TRUE ){temp = sample(1:4, 1)}xx=x+move[[temp]][1]yy=y+move[[temp]][2]if(in_map(xx, yy) && block_map[xx, yy] == 0){block_map[xx, yy] <<- 1connect(x, y, xx, yy)dfs(xx, yy)direction[temp]=TRUEif(neighbor_count(x, y) == 4){return}}}return}# 设起点,并开始生成地图矩阵block_map[2, 2] = 1dfs(2, 2)# 生成地图windows()plot(0,0,xlim=c(0, size2),ylim=c(0, size2),type='n',xaxs="i", yaxs="i")for(i in 1:(size2 -1)){abline(h=i,col="gray60") # 水平线abline(v=i,col="gray60") }abline(h=size2)abline(v=size2)for(i in 1:size2){for(j in 1:size2){if(maze_map[i, j]==1){points(i-0.5, j-0.5, col = 8, pch = 15, cex = cex_set)}else{points(i-0.5, j-0.5, col = 7, pch = 15, cex = cex_set)}}}now.x = 2now.y = 2dest.x = size2 - 1dest.y = size2 - 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)points(dest.x-0.5, dest.y-0.5, col = 6, pch = 15, cex = cex_set)keydown<-function(K){K = tolower(K)print(K)if(K == "down"){if(now.y > 2 && maze_map[now.x, now.y-1] == 0){points(now.x-0.5, now.y-0.5, col = 7, pch = 15, cex = cex_set)now.y <<- now.y - 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)}}if(K == "up"){if(now.y < size2 - 1 && maze_map[now.x, now.y+1] == 0){points(now.x-0.5, now.y-0.5, col = 7, pch = 15, cex = cex_set)now.y <<- now.y + 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)}}if(K == "left"){if(now.x > 2 && maze_map[now.x - 1, now.y] == 0){points(now.x-0.5, now.y-0.5, col = 7, pch = 15, cex = cex_set)now.x <<- now.x - 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)}}if(K == "right"){if(now.x < size2 - 1 && maze_map[now.x + 1, now.y] == 0){points(now.x-0.5, now.y-0.5, col = 7, pch = 15, cex = cex_set)now.x <<- now.x + 1points(now.x-0.5, now.y-0.5, col = 2, pch = 15, cex = cex_set)}}if(now.x == dest.x && now.y == dest.y){text(2, 2, label="You Win", cex = 3)getGraphicsEvent(onKeybd = NULL)} }getGraphicsEvent(onKeybd = keydown)}maze(16)

用R写一个迷宫小游戏相关推荐

  1. Python写一个迷宫小游戏

    相关文件 关注小编,私信小编领取源码和其他小游戏的代码哟~~ 开发环境 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块. 原码.安装包(点击领取即可) 原 ...

  2. 用C语言做一个迷宫小游戏

    用C语言做一个迷宫小游戏,以下是代码段 这个迷宫游戏使用了递归回溯算法来寻找通往出口的路径.迷宫中的墙用'#'表示,路径用空格表示,入口和出口分别用'S'和'E'表示,已走过的路径用'*'表示.在生成 ...

  3. ChatGPT实现用C语言写一个扫雷小游戏

    前几天我们利用 ChatGPT实现用C语言写一个学生成绩管理系统 其过程用时不到30秒,速度惊人 今天又让ChatGPT用C语言写了一个扫雷小游戏,它的回答是:抱歉,我是AI语言模型,无法编写程序. ...

  4. ES6 手写一个“辨色”小游戏

    1. 前言 依稀记得几年前朋友圈流行的辨色小游戏,找出颜色不同的矩形.前些天突发奇想,打算自己手写一个类似的游戏,话不多说,先上 Demo . --项目源码 本实例基于 ES6 实现,并兼容 ie9及 ...

  5. 教你前端如何用js写一个跑酷小游戏

    在线体验地址:http://summer.pkec.net/ 源码地址:https://gitee.com/ihope_top/juejin-summer 前言 不知不觉夏天又到了,提到夏天你们能想到 ...

  6. 【C语言】写一个斗牛小游戏的发牌器

    我可能是个比较乱的目录 斗牛规则 创建牌库 删除牌库 洗牌 扑克的打印 扑克牌相关函数试验 按照斗牛规则发牌 游戏的实现 未来可改进 全部源码 与女朋友喜欢玩斗牛纸牌游戏.一直想用C语言实现发牌,今天 ...

  7. 【Python妙用】用200行Python代码制作一个迷宫小游戏

    相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路. 虽然 ...

  8. 俄罗斯小方块游戏html,通过h5的canvas手写一个俄罗斯方块小游戏

    开始自己手写一个好玩的俄罗斯方块吧,上变形,左右移动,下加速,空格瞬移等功能,无聊的时候学习下canvas,f12 修改分数,体验金手指的快乐吧 1.定义界面,和按钮 上 下 左 右 2.js部分 1 ...

  9. 使用C语言写一个扫雷小游戏

    前言 相信扫雷游戏小伙伴们肯定都玩过吧,学习了C语言中的数组.函数等基础内容之后就可以自己写一个简易的扫雷小游戏了,今天就我写扫雷小游戏的过程及思路写一篇博客,希望大家看完我的博客能有所收获. 软件及 ...

最新文章

  1. java 二分查找
  2. 内存对齐与sizeof
  3. django学习(2)----APP
  4. 探究Java常量本质及三种常量池(JVM)
  5. springmvc jsp页面提交表单乱码
  6. 十二、dbms_logmnr(分析重做日志和归档日志)
  7. VMware内存回收与分配机质
  8. 数据库不停机导数据方案_如何计算数据停机成本
  9. scala(10)-----Scala 闭包
  10. 如何在windows下成功的编译和安装python组件hyperscan
  11. JDBC工具类DataSourceUtils,dao接口代码示例;
  12. 图片裁剪上传插件——jquery.photoClip.js
  13. MySQL IS NULL(IS NOT NULL)使用索引分析
  14. Java开源大全 网站
  15. 解决主页被 hao.360.cn 劫持 及 分析
  16. 闹闹天宫一直显示服务器错误,闹闹天宫为什么进不去_闹闹天宫进不去解决办法_玩游戏网...
  17. Mac——技巧:修复 M1 Mac Mini 蓝牙问题
  18. 店铺小程序怎么做的?【小程序商城】
  19. python1 到n_怎么用python求1到n所有整数的和
  20. JAVA实战——视频管理系统构建common, pojo, mapper和service工程

热门文章

  1. RMA Line stuck in AWAITING_RETURN or AWAITING_RETURN_DISPOSITION (文档 ID 378221.1)
  2. 苹果 M1 单核性能勇超 Intel 11 代 i7;经纬张颖:用户增长与保护隐私不矛盾;阿里云盘正式公测 | EA周报
  3. 从静态检查工具谈代码编程规范
  4. MySQL单表查询总结
  5. 使用python对字符串进行md5加密
  6. An error occurred while attempting to sign 处理方法
  7. 数据图像处理——期末复习知识点
  8. 微信开发者工具:代码更新后页面未刷新
  9. Java输出PPT文件(二) - 占位符数据替换
  10. 配置Pico App ID