背景

初学clojure,想着看一些算法来熟悉clojure语法及相关算法实现。

找到一个各种语言生成迷宫的网站:http://rosettacode.org/wiki/Maze_generation

在上述网站可以看到clojure的实现版,本文就是以初学者的视角解读改程序。

小试牛刀

先看一些简单的示例,以帮助我们理解迷宫生成程序。

绑定符号x++

(defn f [x]
(let [x++ (+ x 5)]
#{[x x++]}))
(println (f 1))
=> #'sinlov.clojure.base-learn/f
#{[1 6]}
=> nil

Tips: 上述程序将x++绑定为x+5,不同于c语言中的自增运算符。

集合过滤

(select odd? (set [1 2 3 4 5]))
=> #{1 3 5}
(select (partial odd?) (set [1 2 3 4 5]))
=> #{1 3 5}

select语法参考文档:http://clojuredocs.org/clojure.set/select

partial解释见下文

vec交叉合并

(interleave [0 1 2] ['a 'b 'c])
=> (0 a 1 b 2 c)
(interleave [0 1 2] ['a 'b 'c] ['b])
=> (0 a b)
(interleave [0 1 2] ['a 'b 'c] (repeat 'z))
=> (0 a z 1 b z 2 c z)

文档:http://clojuredocs.org/clojure.core/interleave

transduce

transducer是clojure里面的一种编程思想,使用transducer可以简化很多语法。

可以参考这篇文章链接,帮助理解

文档:http://clojuredocs.org/clojure.core/transduce

思路

笔者阅读了迷宫生成算法,将思路整理如下

坐标点与符号映射关系

比如迷宫的左上角是如何生成的,不同大小的迷宫如何确定?

经过阅读源码发现,一个坐标点的符号与其周围4个临接点相关,如果按照坐标点表示,5个点排序顺序是一致的。

比如,上述坐标点(5,5),和其4个临界点。可以看到在该坐标系内,一个点与其临界点做成的集合排序一定是下面的顺序:

比如迷宫左上角坐标是(0, 0),该点五元组应该是

不在迷宫,不在迷宫,(0, 0), (0, 1), (1, 0)

假设不在迷宫或者该位置为空,记为0;如果是墙记为1

那么上述五元组可以换算为11100

再比如迷宫右上角,五元组为

(n-1, 0), 不在迷宫, (n, 0), (n, 1), 不在迷宫

可换算为10110

按照如上规则可以生成如下表:

["  " "  " "  " "  " "· " "╵ " "╴ " "┘ "
" " " " " " " " "╶─" "└─" "──" "┴─"
" " " " " " " " "╷ " "│ " "┐ " "┤ "
" " " " " " " " "┌─" "├─" "┬─" "┼─"]

程序代码

(ns maze.core
(:require [clojure.set :refer [intersection
select]]
[clojure.string :as str])) ;; 得到周围临界点
(defn neighborhood
([] (neighborhood [0 0]))
([coord] (neighborhood coord 1))
([[y x] r]
(let [y-- (- y r) y++ (+ y r)
x-- (- x r) x++ (+ x r)]
#{[y++ x] [y-- x] [y x--] [y x++]}))) ;; 判断位置是否为空
(defn cell-empty? [maze coords]
(= :empty (get-in maze coords))) ;; 判断位置是否为墙
(defn wall? [maze coords]
(= :wall (get-in maze coords))) ;; 过滤迷宫中指定类型的点的集合
(defn filter-maze
([pred maze coords]
(select (partial pred maze) (set coords)))
([pred maze]
(filter-maze
pred
maze
(for [y (range (count maze))
x (range (count (nth maze y)))]
[y x])))) ;; 创建新迷宫
(defn create-empty-maze [width height]
(let [width (inc (* 2 width))
height (inc (* 2 height))]
(vec (take height
(interleave
(repeat (vec (take width (repeat :wall))))
(repeat (vec (take width (cycle [:wall :empty]))))))))) (defn next-step [possible-steps]
(rand-nth (vec possible-steps))) ;; 核心算法,深度优先递归
(defn create-random-maze [width height]
(loop [maze (create-empty-maze width height)
stack []
nonvisited (filter-maze cell-empty? maze)
visited #{}
coords (next-step nonvisited)]
(if (empty? nonvisited)
maze
(let [nonvisited-neighbors (intersection (neighborhood coords 2) nonvisited)]
(cond
(seq nonvisited-neighbors)
(let [next-coords (next-step nonvisited-neighbors)
wall-coords (map #(+ %1 (/ (- %2 %1) 2)) coords next-coords)]
(recur (assoc-in maze wall-coords :empty)
(conj stack coords)
(disj nonvisited next-coords)
(conj visited next-coords)
next-coords)) (seq stack)
(recur maze (pop stack) nonvisited visited (last stack))))))) ;; 迷宫坐标与字符映射
(def cell-code->str
[" " " " " " " " "· " "╵ " "╴ " "┘ "
" " " " " " " " "╶─" "└─" "──" "┴─"
" " " " " " " " "╷ " "│ " "┐ " "┤ "
" " " " " " " " "┌─" "├─" "┬─" "┼─"]) ;; 获取迷宫坐标的类型
;; 使用5 bit表示一个点对应的字符映射
;; 例如:00111对应┘
(defn cell-code [maze coord]
(transduce
(comp
(map (partial wall? maze))
(keep-indexed (fn [idx el] (when el idx)))
(map (partial bit-shift-left 1)))
(completing bit-or)
0
(sort (cons coord (neighborhood coord))))) (defn cell->str [maze coord]
(get cell-code->str (cell-code maze coord))) ;; 将迷宫坐标转换为字符
(defn maze->str [maze]
(->> (for [y (range (count maze))]
(for [x (range (count (nth maze y)))]
(cell->str maze [y x])))
(map str/join)
(str/join \newline))) ;; 生成迷宫
(println (maze->str (create-random-maze 10 10)))

上述程序输出:

Clojure——学习迷宫生成的更多相关文章

  1. ICML 2018 | 从强化学习到生成模型:40篇值得一读的论文

    https://blog.csdn.net/y80gDg1/article/details/81463731 感谢阅读腾讯AI Lab微信号第34篇文章.当地时间 7 月 10-15 日,第 35 届 ...

  2. O(n)线性空间的迷宫生成算法

    之前所有的迷宫生成算法,空间都是O(mn),时间同样是O(mn),时间上已经不可能更优化, 于是,我就从空间优化上着手,研究一个仅用O(n)空间的生成算法. 我初步的想法是,每次生成一行,生成后立即输 ...

  3. NASNet学习笔记——   核心一:延续NAS论文的核心机制使得能够自动产生网络结构;    核心二:采用resnet和Inception重复使用block结构思想;    核心三:利用迁移学习将生成的网络迁移到大数据集上提出一个new search space。

    from:https://blog.csdn.net/xjz18298268521/article/details/79079008 NASNet总结 论文:<Learning Transfer ...

  4. [迷宫中的算法实践]迷宫生成算法——递归分割算法

    Recursive division method        Mazes can be created with recursive division, an algorithm which wo ...

  5. MySQL学习记录--生成时间日期数据

    时间数据格式组件: 组件 定义 范围 YYYY 年份,包括世纪 1000~9999 MM 月份 01(January)~12(December) DD 日 01~31 HH 小时 00~23 HHH ...

  6. Clojure学习笔记(一)——介绍、安装和语法

    什么是Clojure Clojure是一种动态的.强类型的.寄居在JVM上的语言. Clojure的特性: 函数式编程基础,包括一套性能可以和典型可变数据结构媲美的持久性数据结构 由JVM提供的成熟的 ...

  7. [ExtJS5学习笔记]第三节 sencha cmd学习笔记 生成应用程序构建的内部细节

    本文地址: http://blog.csdn.net/sushengmiyan/article/details/38316829本文作者:sushengmiyan------------------- ...

  8. Clojure学习资料

    以下大部分收藏自博客:http://blog.csdn.net/ithomer/article/details/17225813 官方文档: http://clojure.org/documentat ...

  9. Clojure 学习入门(19)—— 数组

    1.创建数组 1.1 从集合创建数组 into-array into-array (into-array aseq) (into-array type aseq) 演示样例: user=> (i ...

随机推荐

  1. ElasticSearch和Kibana 5.X集群的安装

    ElasticSearch和Kibana 5.X集群的安装 1.准备工作 1.1.下载安装包 1.2.系统的准备 2.ElasticSearch集群的安装 2.1.修改 config/elastics ...

  2. c语言中的文件格式化读写函数fscanf和fprintf函数

    很多时候我们需要写入数据到文件中时都觉得很困扰,因为格式乱七八槽的,可读性太差了,于是我们就想有没有什么函数可以格式化的从文件中输入和输出呢,还真有.下面我将讲解一下fscanf和fprintf的强大 ...

  3. 学习js函数--函数定义

    函数的定义方法有三种 1.函数表达式 2.函数声明 3,new function构造函数 这边主要看下函数表达式和函数声明 函数表达式(未省略标志的) var alertName = function ...

  4. Maven setting.xml 文件剖析

    全局配置: ${M2_HOME}/conf/settings.xml (配置环境变量  新建 M2_HOME    安装目录到版本名那里(D:\apache-maven-3.0.2) 编辑path 环 ...

  5. ROS学习记录(一)————创建简单的机器人模型smartcar

    这是我在古月居上找的(http://www.guyuehome.com/243),但直接运行的话,没办法跑起来,我也是查了好多博客和日志,才实现最后的功能的,所以,记录下来,以备后用吧,也欢迎其他和我 ...

  6. Springboot与Thymeleaf模板引擎整合基础教程(附源码)

    前言 由于在开发My Blog项目时使用了大量的技术整合,针对于部分框架的使用和整合的流程没有做详细的介绍和记录,导致有些朋友用起来有些吃力,因此打算在接下来的时间里做一些基础整合的介绍,当然,可能也 ...

  7. 【★】KMP算法完整教程

    KMP算法完整教程 全称:                               Knuth_Morris_Pratt Algorithm(KMP算法) 类型:                 ...

  8. mybaties-plus入门

    目前正在维护的公司的一个项目是一个ssm架构的java项目,dao层的接口有大量数据库查询的方法,一个条件变化就要对应一个方法,再加上一些通用的curd方法,对应一张表的dao层方法有时候多达近20个 ...

  9. JQuery中的表单验证及相关的内容

      前  言 JRedu Android应用开发中,经常要用到表单.既然用到了表单,那就不可避免的要用到表单的验证.但是,在提交表单时,但是,并不是,每次提交的表单内容都是正确的,如果 每次都将表单的 ...

  10. 团队作业8——第二次项目冲刺(Beta阶段)--第二天

    团队作业8--第二次项目冲刺(Beta阶段)--第二天 会议照片: 燃尽图: 项目进展: 今天完成了记录用户的姓名,其他的任务还在跟进. 团队贡献比: 队员 角色 团队贡献比 陈麟凤 PM 16% 张 ...