news 2026/8/31 14:03:31

Java实现六爻起卦排盘小程序:从随机算法到规则引擎实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java实现六爻起卦排盘小程序:从随机算法到规则引擎实战

简介:本资源是一套基于Java实现的六爻起卦排盘小程序完整源码,面向对周易文化与编程实践交叉领域感兴趣的开发者、传统文化爱好者及高校计算机专业学生,旨在解决传统六爻占卜手工起卦繁琐、解读门槛高、缺乏数字化工具支持等问题。压缩包共17个文件,含3个核心Java源文件(实现起卦逻辑、卦象分析与结果输出)、5个编译后class文件、7个XML配置与项目元数据文件(涵盖Maven构建、IDEA工作区及编译参数),整体仅28KB,轻量易读,结构清晰体现面向对象设计思想。已有263人学习下载,源码完整覆盖随机爻变模拟、六十四卦映射、卦辞解析等关键环节,附带标准Maven项目结构与基础测试目录,便于快速导入IDE运行、调试或二次开发,是理解传统文化算法化落地的优质教学与实践参考。 六爻起卦排盘这种项目,乍一听像玄学,细一看全是逻辑。我最初做这个小程序,其实是被朋友拉去救火的:他们有一个微信公众号需要内置一个起卦工具,但找来的外包只会套前端模板,后端起卦逻辑全拿数组硬凑,排出来的卦经常世应错位、纳甲乱套。我接手后直接用Java把整套排盘引擎重写了一遍,再用微信小程序做前端壳子,前后花了两个周末搞定。这篇博文就把这套“Java代码实现六爻起卦排盘小程序”的完整设计思路、核心算法、接口封装和上线避坑经验全部摊开讲,适合两种情况的人参考:一是Java后端想接传统文化类项目涨经验,二是对易学排盘有兴趣、想用代码验证规则的开发者。

这套小程序最终能做什么?用户在小程序里点一个“摇卦”按钮,模拟三枚铜钱摇六次,生成一个本卦;如果有动爻,再生成变卦;然后后端自动排出卦名、世应、六亲、六神,把完整的排盘信息返回前端展示。整个流程从用户点击到看到卦象,耗时不超过1秒,后端用Java实现,前端是原生微信小程序,数据全部走HTTPS接口。下面我按照从设计到落地的顺序,把每一步的关键决策和代码细节都过一遍。

1. 项目整体架构与核心需求拆解

1.1 六爻起卦到底做了什么

很多第一次接触六爻的开发者会以为“起卦排盘”就是随机生成六个阴阳爻,然后查表输出卦名。真做起来才发现,排盘是一个有严格规则的组合过程,远不止一个随机数。

完整流程是:先通过随机事件(传统是抛三枚铜钱六次)得到六个爻位,每个爻位有四种状态——少阳、少阴、老阳、老阴。其中老阳和老阴是动爻,会触发变卦。然后根据六爻的上下组合确定是六十四卦中的哪一卦,再根据卦宫、世应、纳甲、六亲、六神等规则,层层推算出完整的排盘数据。

这里有个容易忽略的点:普通随机生成六爻只能得到“本卦”,但动爻变卦、世应位置、六亲属性这些信息全部缺失。一个真正的排盘工具,核心价值不只是“摇出卦象”,而是把一整套传统规则用代码精确复现。所以我在设计时把需求拆成了三层:

  1. 随机层:模拟铜钱摇卦,生成六个爻位状态。
  2. 推演层:根据爻位组合算出卦名、本卦变卦、世应位置。
  3. 装配层:补齐纳甲、六亲、六神,输出完整排盘记录。

这三层各司其职,也正好对应了代码里的三个核心类。后面我会逐个展开。

1.2 技术栈选型和项目结构

技术选型上,我用了 Java 17 + Spring Boot 3,前端是原生微信小程序,没有引入重量级框架。为什么这样选?三点原因:

第一,排盘逻辑本身是纯计算任务,不涉及高并发、分布式,Spring Boot 足够轻量,而且 Java 的枚举和强类型非常适合表达八卦、六十四卦这种固定集合的规则数据。

