简介:这是一套面向易学研究者、前端开发者及传统文化爱好者的六爻占卜工具开源实现,解决传统起卦流程繁琐、解析门槛高、跨端体验不一致等问题。资源为纯前端TypeScript工程,共23个文件,含10个tsx核心组件、5个ts业务逻辑与工具类、3个json配置文件(含元数据与环境变量)、2个html入口与资源页,包体仅31KB,轻量易部署。已有152人学习下载,适合希望快速理解六爻排盘原理、定制化扩展卦辞解读或集成至自有应用的中初级开发者。源码结构清晰分层:views层组织功能视图,components封装卦象渲染(如HexagramLines.tsx)、摇卦交互(Coin.tsx)等原子模块,services与utils提供时间排盘、六亲推演、伏神计算等核心算法,预留接口支持历史记录、AI卦解等二次开发,README.md详述运行与拓展方式。
1. 项目概述:从“玄学”到“算法”的现代实践
最近在整理个人项目仓库时,翻出了一个几年前写的“六爻起卦工具”的源码。这个项目源于一个非常个人化的需求:我身边有不少对传统文化感兴趣的朋友,他们偶尔会想用六爻来辅助决策或思考,但传统的蓍草或铜钱起卦法,步骤繁琐且随机性难以保证,对于现代人来说,时间和环境都不太允许。于是,我就琢磨着,能不能用代码来模拟这个“随机”的过程,做一个既尊重传统逻辑,又方便快捷的数字化工具?这个工具的核心,就是用程序来模拟三次投掷硬币(或铜钱)的过程,根据正反面的组合生成一个“爻”,重复六次得到完整的卦象,并自动匹配《周易》六十四卦的卦辞、爻辞进行解读。
这听起来有点“跨界”,一边是古老的东方智慧,一边是冰冷的计算机代码。但实际操作下来,你会发现,这本质上是一个随机数生成、状态映射和规则解析的经典编程问题。它不涉及任何“超自然”的信仰,而是对一套既定规则系统的数字化实现。对于开发者而言,这是一个绝佳的练手项目,可以深入理解状态机、数据建模(如何优雅地存储和查询六十四卦的复杂信息)、以及如何设计一个清晰的用户界面来呈现结构化结果。对于传统文化爱好者,它则是一个随时可用的“数字卦筒”,消除了起卦过程中的物理限制和心理干扰,让关注点回归到卦象本身的思考上。
今天,我就把这个项目的完整源码和设计思路分享出来。无论你是想学习如何用代码处理复杂规则系统,还是单纯想拥有一个属于自己的起卦工具,相信这份“干货”都能给你带来启发。我们将从核心算法讲起,一步步拆解数据结构的构建、前后端的实现,以及那些我踩过坑后才总结出的注意事项。
2. 核心算法与规则的数字建模
六爻起卦的规则,是整個项目的基石。用代码实现的第一步,就是必须把这些流传千年的规则,毫无歧义地翻译成计算机能理解的逻辑。
2.1 爻的生成:三变得一爻
传统方法是用50根蓍草经过“三变”得出一爻,我们常用更简易的“钱币法”来模拟:设定硬币正面(有字面)为数字3,反面(有图案面)为数字2。一次投掷三枚硬币,其总和只有四种可能:
- 6 = 2+2+2 (三反):老阴,记为
▅▅ ▅▅ X(变爻) - 7 = 3+2+2 (一正两反):少阳,记为
▅▅▅▅▅(不变爻) - 8 = 3+3+2 (两正一反):少阴,记为
▅▅ ▅▅(不变爻) - 9 = 3+3+3 (三正):老阳,记为
▅▅▅▅▅ O(变爻)
在代码中,这就是一个典型的随机数生成与条件判断。我们需要一个函数,模拟一次投掷,返回爻的类型和其对应的数值表示。
import random def generate_yao(): """ 模拟三枚硬币投掷,生成一个爻。 返回: (yao_type, yao_symbol, is_change) """ # 模拟三枚硬币,随机生成3或2 coins = [random.choice([2, 3]) for _ in range(3)] total = sum(coins) if total == 6: return 'old_yin', '▅▅ ▅▅ X', True # 老阴,变爻 elif total == 7: return 'young_yang', '▅▅▅▅▅', False # 少阳,不变 elif total == 8: return 'young_yin', '▅▅ ▅▅', False # 少阴,不变 elif total == 9: return 'old_yang', '▅▅▅▅▅ O', True # 老阳,变爻 else: # 理论上不会发生,但保持健壮性 raise ValueError(f"Invalid coin sum: {total}")注意:这里的随机数生成器
random使用的是伪随机算法,对于此类应用完全足够。如果你追求更不可预测的随机源,可以考虑接入系统熵池(如os.urandom)或让用户参与随机过程(如要求用户输入一个随机字符串作为种子)。但核心在于,算法本身是对物理过程的模拟,其“随机性”的哲学意义应由使用者自行理解。
2.2 卦的构成:从下到上的堆叠
一个完整的卦由六个爻组成,顺序是从下往上(初爻、二爻、三爻、四爻、五爻、上爻)。在程序中,我们用一个列表来存储这六个爻的信息,列表的第一个元素是初爻。
def generate_gua(): """生成一个完整的六爻卦""" gua = [] for i in range(6): yao_info = generate_yao() # 存储信息:位置、类型、符号、是否为变爻 gua.append({ 'position': i + 1, # 位置,1为初爻 'type': yao_info[0], 'symbol': yao_info[1], 'is_changing': yao_info[2] }) return gua2.3 本卦、变卦与动爻:核心逻辑解析
这是六爻推算中最精妙也最容易出错的部分。
- 本卦:最初生成的六个爻所直接对应的卦。
- 动爻(变爻):在生成过程中,标记为
is_changing为True的爻(即老阴或老阳)。 - 变卦:将本卦中的所有动爻进行阴阳转换(老阴变少阳,老阳变少阴)后,得到的新卦。
例如,本卦的初爻是老阳(▅▅▅▅▅ O),那么在变卦中,初爻就变为少阴(▅▅ ▅▅)。不变爻则保持不变。
def get_changing_yao_indices(gua): """获取卦中所有变爻的位置索引(0-based,从初爻开始)""" return [i for i, yao in enumerate(gua) if yao['is_changing']] def apply_change(gua, changing_indices): """根据变爻索引,生成变卦的爻列表""" changed_gua = [] for i, yao in enumerate(gua): if i in changing_indices: # 阴阳互变 if yao['type'] == 'old_yang': # 老阳 -> 少阴 new_yao = {'position': yao['position'], 'type': 'young_yin', 'symbol': '▅▅ ▅▅', 'is_changing': False} elif yao['type'] == 'old_yin': # 老阴 -> 少阳 new_yao = {'position': yao['position'], 'type': 'young_yang', 'symbol': '▅▅▅▅▅', 'is_changing': False} else: # 非动爻,理论上不会进入此分支 new_yao = yao.copy() changed_gua.append(new_yao) else: # 不变爻直接复制 changed_gua.append(yao.copy()) return changed_gua2.4 卦象匹配:构建六十四卦数据库
有了爻的列表,我们需要将其映射到具体的六十四卦之一。六爻卦可以看作是两个三爻的“经卦”上下叠加而成。上卦(四、五、上爻)和下卦(初、二、三爻)各对应八卦之一。
首先,定义八卦:
# 用三位二进制表示八卦,0为阴(-),1为阳(—),从下往上读。 # 例如:乾 (111),坤 (000),震 (001),巽 (110)... BAGUA_MAP = { (1, 1, 1): ('乾', '天', '☰'), (0, 0, 0): ('坤', '地', '☷'), (1, 0, 0): ('震', '雷', '☳'), (0, 1, 0): ('坎', '水', '☵'), (1, 1, 0): ('艮', '山', '☶'), (0, 0, 1): ('巽', '风', '☴'), (1, 0, 1): ('离', '火', '☲'), (0, 1, 1): ('兑', '泽', '☱'), }将爻转换为二进制:少阳(阳爻)为1,少阴(阴爻)为0。老阳和老阴在成卦时按其变化前的状态算(即老阳为阳1,老阴为阴0),在变卦时则按变化后的状态算。
然后,根据上下卦的组合,查询预置的六十四卦数据库。这个数据库需要包含:卦序、卦名、拼音、上下卦组合、卦辞、彖辞、大象辞,以及每一爻的爻辞和象辞。我选择用JSON文件来存储,结构清晰且易于维护。
// gua_data.json 片段 { "1": { "sequence": 1, "name": "乾", "pinyin": "Qián", "upper": "乾", "lower": "乾", "hexagram": "䷀", "gua_ci": "元亨利贞。", "tuan_zhuan": "大哉乾元,万物资始,乃统天...", "da_xiang": "天行健,君子以自强不息。", "yao": [ {"position": 1, "yao_ci": "潜龙勿用。", "xiang_ci": "潜龙勿用,阳在下也。"}, {"position": 2, "yao_ci": "见龙在田,利见大人。", "xiang_ci": "见龙在田,德施普也。"}, // ... 其余四爻 ] }, "2": { "sequence": 2, "name": "坤", "pinyin": "Kūn", "upper": "坤", "lower": "坤", "hexagram": "䷁", // ... 其他字段 } // ... 其余62卦 }实操心得:构建这个数据库是最耗时但也是最基础的一步。务必核对古籍,确保卦辞、爻辞的准确性。我最初从网络爬取的数据存在不少错漏和格式问题,手动校对了一遍才敢用。此外,爻辞的索引一定要与爻位(初、二、三、四、五、上)严格对应,这是后续查询的关键。
3. 系统架构与模块化实现
一个完整的工具不能只有算法,还需要考虑用户交互和数据流转。我采用了前后端分离的简单架构,后端提供核心计算和卦辞查询API,前端负责展示和交互。
3.1 后端设计:Python Flask 应用
后端主要负责三件事:生成卦象、查询卦辞、提供API接口。使用Flask是因为它轻量、快速,非常适合这类小型工具。
核心文件结构:
/backend ├── app.py # Flask主应用 ├── gua_generator.py # 起卦算法模块 ├── gua_lookup.py # 卦象查询模块 ├── data/ │ └── gua_data.json # 六十四卦数据库 └── requirements.txt # 依赖列表app.py主要代码片段:
from flask import Flask, jsonify, request from gua_generator import generate_full_guas # 导入封装的起卦函数 from gua_lookup import lookup_gua_by_yao, get_gua_detail import json app = Flask(__name__) @app.route('/api/generate', methods=['GET']) def api_generate(): """生成卦象的API端点""" try: # 调用核心算法,得到本卦、变卦、动爻信息 original_gua, changed_gua, changing_positions = generate_full_guas() # 查询本卦和变卦的详细信息 original_gua_detail = lookup_gua_by_yao(original_gua) changed_gua_detail = lookup_gua_by_yao(changed_gua) # 获取动爻的爻辞(只取本卦中动爻的爻辞) changing_yao_details = [] for pos in changing_positions: # pos是1-based的爻位 yao_info = original_gua_detail['yao'][pos-1] # 获取对应爻辞 changing_yao_details.append({ 'position': pos, 'yao_ci': yao_info['yao_ci'], 'xiang_ci': yao_info['xiang_ci'] }) response = { 'success': True, 'data': { 'original_gua': original_gua_detail, 'changed_gua': changed_gua_detail, 'changing_yao': changing_yao_details, 'changing_positions': changing_positions } } return jsonify(response) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/gua/<int:sequence>', methods=['GET']) def api_get_gua(sequence): """根据卦序查询卦的详细信息""" detail = get_gua_detail(sequence) if detail: return jsonify({'success': True, 'data': detail}) else: return jsonify({'success': False, 'error': '卦未找到'}), 404 if __name__ == '__main__': app.run(debug=True, port=5000)gua_generator.py封装:这个文件整合了第二章节的所有算法函数,提供一个干净的接口generate_full_guas(),一次性返回本卦爻列表、变卦爻列表和动爻位置。
3.2 前端设计:Vue.js 单页应用
前端的目标是提供一个直观、美观的界面,展示卦象、爻变、卦辞和爻辞。我选择了Vue 3,因为它响应式系统能很好地处理卦象状态变化。
核心组件:
- GuaDisplay.vue:负责渲染卦象。将六个爻垂直排列(从初爻到上爻),并用不同的样式或颜色高亮显示动爻(如老阳加红色边框,老阴加蓝色边框)。变卦可以并列显示或通过切换查看。
- InfoPanel.vue:展示卦的详细信息。包括卦名、卦象图、卦辞、彖传、大象传。通过标签页(Tabs)切换显示本卦和变卦的信息。
- YaoDetail.vue:如果存在动爻,这个组件会突出显示所动之爻的爻辞和小象传,这是解卦时重点参考的内容。
- ControlPanel.vue:包含“起卦”按钮。点击后调用后端
/api/generate接口,获取新卦数据并更新整个应用状态。
关键交互逻辑(在Vue的setup中):
import { ref } from 'vue'; import axios from 'axios'; const originalGua = ref(null); const changedGua = ref(null); const changingYao = ref([]); const isLoading = ref(false); const generateGua = async () => { isLoading.value = true; try { const response = await axios.get('http://localhost:5000/api/generate'); if (response.data.success) { const data = response.data.data; originalGua.value = data.original_gua; changedGua.value = data.changed_gua; changingYao.value = data.changing_yao; // 更新UI... } } catch (error) { console.error('起卦失败:', error); // 提示用户 } finally { isLoading.value = false; } };注意事项:前端展示爻象时,字符的兼容性很重要。我使用了
▅▅▅▅▅和▅▅ ▅▅这样的Unicode块字符来模拟阳爻和阴爻,并在动爻后加上O和X标记。虽然不如真正的卦画美观,但在绝大多数终端和浏览器中都能正确显示。如果你想追求更完美的显示,可以考虑使用SVG绘制,或者引入专门的易经字体。
4. 数据持久化与高级功能探讨
基础功能实现后,可以考虑增加一些提升用户体验和项目深度的功能。
4.1 起卦记录与复盘
很多使用者希望回顾之前的卦象。我们可以增加简单的本地存储功能。
- 前端:使用
localStorage或IndexedDB存储每次起卦的结果(时间戳、卦象数据、用户输入的简要问题)。 - 数据结构:
const record = { id: Date.now(), timestamp: new Date().toISOString(), question: userQuestion, // 用户输入的问题 originalGua: originalGua.value, changedGua: changedGua.value, changingYao: changingYao.value }; // 存入 localStorage const history = JSON.parse(localStorage.getItem('gua_history') || '[]'); history.unshift(record); // 新的放前面 localStorage.setItem('gua_history', JSON.stringify(history.slice(0, 100))); // 只保留最近100条 - 界面:增加一个“历史”页面,以列表形式展示记录,点击可查看详情。
4.2 手动指定动爻与自定义起卦
为了满足学习或特定场景的需求,可以增加“手动模式”。
- 功能:提供一个交互式的六爻画板,让用户可以点击每个爻来切换阴阳状态(少阳/少阴),并手动标记某个爻为“动爻”(老阳/老阴)。
- 实现:这需要修改后端的
api/generate接口,使其能接收一个代表六个爻状态的数组作为POST参数,然后根据这个固定状态生成卦象和变卦,而不是随机生成。@app.route('/api/generate/custom', methods=['POST']) def api_generate_custom(): data = request.json custom_yao_states = data.get('yao_states') # 例如 ['young_yang', 'old_yin', ...] # 根据自定义状态生成卦...
4.3 卦象解读提示系统(谨慎实现)
这是一个更进阶也更敏感的功能。核心是不提供“算命式”的断言,而是建立一个关键词库或语境提示系统。
- 思路:为每一卦、每一爻的辞句,提取关键意象(如“乾卦”关联“刚健”、“开创”、“领导”;“潜龙勿用”关联“等待时机”、“积蓄力量”)。当用户输入一个简短的问题(如“问事业发展”)时,系统可以高亮显示卦辞爻辞中与“事业”、“发展”、“行动”相关的关键词句。
- 实现:
- 在
gua_data.json中为每条辞句增加一个tags字段,包含一些中性关键词。 - 前端提供一个简单的输入框让用户描述所问之事。
- 后端进行非常基础的文本匹配(或使用更简单的规则),返回匹配到的标签,前端据此进行视觉上的强调。
- 在
- 重要警告:这个功能必须严格设计,只能作为“文本高亮”或“信息归类”工具,绝不能输出任何结论性、预测性的语句。界面应明确标注:“以下内容为古籍原文,解读因人因事而异,仅供参考与思考。”
5. 部署、优化与常见问题
5.1 项目部署指南
想让别人也能用上你的工具,就需要部署。
- 后端部署:推荐使用Vercel(Python Runtime) 或Railway。它们对Flask应用支持友好,有免费额度。关键是修改
app.py最后一行,监听0.0.0.0和PORT环境变量提供的端口。if __name__ == '__main__': port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port) - 前端部署:构建生产版本 (
npm run build),将生成的dist文件夹内的静态文件,部署到Netlify、Vercel (Static)或GitHub Pages。这些平台都提供免费的自动化部署。 - 连接前后端:部署后,前端需要知道后端API的地址。在Vue项目中,可以通过环境变量来配置。
然后在代码中引用:// .env.production VITE_API_BASE_URL=https://your-flask-backend.vercel.appaxios.create({ baseURL: import.meta.env.VITE_API_BASE_URL })。
5.2 性能优化与代码质量
- 卦辞数据库加载:每次请求都读取和解析JSON文件是低效的。应该在服务启动时就将
gua_data.json加载到内存中,作为一个全局字典或缓存对象。import json with open('data/gua_data.json', 'r', encoding='utf-8') as f: GUADATA = json.load(f) # 后续查询都从 GUADATA 这个字典中获取 - 前端懒加载:如果卦辞内容非常长,可以考虑在用户点击查看详情时再动态加载该卦的完整爻辞,而不是一次性全部加载。
- 错误处理与日志:在后端关键函数中添加
try...except,并记录日志,便于排查线上问题。
5.3 常见问题与排查实录
在开发和用户反馈中,我遇到了以下几个典型问题:
生成的卦象总是某几个卦?
- 排查:检查随机数生成函数
generate_yao。最常见的原因是随机数种子被固定,或者硬币正反面的概率模拟不均等(random.choice([2, 3])是等概率的,没问题)。确保在每次起卦时没有重置随机种子。 - 解决:使用
random.SystemRandom()或在生成前引入时间戳等变化量作为种子。
- 排查:检查随机数生成函数
变卦查询结果错误或为空?
- 排查:这是最复杂的逻辑错误。首先,打印出本卦和变卦的爻列表,确认阴阳转换是否正确。其次,检查
lookup_gua_by_yao函数。确保它正确地将爻列表(包含老阴老阳)转换成了用于查询的“成卦”二进制码(老阴作阴,老阳作阳)。 - 调试技巧:写一个单元测试,固定一组爻,手动计算它应该对应的卦,然后看程序输出是否一致。
- 排查:这是最复杂的逻辑错误。首先,打印出本卦和变卦的爻列表,确认阴阳转换是否正确。其次,检查
前端显示乱码或卦画错位?
- 排查:Unicode字符渲染问题。确保HTML文件指定了UTF-8编码 (
<meta charset="UTF-8">)。对于卦画字符,有些字体可能不支持,可以在CSS中指定一个更通用的字体族,如font-family: "SimSun", "NSimSun", serif;(宋体通常支持较好)。
- 排查:Unicode字符渲染问题。确保HTML文件指定了UTF-8编码 (
部署后API请求失败(CORS错误)?
- 现象:前端控制台报错
Access-Control-Allow-Origin。 - 解决:在后端Flask应用中安装并启用CORS支持。
from flask_cors import CORS app = Flask(__name__) CORS(app) # 允许所有来源,生产环境应指定具体前端地址
- 现象:前端控制台报错
用户觉得“不灵”或“不准”?
- 定位:这不是技术问题,而是产品定位问题。
- 应对:在工具醒目位置添加说明,明确告知:“本工具是一个基于随机数生成算法对传统六爻起卦方法的程序化模拟,其结果不具备任何神秘学意义。旨在为传统文化爱好者提供一种便捷的参考和研习方式,请理性看待,切勿沉迷。” 将工具的定位从“占卜”转向“文化学习与模拟”,可以避免很多不必要的争议。
这个项目从构思到实现,再到不断打磨,让我深刻体会到,将一套复杂的传统规则系统进行数字化封装,最大的挑战不是技术,而是对原始规则的精确理解和严谨翻译。每一行代码背后,都需要对古籍原文的反复揣摩。最终产出的不仅是一个工具,更是一个结构化的、可交互的“周易”数据模型。无论你对它的态度是文化研究、编程练习,还是单纯的兴趣使然,这个过程本身,就是一种充满乐趣的探索。
本文还有配套的精品资源,点击获取