第二,周易领域有很多规则表,比如八宫卦序表、纳甲表、六神顺序表。用 Java 的枚举 + Map 常量来建模,比用数据库表更直观,查询性能也更好。数据量本身很小,完全不需要引入 Redis 或数据库。

第三,小程序端不需要太重的框架。原生小程序已经支持组件化,起卦页面主要就是按钮、动画和结果展示,用原生写法反而更容易控制体积和渲染性能。

后端项目的包结构大致如下:

com.example.liuyao ├── controller │ └── DivinationController.java ├── service │ ├── DivinationService.java │ ├── CastService.java │ └── PaipanService.java ├── model │ ├── Yao.java │ ├── Hexagram.java │ ├── DivinationRequest.java │ └── DivinationResult.java ├── constant │ ├── TrigramConstant.java │ └── LiushierConstant.java └── util └── HexagramUtil.java

核心就一个思想:CastingService负责随机摇卦,PaipanService负责排盘规则串联,两者相互独立,方便单测。小程序端就两个页面,一个首页摇卦,一个详情展示,分包结构都还没用上。

2. 排盘引擎:从铜钱到卦象的算法实现

2.1 模拟铜钱摇卦:随机数与概率模型

传统六爻起卦最常见的铜钱法,是用三枚铜钱同时抛掷,看正反面组合来定爻。铜钱有正反两面,正面记为3,反面记为2。三枚铜钱一起抛,结果只有四种:

  • 三个正面:3+3+3=9,叫老阳,是动爻。
  • 两正一反:3+3+2=8,叫少阴。
  • 一正两反:3+2+2=7,叫少阳。
  • 三个反面:2+2+2=6,叫老阴,是动爻。

这里很多人会问:正反面概率不是各50%吗?为什么不是四种结果各25%?因为组合数不同。三个正面只有一种排列(正正正),但两正一反有三种排列(正正反、正反正、反正正)。所以老阳和老阴的概率都是 1/8,少阴和少阳的概率都是 3/8。这种概率分布和真实铜钱完全一致。

用 Java 模拟非常直接,一个Random实例就能搞定:

public class CastService { private static final Random RANDOM = new Random(); public List<Yao> castSixYao() { List<Yao> result = new ArrayList<>(6); for (int i = 0; i < 6; i++) { result.add(castOneYao()); } return result; } private Yao castOneYao() { int sum = 0; for (int i = 0; i < 3; i++) { // 1 表示正面,0 表示反面 sum += RANDOM.nextInt(2) == 1 ? 3 : 2; } return switch (sum) { case 6 -> new Yao(YaoType.LAO_YIN, true); case 7 -> new Yao(YaoType.SHAO_YANG, false); case 8 -> new Yao(YaoType.SHAO_YIN, false); case 9 -> new Yao(YaoType.LAO_YANG, true); default -> throw new IllegalStateException("Unexpected sum: " + sum); }; } }

注意Yao对象里我保存了两个关键信息:爻类型和是否为动爻。动爻是后面生成变卦的依据,老阳变少阴,老阴变少阳。这个封装在后面排盘时会非常方便。

有同学可能会说:随机数够不够随机?我用Random会不会被质疑?说实话,周易起卦讲究的是“心诚则灵”,从代码角度我们只需要保证概率分布正确即可。Random是伪随机,但对于这种非加密场景完全够用。如果你实在在意,可以用SecureRandom替代,代码改动只需一行。

2.2 纳甲装卦与六亲推算的编码思路

六爻排盘的核心难点不在随机摇卦,而在装卦规则。装卦等于给每一个爻位贴上标签:这个爻属于哪个天干地支,对应什么六亲,世爻在哪,应爻在哪,卦身是什么,六神怎么排。这些规则在传统书籍里有固定说明,但要用代码表达,就得把规则表全部转换成可查询的数据结构。

先说纳甲。纳甲是把十天干和十二地支分配到六十四卦的六个爻上。每个卦的内卦(下三爻)和外卦(上三爻)分别纳一组干支。以乾卦为例,内卦纳“子寅辰”,外卦纳“午申戌”,配上甲壬两个天干;坤卦内卦纳“未巳卯”,外卦纳“丑亥酉”,配上乙癸。

我的做法是建一张Map<Trigram, List<String>>,key 是内外卦,value 是六爻对应的地支序列,再配合天干生成完整的干支标记。这样在代码里查询一个爻的地支,时间复杂度就是 O(1)。

public class NaJiaUtil { // key: 八卦名称 + "/" + 内或外 // value: 六个地支(内卦三爻 + 外卦三爻) private static final Map<String, List<String>> NA_JIA_MAP = new HashMap<>(); static { // 乾内卦纳子寅辰,乾外卦纳午申戌 NA_JIA_MAP.put("乾/内", Arrays.asList("子", "寅", "辰")); NA_JIA_MAP.put("乾/外", Arrays.asList("午", "申", "戌")); // 坤内卦纳未巳卯,坤外卦纳丑亥酉 NA_JIA_MAP.put("坤/内", Arrays.asList("未", "巳", "卯")); NA_JIA_MAP.put("坤/外", Arrays.asList("丑", "亥", "酉")); // 震、巽、坎、离、艮、兑依此类推 } public static String getDiZhi(String trigram, boolean inner, int pos) { String key = trigram + "/" + (inner ? "内" : "外"); return NA_JIA_MAP.get(key).get(pos); } }

再说六亲。六亲是“父母、兄弟、子孙、妻财、官鬼”加一个“我”(世爻)。定六亲的规则是以卦宫的五行为基准:生我者为父母,我生者为子孙,克我者为官鬼,我克者为妻财,比和者为兄弟。所以要先计算出卦宫五行,再用爻的五行去对比。

在代码里,五行用枚举来表示,比较逻辑就是一个简单的五行生克表:

public enum WuXing { 金, 木, 水, 火, 土; public boolean generates(WuXing other) { return switch (this) { case 金 -> other == 水; case 水 -> other == 木; case 木 -> other == 火; case 火 -> other == 土; case 土 -> other == 金; }; } public boolean overcomes(WuXing other) { return switch (this) { case 金 -> other == 木; case 木 -> other == 土; case 土 -> other == 水; case 水 -> other == 火; case 火 -> other == 金; }; } }

有了这套枚举方法,六亲的判断就是几个if/else的事,不容易出错,也方便后续做卦象解读时扩展。

世应定位我采用查表法。八宫六十四卦的世爻位置是有固定规律的:八纯卦世在上爻,一世卦世在初爻,二世卦世在二爻,以此类推。这个规律我直接写在一个静态映射里,用卦名查世爻位置,然后根据“世应相隔两爻”的规则推应爻位置。

这里最大的坑是:必须搞清楚内卦和外卦的五行属性。很多初学者会把“卦的五行”和“爻的五行”搞混。比如乾宫卦属金,但乾卦内卦的初爻纳甲是“子水”,这个“水”是爻的五行,不能直接拿来做六亲判断时当卦宫五行用。我做了一个小小的工具方法,专门取卦宫五行,避免在循环里误用爻五行。

2.3 本卦变卦生成与六神装配

有了六个爻,下一步就是组装卦象。本卦就是六个爻从下往上排列,下三爻为内卦,上三爻为外卦,通过查六十四卦表拿到卦名。变卦则是把所有动爻的阴阳属性反转:老阳变少阴,老阴变少阳,动静爻保持不变。

这里有个细节:起卦时爻是从初爻开始往上记录,也就是第一次摇的卦在最下面。代码里用List<Yao>的第0个元素表示初爻,但输出给前端展示时要按“上爻在上一行”的顺序排列,这个先后顺序特别容易搞反。我在真实项目里就遇到过一次:排出来的卦名是对的,但页面展示时上下颠倒,用户反馈“卦象看起来怪怪的”。排查过程很简单,打印一份排盘记录对比传统排盘软件就发现了。

六神装配相对机械。六神是青龙、朱雀、勾陈、螣蛇、白虎、玄武,按日干的五行属性确定起始六神,然后按固定顺序依次配到六个爻上。这个功能在MVP版本里可以做成可选,因为不是所有排盘场景都需要六神。我在第一版里先预留了接口,第二版才补上。

3. 后端接口与小程序页面联调

3.1 Spring Boot接口设计与数据格式

后端接口我设计得非常简单,就一个 POST 接口:/api/divination/cast。请求体只有一个字段,代表起卦方式,默认是“copper”,也就是铜钱法。返回体是一个结构完整的 JSON,前端拿到后直接渲染。

@RestController @RequestMapping("/api/divination") public class DivinationController { private final DivinationService divinationService; public DivinationController(DivinationService divinationService) { this.divinationService = divinationService; } @PostMapping("/cast") public Result<DivinationResult> cast(@RequestBody DivinationRequest request) { return Result.success(divinationService.cast(request)); } }

DivinationResult的结构大致如下:

{ "benGua": { "name": "乾为天", "symbol": "111111", "yaoList": [ {"position": 6, "type": "老阳", "isMoving": true, "liuqin": "父母", "dizhi": "戌", "liuShen": "玄武"}, ... ] }, "bianGua": { "name": "天风姤", "symbol": "111110" }, "shiYao": 6, "yingYao": 3, "guaGong": "乾宫", "wuXing": "金" }

使用Result<T>统一包装,是为了前端方便做统一错误提示。建议每个字段都加上注释说明含义,尤其是yaoList里面的爻顺序,一定要写清楚是从下往上还是从上往下,不然过两个月你自己回来看代码都会懵。

接口层没有做复杂的鉴权,因为这种工具类小程序都是匿名访问,但我在网关层做了简单的限流,防止有人用脚本恶意刷请求。限流用 Spring Boot 自带的RateLimiter或者 Redis + Lua 都能实现,MVP 阶段先用本地ConcurrentHashMap做了一个简单的计数器就够了。

3.2 小程序端摇卦交互和常见适配

小程序端就两个核心页面:首页和结果页。首页放一个大按钮,用户点击后播放一个铜钱摇动的动画,动画结束时调用后端接口取结果,再跳转到结果页展示完整排盘。

摇卦动画我用了小程序自带的animationAPI,控制一个铜钱图片的旋转和上下跳动。重点不是动画本身,而是动画和接口调用的时序:必须在动画结束后再发请求,否则用户会觉得“卦还没摇完就出结果了”,体验很差。我用的方案是setTimeout固定动画时长,在回调里执行wx.request

playAnimation() { const query = wx.createSelectorQuery() query.select('.coin').boundingClientRect() query.exec((res) => { this.animate('.coin', [ { translateY: 0 }, { translateY: -30 }, { translateY: 0 } ], 300, () => { wx.request({ url: `${API_BASE}/api/divination/cast`, method: 'POST', data: { method: 'copper' }, success: (res) => { wx.navigateTo({ url: `/pages/result/result?data=${JSON.stringify(res.data.data)}` }) } }) }) }) }

页面适配方面有几个实际踩过的坑。第一个是顶部导航栏高度:小程序在 iPhone X 及以上机型有刘海,自定义导航时需要用wx.getMenuButtonBoundingClientRect()动态计算胶囊按钮位置,再算出导航栏高度。直接写死 64px 在全面屏上会顶到状态栏,很丑。

第二个是单选框组件的使用。如果后续要做“手动指定动爻”的排盘模式,一定会用到radio-groupradio。这里有一个老问题:radiolabelvalue区分要提前设计清楚,我第一版直接把value绑成数字 1-6,后面发现传给后端时还要做一次映射,非常麻烦。建议用 “初爻”“二爻” 这种可读性强的字符串做 value,前端展示和调试都清晰。

第三个是分包异步化。如果你准备把六十四卦卦辞库做进去,包体积会膨胀很快。原生小程序的subpackages分包机制可以解决,但要注意:如果用 “分包异步化” 的require.async加载,在低版本微信上有兼容问题,需要在app.json里配置好lazyCodeLoading: "requiredComponents",并且做好真机测试。我目前把所有卦辞放在一个单独分包里,主包只保留首页和结果页,加载速度明显提升。

4. 排盘校验与上线避坑实录

4.1 用已知卦例验证排盘结果

排盘逻辑写完之后,最重要的一步不是上线,是校验。易学排盘这东西,错了就是错了,哪怕是世应差一位,行业内的人一眼就能看出来。所以我花了一整天时间,用手工排盘对了几十个卦例。

一个比较高效的校验方法是:先写单元测试,用固定的六个爻输入,断言输出的卦名、世应、六亲完全等于预期的排盘结果。比如乾为天卦,六个爻都是少阳,卦宫是乾宫,五行属金,世爻在上爻,应爻在三爻。这是一个非常经典的测试用例。

@Test void testQianWeiTian() { List<Yao> yaos = Arrays.asList( new Yao(YaoType.SHAO_YANG, false), new Yao(YaoType.SHAO_YANG, false), new Yao(YaoType.SHAO_YANG, false), new Yao(YaoType.SHAO_YANG, false), new Yao(YaoType.SHAO_YANG, false), new Yao(YaoType.SHAO_YANG, false) ); Hexagram result = paipanService.buildBenGua(yaos); assertEquals("乾为天", result.getName()); assertEquals("乾宫", result.getGuaGong()); assertEquals(6, result.getShiYao()); assertEquals(3, result.getYingYao()); }

这种用例的价值在于:它把排盘规则固化成代码,之后任何人改动逻辑,只要跑一遍测试就能知道有没有破坏原有规则。建议把八宫卦每一宫的首卦都写成一个TestCase,覆盖率足够高。

我实际开发中还发现了一个隐蔽问题:当本卦和变卦都有动爻时,六亲是按本卦宫位定,还是按变卦宫位定?正确做法是按本卦宫位定六亲,变卦只显示变卦卦名和变爻的最终状态,不重新排六亲。很多新手排盘工具在这里会出错,因为直接用变卦去查了一遍六亲。我把这个逻辑单独抽成一个方法,并且加了一行注释:六亲以本卦宫位为准。

4.2 小程序上线遇到的实际问题

上线阶段的问题,和纯后端排盘完全不同,主要集中在微信公众平台审核、域名HTTPS、真机兼容三块。

第一块是类目资质问题。如果你的服务涉及“占卜算命”,微信审核会卡得很严。我这次没有直接做“占卜”功能,而是定位成“传统文化学习与娱乐工具”,在提审时如实选择了工具类目,提交了软著证明,审核顺利通过。这里提醒一句:建议功能描述里不要出现“预测吉凶”“算命”“占卜”这类敏感词,避免审核被拒。这个红线要守住,内容只保留排盘结果,不做任何吉凶断语。

第二块是域名和HTTPS。小程序的wx.request强制要求HTTPS域名,且域名必须在小程序后台配置为合法域名。开发调试期间可以通过“不校验合法域名”选项绕过,但真机预览、体验版和正式版都必须在后台配好。

第三块是真机兼容。我遇到过安卓和iOS上动画表现不一致的情况:安卓手机动画播放时偶发卡顿,用户点击按钮后动画没播完就跳页面了。原因是动画执行过程中如果被wx.navigateTo打断,某些安卓机型的 WebView 会直接丢弃剩余动画帧。解决办法是把动画时长调短到300ms,同时在动画回调里加了节流标志,防止用户快速连点多次请求。

还有一个实际遇到的怪问题:有用户反馈在安卓14上蓝牙开关打开时,小程序偶发卡死。排查后发现和我的小程序没直接关系,是微信基础库和系统蓝牙服务的兼容性bug,一般通过升级微信版本解决。这个问题我从后台日志里定位到是某些老版本基础库渲染 card 组件时的内存溢出,后来把结果页的card组件换成普通view,问题就没再出现了。

4.3 排盘引擎性能与扩展设计

排盘引擎的最终性能很好,单次请求平均耗时在 5ms 以下,主要耗在 JSON 序列化上,计算本身几乎可以忽略。但我建议不要在这个阶段过早优化,而是把注意力放在扩展性上。

比如后续如果要做每日运势、卦象详解、六十四卦文库,建议把卦辞和爻辞做成单独的数据类,而不是写死在枚举里。还可以预计算好所有六十四卦的静态信息,在启动时加载到内存,请求时直接查表,性能会更快。

我个人在实际操作中的一个体会是:千万不要在排盘逻辑里混入业务展示逻辑。比如前端需要把卦象画成六条横线,后端的symbol字段就用字符串111111表示,不要把“横线宽度”“颜色”这些展示属性塞进后端返回里。前后端各管一边,后期改版才不痛苦。

写在最后的几个经验

这个项目前后实际投入大约三天,最难的不是代码,而是把一套没有统一标准的传统规则转化成程序员能看懂的确定性逻辑。我建议后来者先不要急着写代码,先把六十四卦、纳甲、世应这些规则表整理成Excel,确认没有歧义再动手,能省下大量返工时间。

另外一个小技巧:起卦接口一定要做单元测试和日志打印。日志里把每次摇卦的结果完整打出来,包括六个爻的类型、是否动爻、生成的卦名、世应位置。这样即使线上出问题,也能靠日志快速定位是随机源的问题还是规则表的问题,而不是靠用户截图前端页面来猜。

如果你也准备做类似的传统文化数字化工具,可以从这个起卦排盘小程序入手。它的规则足够固定、边界足够清晰,非常适合用来练手领域建模和规则引擎设计,做完之后你对 Java 枚举、表驱动、前后端联调的理解都会上一个台阶。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 14:01:36

STM32+JQ8900公交车报站语音系统设计与实战经验分享

简介&#xff1a;本资源是一个基于STM32F10x系列微控制器的公交车智能语音报站系统完整工程&#xff0c;面向嵌入式初学者与课程设计开发者&#xff0c;解决公共交通场景中自动语音提示、站点精准播报与硬件协同控制等实际问题。压缩包含222个文件&#xff0c;总大小8.45MB&…

作者头像 李华
网站建设 2026/8/31 14:00:51

基于Java的Timeline抽象库:统一Feed、IM与推送的数据流分发实践

简介&#xff1a;这是一份面向中高级Java后端开发者与社交平台架构师的轻量级抽象库&#xff0c;聚焦Timeline模式下的朋友圈、微博类feed流构建、IM实时通讯及消息推送系统开发&#xff0c;解决数据流分发、时序聚合、离线同步与高并发推送等核心难题。资源包共71个文件&#…

作者头像 李华
网站建设 2026/8/31 13:57:37

途虎养车2023秋招算法笔试题解析:从KMP到业务场景

1. 看一份算法笔试卷&#xff0c;先看它在筛选什么 说到“途虎养车2023秋招算法笔试试卷A”&#xff0c;很多准备秋招的同学第一反应是到处找原题、背答案。我做了几年算法工程师&#xff0c;也参与过校招笔试出题和面试&#xff0c;这里先说一个可能不太中听但很真实的话&…

作者头像 李华
网站建设 2026/8/31 13:56:48

Gradio 3 行代码搭建模型界面:Colab 零成本部署演示 Demo

Gradio 3 行代码搭建模型界面&#xff1a;Colab 零成本部署演示 Demo 【免费下载链接】gradio Build and share delightful machine learning apps, all in Python. &#x1f31f; Star to support our work! 项目地址: https://gitcode.com/GitHub_Trending/gr/gradio …

作者头像 李华
网站建设 2026/8/31 13:56:45

MATLAB实现核偏最小二乘KPLS:从原理到代码的非线性建模指南

简介&#xff1a;本资源是面向机器学习与数据分析初学者及科研人员的MATLAB版核偏最小二乘&#xff08;KPLS&#xff09;算法实现包&#xff0c;专为解决高维、非线性回归与建模问题设计&#xff0c;适用于化工过程建模、光谱分析、生物信息等需强非线性拟合能力的场景。压缩包…

作者头像 李华
网站建设 2026/8/31 13:56:14

具身智能商业化:从“接工单”到“算ROI”的系统工程

在机器人赛道&#xff0c;最近两年有一个明显变化&#xff1a;很多团队不再只聊“我们的机器人能做什么动作”&#xff0c;而是开始聊“这个项目多久回本、一年能替客户省多少成本”。这背后不只是市场话术变了&#xff0c;而是具身智能的商业化逻辑正在从“接工单”切换到“算…

作者头像 李华