Matplotlib绘制游戏内量子态:从薛定谔的猫到玩家决策的叠加态与坍缩可视化

引言:量子力学与游戏设计的奇妙交汇
量子力学作为现代物理学的基石,其核心概念如叠加态、波函数坍缩和观测者效应,不仅深刻改变了我们对物质世界的理解,也为游戏设计提供了极具创意的灵感源泉。在许多游戏中,玩家的决策往往决定了故事的走向和角色的命运,这种”不确定性”和”可能性”与量子态的叠加有着惊人的相似性。本文将探讨如何利用Python的Matplotlib库,将抽象的量子概念——特别是薛定谔的猫这一著名思想实验——与游戏中的玩家决策机制相结合,通过可视化手段展现从叠加态到坍缩的动态过程。
量子态与游戏机制的类比
在量子力学中,一个粒子可以同时处于多个状态的叠加,直到被观测时才会”坍缩”到一个确定的状态。类似地,在游戏中,玩家的决策在做出之前往往处于一种”可能性的叠加”——比如一个角色可能同时是盟友或敌人,一个宝箱可能包含宝藏或陷阱,直到玩家做出选择(相当于”观测”)时,这些可能性才坍缩为确定的结果。
这种类比为游戏设计提供了丰富的可能性:
叙事分支
:故事线在关键决策点前处于叠加态,玩家的选择导致故事坍缩到特定路径
角色属性
:角色的能力或立场在未被揭示前处于叠加态
随机事件
:看似随机的事件背后可能隐藏着量子化的决策树
Matplotlib的优势
Matplotlib作为Python最强大的绘图库之一,具有以下适合本项目的特性:
动画支持
:可以通过
FuncAnimation
类创建平滑的量子态演化动画
丰富的图形元素
:支持自定义形状、颜色和文本,便于绘制量子比特、猫等符号
交互性
:可以结合事件处理实现玩家点击”观测”按钮来触发坍缩
3D绘图能力
:对于更复杂的量子态可视化,可以使用mplot3d
薛定谔的猫:量子叠加的经典思想实验
思想实验背景
1935年,埃尔温·薛定谔提出了这个著名的思想实验,旨在说明量子力学在宏观尺度上的荒谬性。实验设置如下:
一只猫被关在密闭的钢盒内
盒中有一个放射性原子,其衰变概率为50%
原子衰变会触发盖革计数器,进而释放毒气杀死猫
在打开盒子观测之前,根据量子力学,原子处于”已衰变”和”未衰变”的叠加态,因此猫也处于”既死又活”的叠加态
量子力学解释
在量子力学框架下,系统的状态由波函数描述:
|ψ⟩ = α|活⟩ + β|死⟩
其中α和β是复数概率幅,满足|α|² + |β|² = 1。观测时,系统会以|α|²的概率坍缩到|活⟩态,以|β|²的概率坍缩到|死⟩态。
游戏化改编思路
我们可以将这个思想实验改编为一个简单的游戏机制:
玩家面对一个神秘的箱子,可以选择”打开”(观测)或保持”不打开”
箱子内部状态在打开前处于叠加态(猫既死又活)
打开箱子时,系统随机决定猫的最终状态(基于概率幅)
玩家的决策影响后续剧情发展
使用Matplotlib实现量子态可视化
基础环境设置
首先,确保安装了必要的库:
pip install matplotlib numpy
创建量子态可视化类
下面是一个完整的实现,包含量子态的动画展示和交互式坍缩:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.patches import Circle, Rectangle
import matplotlib.patheffects as patheffects
from matplotlib.widgets import Button
import random

class QuantumCatGame:
def __init__(self):
“””初始化量子猫游戏可视化”””
self.fig, self.ax = plt.subplots(figsize=(12, 8))
self.ax.set_xlim(0, 10)
self.ax.set_ylim(0, 10)
self.ax.set_aspect(‘equal’)
self.ax.axis(‘off’)

# 量子态参数
self.alpha = np.sqrt(0.5) # 活猫的概率幅
self.beta = np.sqrt(0.5) # 死猫的概率幅
self.state = “superposition” # 当前状态: superposition, alive, dead
self.observation_count = 0 # 观测次数

# 绘制场景元素
self.draw_scene()

# 状态显示文本
self.status_text = self.ax.text(5, 9, ”,
ha=’center’, va=’center’,
fontsize=16, fontweight=’bold’)

# 概率显示
self.prob_text = self.ax.text(5, 8.2, ”,
ha=’center’, va=’center’,
fontsize=12)

# 按钮
self.ax_observe = plt.axes([0.7, 0.05, 0.2, 0.08])
self.btn_observe = Button(self.ax_observe, ‘观测 (打开盒子)’, color=’lightgoldenrodyellow’)
self.btn_observe.on_clicked(self.observe)

self.ax_reset = plt.axes([0.1, 0.05, 0.2, 0.08])
self.btn_reset = Button(self.ax_reset, ‘重置’, color=’lightcoral’)
self.btn_reset.on_clicked(self.reset)

# 动画相关
self.animation = None
self.superposition_phase = 0

# 更新初始显示
self.update_display()

def draw_scene(self):
“””绘制游戏场景”””
# 绘制盒子(关闭状态)
self.box = Rectangle((3, 2), 4, 4,
fill=True, facecolor=’darkgoldenrod’,
edgecolor=’black’, linewidth=2)
self.ax.add_patch(self.box)

# 盒子盖子
self.lid = Rectangle((3, 5.8), 4, 0.2,
fill=True, facecolor=’goldenrod’,
edgecolor=’black’, linewidth=1)
self.ax.add_patch(self.lid)

# 盒子上的问号(表示叠加态)
self.question_mark = self.ax.text(5, 4, ‘?’,
ha=’center’, va=’center’,
fontsize=40, fontweight=’bold’,
color=’white’)

# 猫的轮廓(初始隐藏)
self.cat_alive = None
self.cat_dead = None

# 叠加态波纹效果
self.superposition_ring = Circle((5, 4), 0.1,
fill=False, edgecolor=’cyan’,
linewidth=2, alpha=0.7)
self.ax.add_patch(self.superposition_ring)

def update_display(self):
“””根据当前状态更新显示”””
if self.state == “superposition”:
# 叠加态显示
self.status_text.set_text(‘量子态: 叠加态 (既死又活)’)
self.status_text.set_color(‘cyan’)
self.prob_text.set_text(f’观测次数: {self.observation_count} | 活猫概率: {self.alpha**2:.1%} | 死猫概率: {self.beta**2:.1%}’)

# 显示问号
self.question_mark.set_visible(True)

# 隐藏猫
if self.cat_alive:
self.cat_alive.set_visible(False)
if self.cat_dead:
self.cat_dead.set_visible(False)

# 开始波纹动画
self.start_superposition_animation()

elif self.state == “alive”:
# 活猫状态
self.status_text.set_text(‘观测结果: 活猫!’)
self.status_text.set_color(‘green’)
self.prob_text.set_text(f’观测次数: {self.observation_count} | 概率: {self.alpha**2:.1%}’)

# 隐藏问号
self.question_mark.set_visible(False)

# 显示活猫
self.draw_alive_cat()

# 停止波纹动画
self.stop_superposition_animation()

elif self.state == “dead”:
# 死猫状态
self.status_text.set_text(‘观测结果: 死猫!’)
self.status_text.set_color(‘red’)
self.prob_text.set_text(f’观测次数: {self.observation_count} | 概率: {self.beta**2:.1%}’)

# 隐藏问号
self.question_mark.set_visible(False)

# 显示死猫
self.draw_dead_cat()

# 停止波纹动画
self.stop_superposition_animation()

self.fig.canvas.draw_idle()

def draw_alive_cat(self):
“””绘制活猫”””
if self.cat_alive is None:
# 猫头
head = Circle((5, 4), 0.4, fill=True, facecolor=’lightgray’, edgecolor=’black’)
self.ax.add_patch(head)

# 眼睛
eye_left = Circle((4.85, 4.15), 0.05, fill=True, facecolor=’green’)
eye_right = Circle((5.15, 4.15), 0.05, fill=True, facecolor=’green’)
self.ax.add_patch(eye_left)
self.ax.add_patch(eye_right)

# 耳朵
ear_left = Polygon([[4.7, 4.5], [4.6, 4.9], [4.8, 4.7]],
fill=True, facecolor=’lightgray’, edgecolor=’black’)
ear_right = Polygon([[5.3, 4.5], [5.2, 4.9], [5.4, 4.7]],
fill=True, facecolor=’lightgray’, edgecolor=’black’)
self.ax.add_patch(ear_left)
self.ax.add_patch(ear_right)

# 胡须
self.ax.plot([4.6, 4.4], [4.0, 4.0], ‘k-‘, linewidth=1)
self.ax.plot([4.6, 4.4], [4.1, 4.2], ‘k-‘, linewidth=1)
self.ax.plot([5.4, 5.6], [4.0, 4.0], ‘k-‘, linewidth=1)
self.ax.plot([5.4, 5.6], [4.1, 4.2], ‘k-‘, linewidth=1)

# 尾巴(动态)
self.alive_tail = self.ax.plot([], [], ‘k-‘, linewidth=3)[0]

self.cat_alive = [head, eye_left, eye_right, ear_left, ear_right, self.alive_tail]
else:
# 更新尾巴动画
t = np.linspace(0, 2*np.pi, 20)
x = 5 + 0.8 * np.cos(t + self.superposition_phase)
y = 2.5 + 0.3 * np.sin(2*t + self.superposition_phase)
self.alive_tail.set_data(x, y)

for part in self.cat_alive:
part.set_visible(True)

def draw_dead_cat(self):
“””绘制死猫”””
if self.cat_dead is None:
# 猫头(灰色)
head = Circle((5, 4), 0.4, fill=True, facecolor=’dimgray’, edgecolor=’black’)
self.ax.add_patch(head)

# X眼睛
self.ax.plot([4.8, 4.9], [4.1, 4.2], ‘k-‘, linewidth=2)
self.ax.plot([4.8, 4.9], [4.2, 4.1], ‘k-‘, linewidth=2)
self.ax.plot([5.1, 5.2], [4.1, 4.2], ‘k-‘, linewidth=2)
self.ax.plot([5.1, 5.2], [4.2, 4.1], ‘k-‘, linewidth=2)

# 倒下的耳朵
ear_left = Polygon([[4.7, 4.5], [4.6, 4.3], [4.8, 4.4]],
fill=True, facecolor=’dimgray’, edgecolor=’black’)
ear_right = Polygon([[5.3, 4.5], [5.2, 4.3], [5.4, 4.4]],
fill=True, facecolor=’dimgray’, edgecolor=’black’)
self.ax.add_patch(ear_left)
self.ax.add_patch(ear_right)

# 舌头(吐出)
tongue = Rectangle((4.9, 3.7), 0.2, 0.15,
fill=True, facecolor=’red’, edgecolor=’black’)
self.ax.add_patch(tongue)

self.cat_dead = [head, ear_left, ear_right, tongue]
else:
for part in self.cat_dead:
part.set_visible(True)

def start_superposition_animation(self):
“””开始叠加态动画”””
if self.animation is None:
self.animation = FuncAnimation(
self.fig, self.animate_superposition,
frames=np.arange(0, 100),
interval=50,
blit=False,
repeat=True
)

def stop_superposition_animation(self):
“””停止叠加态动画”””
if self.animation is not None:
self.animation.event_source.stop()
self.animation = None

def animate_superposition(self, frame):
“””叠加态动画帧更新”””
self.superposition_phase += 0.2

# 更新波纹圆圈
radius = 0.5 + 0.3 * np.sin(self.superposition_phase)
self.superposition_ring.set_radius(radius)
self.superposition_ring.set_alpha(0.3 + 0.2 * np.sin(self.superposition_phase))

# 更新问号位置(轻微浮动)
self.question_mark.set_position((5, 4 + 0.1 * np.sin(2*self.superposition_phase)))

# 如果有活猫,更新尾巴
if self.state == “alive” and self.cat_alive:
self.draw_alive_cat()

return [self.superposition_ring, self.question_mark]

def observe(self, event):
“””观测事件处理”””
if self.state != “superposition”:
# 已经观测过,需要重置
self.status_text.set_text(‘请先重置!’)
self.status_text.set_color(‘orange’)
self.fig.canvas.draw_idle()
return

self.observation_count += 1

# 模拟量子坍缩
# 根据概率幅决定结果
if random.random() < self.alpha**2: self.state = "alive" else: self.state = "dead" self.update_display() def reset(self, event): """重置游戏""" self.state = "superposition" self.update_display() # 重置概率幅(可选:每次重置后概率可以随机变化,模拟不同的量子系统) # self.alpha = np.sqrt(random.random()) # self.beta = np.sqrt(1 - self.alpha**2) # 运行游戏 if __name__ == "__main__": game = QuantumCatGame() plt.tight_layout() plt.show() 代码详解 1. 类结构与初始化 QuantumCatGame 类封装了整个可视化系统: 图形设置 :创建12x8英寸的画布,关闭坐标轴,设置坐标范围 量子参数 :α和β表示概率幅,满足|α|² + |β|² = 1 状态管理 :跟踪当前是叠加态、活猫还是死猫 UI组件 :包含观测和重置按钮 2. 场景绘制 draw_scene() 方法绘制基础场景: 盒子 :使用Rectangle绘制一个棕色矩形代表盒子 问号 :在盒子中心显示白色问号,表示未知状态 波纹圆圈 :cyan色的空心圆,用于叠加态动画 3. 状态更新与显示 update_display() 方法是核心逻辑: 叠加态 :显示青色文本,显示问号,隐藏猫,启动波纹动画 活猫 :显示绿色文本,绘制活猫(带摇摆尾巴),停止动画 死猫 :显示红色文本,绘制死猫(X眼睛、吐舌头),停止动画 4. 动画系统 animate_superposition() 方法实现平滑的叠加态视觉效果: 波纹动画 :圆圈半径和透明度正弦波动 问号浮动 :轻微上下浮动,模拟不确定性 尾巴摇摆 :活猫尾巴的正弦曲线运动 5. 交互逻辑 observe() 方法处理观测事件: 检查当前状态,防止重复观测 使用 random.random() 与概率幅平方比较,模拟量子坍缩 更新状态并触发显示更新 运行结果分析 运行上述代码后,你将看到一个交互式界面: 初始状态 :盒子显示问号和波纹动画,状态文本为”量子态: 叠加态” 点击观测 :系统根据概率随机决定结果,动画停止,显示猫的状态 多次观测 :统计观测次数,展示概率分布 重置 :回到叠加态,可以重新开始 这个实现完美展示了量子叠加到坍缩的完整过程,同时保持了游戏的趣味性。 扩展:玩家决策叠加态可视化 游戏场景设计 现在我们扩展概念,将其应用于更复杂的游戏决策场景。假设一个RPG游戏中,玩家面临一个道德选择: 选择A :拯救村民(正义路线) 选择B :夺取宝藏(邪恶路线) 在做出选择前,玩家的”道德状态”处于叠加态,同时包含正义和邪恶的可能性。 实现决策叠加态可视化 import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import RadioButtons, Button from matplotlib.patches import FancyBboxPatch import matplotlib.patheffects as patheffects class DecisionQuantumGame: def __init__(self): """初始化决策量子游戏""" self.fig = plt.figure(figsize=(14, 8)) self.ax_main = plt.subplot2grid((3, 2), (0, 0), rowspan=2) self.ax_controls = plt.subplot2grid((3, 2), (0, 1), rowspan=2) self.ax_info = plt.subplot2grid((3, 2), (2, 0), colspan=2) # 游戏状态 self.player_state = "superposition" # superposition, justice, evil self.choices = ["拯救村民", "夺取宝藏"] self.probabilities = [0.5, 0.5] # 初始概率 self.observation_count = 0 # 绘制场景 self.draw_decision_scene() self.setup_controls() self.setup_info_panel() def draw_decision_scene(self): """绘制决策场景""" self.ax_main.set_xlim(0, 10) self.ax_main.set_ylim(0, 10) self.ax_main.set_aspect('equal') self.ax_main.axis('off') # 绘制角色(处于叠加态) self.draw_player_superposition() # 绘制选择路径 self.draw_paths() # 状态显示 self.state_text = self.ax_main.text(5, 9, '玩家道德态: 叠加态', ha='center', va='center', fontsize=16, fontweight='bold', color='purple') # 概率条 self.prob_bars = None self.update_probability_bars() def draw_player_superposition(self): """绘制处于叠加态的玩家""" # 玩家主体(半透明,表示叠加) player_body = FancyBboxPatch((4, 3), 2, 3, boxstyle="round,pad=0.1", fill=True, facecolor='purple', alpha=0.3, edgecolor='black', linewidth=2) self.ax_main.add_patch(player_body) # 叠加符号 self.ax_main.text(5, 4.5, '⊕', ha='center', va='center', fontsize=40, fontweight='bold', color='white') # 两个状态的幽灵形象 # 正义幽灵(绿色) justice_ghost = FancyBboxPatch((3.5, 3.5), 1, 2, boxstyle="round,pad=0.1", fill=True, facecolor='green', alpha=0.3) self.ax_main.add_patch(justice_ghost) self.ax_main.text(4, 4.5, '⚖', ha='center', va='center', fontsize=20, color='darkgreen') # 邪恶幽灵(红色) evil_ghost = FancyBboxPatch((5.5, 3.5), 1, 2, boxstyle="round,pad=0.1", fill=True, facecolor='red', alpha=0.3) self.ax_main.add_patch(evil_ghost) self.ax_main.text(6, 4.5, '⚔', ha='center', va='center', fontsize=20, color='darkred') # 波纹效果 self.decision_ripple = self.ax_main.plot([], [], 'm-', linewidth=3)[0] def draw_paths(self): """绘制选择路径""" # 左路径(正义) self.ax_main.plot([2, 4], [1, 3], 'g--', linewidth=2, alpha=0.7) self.ax_main.text(1.5, 1.5, '拯救村民\n(正义)', ha='center', va='center', fontsize=12, color='green', fontweight='bold') # 右路径(邪恶) self.ax_main.plot([8, 6], [1, 3], 'r--', linewidth=2, alpha=0.7) self.ax_main.text(8.5, 1.5, '夺取宝藏\n(邪恶)', ha='center', va='center', fontsize=12, color='red', fontweight='bold') # 起点标记 self.ax_main.text(5, 1, '决策点', ha='center', va='center', fontsize=10, style='italic') def setup_controls(self): """设置控制面板""" self.ax_controls.set_title('决策控制', fontsize=14, fontweight='bold') self.ax_controls.axis('off') # 概率调整滑块 self.ax_prob_slider = plt.axes([0.65, 0.65, 0.25, 0.03]) self.prob_slider = Button(self.ax_prob_slider, '调整正义概率', color='lightgreen') self.prob_slider.on_clicked(self.adjust_probability) # 观测按钮 self.ax_observe = plt.axes([0.65, 0.55, 0.25, 0.08]) self.btn_observe = Button(self.ax_observe, '做出选择', color='gold') self.btn_observe.on_clicked(self.make_decision) # 重置按钮 self.ax_reset = plt.axes([0.65, 0.45, 0.25, 0.08]) self.btn_reset = Button(self.ax_reset, '重置决策', color='lightcoral') self.btn_reset.on_clicked(self.reset_decision) # 概率显示 self.ax_controls.text(0.05, 0.8, '当前概率分布:', fontsize=12, transform=self.ax_controls.transAxes) self.prob_display = self.ax_controls.text(0.05, 0.7, '', fontsize=11, transform=self.ax_controls.transAxes) # 指导文本 guide_text = """ 游戏说明: 1. 玩家处于道德叠加态 2. 调整概率影响决策倾向 3. 点击"做出选择"触发坍缩 4. 观察概率分布变化 """ self.ax_controls.text(0.05, 0.2, guide_text, fontsize=9, transform=self.ax_controls.transAxes, verticalalignment='top', bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5)) def setup_info_panel(self): """设置信息面板""" self.ax_info.set_xlim(0, 10) self.ax_info.set_ylim(0, 6) self.ax_info.axis('off') self.info_text = self.ax_info.text(0.5, 5, '', ha='center', va='center', fontsize=11, transform=self.ax_info.transAxes) # 历史记录 self.history_text = self.ax_info.text(0.05, 0.1, '决策历史:', transform=self.ax_info.transAxes, fontsize=10, verticalalignment='top') self.decision_history = [] def update_probability_bars(self): """更新概率条显示""" if self.prob_bars is not None: for bar in self.prob_bars: bar.remove() # 在控制面板绘制概率条 positions = [0.05, 0.45] colors = ['green', 'red'] labels = ['正义', '邪恶'] self.prob_bars = [] for i, (pos, prob, color, label) in enumerate(zip(positions, self.probabilities, colors, labels)): # 背景条 bg = FancyBboxPatch((pos, 0.85), 0.35, 0.1, boxstyle="round,pad=0.02", fill=True, facecolor='lightgray', transform=self.ax_controls.transAxes) self.ax_controls.add_patch(bg) # 概率条 bar = FancyBboxPatch((pos, 0.85), 0.35 * prob, 0.1, boxstyle="round,pad=0.02", fill=True, facecolor=color, alpha=0.7, transform=self.ax_controls.transAxes) self.ax_controls.add_patch(bar) # 文本 text = self.ax_controls.text(pos + 0.175, 0.9, f'{label}: {prob:.1%}', ha='center', va='center', fontsize=9, transform=self.ax_controls.transAxes) self.prob_bars.extend([bg, bar, text]) # 更新文本显示 self.prob_display.set_text(f'正义: {self.probabilities[0]:.1%} | 邪恶: {self.probabilities[1]:.1%}') self.fig.canvas.draw_idle() def adjust_probability(self, event): """调整概率(模拟量子测量影响)""" # 随机调整概率,模拟量子不确定性 change = np.random.normal(0, 0.1) self.probabilities[0] = np.clip(self.probabilities[0] + change, 0.1, 0.9) self.probabilities[1] = 1 - self.probabilities[0] self.update_probability_bars() # 添加动画效果 self.animate_ripple() def make_decision(self, event): """做出决策(观测导致坍缩)""" if self.player_state != "superposition": self.info_text.set_text('请先重置!', color='orange') return self.observation_count += 1 # 根据概率决定结果 if random.random() < self.probabilities[0]: self.player_state = "justice" result = "正义" color = "green" outcome = "你选择了拯救村民,获得了村民的感激和道德的满足。" else: self.player_state = "evil" result = "邪恶" color = "red" outcome = "你选择了夺取宝藏,获得了财富但背负了罪恶。" # 更新显示 self.state_text.set_text(f'玩家道德态: {result} ({self.observation_count}次)', color=color) # 绘制坍缩后的玩家 self.draw_collapsed_player(result, color) # 更新历史 self.decision_history.append(f"{self.observation_count}. {result}") history_str = "决策历史:\n" + "\n".join(self.decision_history[-5:]) # 显示最近5次 self.history_text.set_text(history_str) # 显示结果 self.info_text.set_text(outcome, color=color) # 播放坍缩动画 self.animate_collapse() self.fig.canvas.draw_idle() def draw_collapsed_player(self, result, color): """绘制坍缩后的玩家""" # 清除旧的叠加态图形 for patch in self.ax_main.patches: if patch.get_alpha() == 0.3: # 叠加态的半透明图形 patch.remove() # 清除旧文本 for text in self.ax_main.texts: if text.get_text() in ['⊕', '⚖', '⚔']: text.remove() # 绘制新状态 if result == "正义": # 绘制正义角色 player = FancyBboxPatch((4, 3), 2, 3, boxstyle="round,pad=0.1", fill=True, facecolor=color, alpha=0.8) self.ax_main.add_patch(player) self.ax_main.text(5, 4.5, '⚖', ha='center', va='center', fontsize=40, fontweight='bold', color='white') # 添加光环 circle = Circle((5, 4.5), 1.5, fill=False, edgecolor=color, linewidth=3, alpha=0.6) self.ax_main.add_patch(circle) else: # 绘制邪恶角色 player = FancyBboxPatch((4, 3), 2, 3, boxstyle="round,pad=0.1", fill=True, facecolor=color, alpha=0.8) self.ax_main.add_patch(player) self.ax_main.text(5, 4.5, '⚔', ha='center', va='center', fontsize=40, fontweight='bold', color='white') # 添加闪电效果 self.ax_main.plot([4.5, 5, 5.5], [5, 4, 5.2], 'w-', linewidth=4) self.ax_main.plot([4.5, 5, 5.5], [5.2, 4.2, 5], 'w-', linewidth=4) def animate_ripple(self): """概率调整时的波纹动画""" t = np.linspace(0, 2*np.pi, 50) x = 5 + 0.5 * np.cos(t) y = 4.5 + 0.5 * np.sin(t) self.decision_ripple.set_data(x, y) # 使用一次性动画 def update_ripple(frame): alpha = 0.7 * (1 - frame/20) self.decision_ripple.set_alpha(alpha) if frame == 20: self.decision_ripple.set_data([], []) return self.decision_ripple, anim = FuncAnimation(self.fig, update_ripple, frames=21, interval=50, blit=False) self.fig.canvas.draw_idle() def animate_collapse(self): """坍缩动画""" # 简单的缩放动画 def update_collapse(frame): scale = 1 + 0.2 * np.sin(frame * 0.5) # 这里可以添加更复杂的变换 return [], anim = FuncAnimation(self.fig, update_collapse, frames=20, interval=50, blit=False) self.fig.canvas.draw_idle() def reset_decision(self, event): """重置决策""" self.player_state = "superposition" self.state_text.set_text('玩家道德态: 叠加态', color='purple') self.info_text.set_text('', color='black') # 重新绘制叠加态 for text in self.ax_main.texts: if text.get_text() in ['⚖', '⚔']: text.remove() # 清除坍缩后的图形 for patch in self.ax_main.patches: if patch.get_alpha() == 0.8: # 坍缩后的图形 patch.remove() # 重新绘制叠加态 self.draw_player_superposition() self.fig.canvas.draw_idle() # 运行游戏 if __name__ == "__main__": game = DecisionQuantumGame() plt.tight_layout() plt.show() 代码详解 1. 复杂UI布局 使用 subplot2grid 创建多面板界面: 主面板 :显示玩家状态和路径 控制面板 :概率调整、决策按钮 信息面板 :结果反馈和历史记录 2. 概率系统 动态概率 :玩家可以调整正义/邪恶概率,模拟量子测量对系统的影响 概率条可视化 :实时显示概率分布,使用颜色编码(绿色=正义,红色=邪恶) 3. 状态管理 叠加态 :半透明的紫色主体 + 幽灵形象 + 叠加符号 坍缩态 :实心角色 + 状态符号 + 特殊效果(光环/闪电) 4. 动画系统 波纹动画 :概率调整时触发,增强反馈 坍缩动画 :决策时的缩放效果,增强仪式感 5. 历史记录 跟踪多次决策,展示概率分布的长期趋势,帮助玩家理解量子概率的本质。 高级主题:多量子比特与决策树 概念扩展 在更复杂的游戏中,可以使用多个量子比特来表示复杂的决策树。例如: 3个量子比特 :8种可能结局的叠加 纠缠态 :两个角色的命运相互关联 干涉 :不同路径的概率幅相加或相消 实现多量子比特可视化 import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import CheckButtons, Button import itertools class MultiQubitGame: def __init__(self, n_qubits=3): """初始化多量子比特游戏""" self.n_qubits = n_qubits self.fig, self.ax = plt.subplots(figsize=(14, 8)) self.ax.set_xlim(0, 12) self.ax.set_ylim(0, 10) self.ax.axis('off') # 量子状态:2^n个基态的叠加 self.n_states = 2**n_qubits self.amplitudes = np.ones(self.n_states) / np.sqrt(self.n_states) # 均匀叠加 self.state = "superposition" # superposition, collapsed # 绘制量子比特 self.qubits = [] self.draw_qubits() # 绘制状态空间 self.draw_state_space() # 控制面板 self.setup_controls() # 状态显示 self.status_text = self.ax.text(6, 9, '', ha='center', va='center', fontsize=16, fontweight='bold') self.update_display() def draw_qubits(self): """绘制量子比特""" y_pos = 7 for i in range(self.n_qubits): x_pos = 2 + i * 3 # 量子比特圆圈 circle = Circle((x_pos, y_pos), 0.4, fill=True, facecolor='lightblue', edgecolor='black', linewidth=2) self.ax.add_patch(circle) # 状态文本 text = self.ax.text(x_pos, y_pos, '|0⟩', ha='center', va='center', fontsize=14, fontweight='bold') # 标签 self.ax.text(x_pos, y_pos+0.6, f'Q{i}', ha='center', va='center', fontsize=10, style='italic') self.qubits.append({'circle': circle, 'text': text, 'state': 0}) def draw_state_space(self): """绘制状态空间(概率分布)""" # 清除旧的 if hasattr(self, 'prob_bars'): for bar in self.prob_bars: bar.remove() # 绘制概率条 self.prob_bars = [] bar_width = 0.8 spacing = 1.0 start_x = 0.5 for i in range(self.n_states): x = start_x + i * spacing prob = np.abs(self.amplitudes[i])**2 # 概率条 bar = Rectangle((x, 0.5), bar_width, prob * 4, fill=True, facecolor='purple', alpha=0.6) self.ax.add_patch(bar) self.prob_bars.append(bar) # 状态标签(二进制) binary = format(i, f'0{self.n_qubits}b') self.ax.text(x + bar_width/2, 0.3, binary, ha='center', va='center', fontsize=9, rotation=45) # 概率值 if prob > 0.05:
self.ax.text(x + bar_width/2, 0.5 + prob * 4 + 0.1, f'{prob:.1%}’,
ha=’center’, va=’center’, fontsize=8)

# 标签
self.ax.text(6, 0, ‘状态空间 (所有可能结果的叠加)’, ha=’center’, va=’center’,
fontsize=12, fontweight=’bold’)

def setup_controls(self):
“””设置控制面板”””
# 观测按钮
self.ax_observe = plt.axes([0.75, 0.7, 0.2, 0.08])
self.btn_observe = Button(self.ax_observe, ‘测量所有量子比特’, color=’gold’)
self.btn_observe.on_clicked(self.measure_all)

# 重置按钮
self.ax_reset = plt.axes([0.75, 0.6, 0.2, 0.08])
self.btn_reset = Button(self.ax_reset, ‘重置’, color=’lightcoral’)
self.btn_reset.on_clicked(self.reset)

# 随机旋转按钮
self.ax_rotate = plt.axes([0.75, 0.5, 0.2, 0.08])
self.btn_rotate = Button(self.ax_rotate, ‘随机旋转’, color=’lightgreen’)
self.btn_rotate.on_clicked(self.random_rotation)

# 信息文本
info = “””
多量子比特系统:
– 3个量子比特 = 8种可能结局
– 均匀叠加: 所有结局概率相等
– 测量: 坍缩到一个确定状态
– 随机旋转: 改变叠加系数
“””
self.ax.text(0.75, 0.4, info, fontsize=9, transform=self.ax.transAxes,
verticalalignment=’top’, bbox=dict(boxstyle=”round”, facecolor=”wheat”, alpha=0.5))

def update_display(self):
“””更新显示”””
if self.state == “superposition”:
self.status_text.set_text(‘系统状态: 量子叠加态’, color=’cyan’)

# 更新量子比特显示
for q in self.qubits:
q[‘text’].set_text(‘|ψ⟩’)
q[‘circle’].set_facecolor(‘lightblue’)

else: # collapsed
# 显示测量结果
result = self.get_collapsed_state()
binary = format(result, f’0{self.n_qubits}b’)
self.status_text.set_text(f’测量结果: {binary} (十进制: {result})’, color=’green’)

# 高亮对应的概率条
for i, bar in enumerate(self.prob_bars):
if i == result:
bar.set_alpha(1.0)
bar.set_facecolor(‘green’)
else:
bar.set_alpha(0.2)
bar.set_facecolor(‘gray’)

# 更新量子比特显示
for i, q in enumerate(self.qubits):
bit = (result >> i) & 1
q[‘text’].set_text(f’|{bit}⟩’)
q[‘circle’].set_facecolor(‘green’ if bit else ‘red’)

self.fig.canvas.draw_idle()

def get_collapsed_state(self):
“””获取坍缩后的状态(基于概率)”””
probs = np.abs(self.amplitudes)**2
return np.random.choice(self.n_states, p=probs)

def measure_all(self, event):
“””测量所有量子比特”””
if self.state == “collapsed”:
self.status_text.set_text(‘已测量,请先重置!’, color=’orange’)
return

self.state = “collapsed”
self.update_display()

# 添加测量动画
self.animate_measurement()

def random_rotation(self, event):
“””随机旋转量子态(改变叠加系数)”””
if self.state == “collapsed”:
self.status_text.set_text(‘测量后无法旋转,请先重置!’, color=’orange’)
return

# 生成随机酉矩阵(简化版:随机相位)
random_phases = np.exp(1j * np.random.uniform(0, 2*np.pi, self.n_states))
self.amplitudes = self.amplitudes * random_phases

# 重新归一化
norm = np.sqrt(np.sum(np.abs(self.amplitudes)**2))
self.amplitudes /= norm

# 更新状态空间显示
self.draw_state_space()
self.status_text.set_text(‘量子态已旋转’, color=’purple’)
self.fig.canvas.draw_idle()

def reset(self, event):
“””重置系统”””
self.state = “superposition”
self.amplitudes = np.ones(self.n_states) / np.sqrt(self.n_states)
self.draw_state_space()
self.update_display()

def animate_measurement(self):
“””测量动画”””
# 简单的闪烁效果
def update(frame):
if frame < 10: alpha = 0.2 + 0.1 * (frame % 2) for bar in self.prob_bars: bar.set_alpha(alpha) else: for bar in self.prob_bars: bar.set_alpha(0.6) return self.prob_bars, anim = FuncAnimation(self.fig, update, frames=15, interval=50, blit=False) self.fig.canvas.draw_idle() # 运行游戏 if __name__ == "__main__": game = MultiQubitGame(n_qubits=3) plt.tight_layout() plt.show() 代码详解 1. 多量子比特系统 状态空间 :2^n个基态的叠加,每个基态对应一个可能的结果 均匀叠加 :初始时所有结果概率相等(1/2^n) 概率幅 :复数系数,包含相位信息 2. 随机旋转 酉变换 :通过随机相位改变叠加系数,模拟量子门操作 可视化 :实时更新概率分布条形图 3. 测量与坍缩 概率采样 :根据概率幅的平方进行随机抽样 结果显示 :二进制和十进制同时显示,便于理解 视觉反馈 :高亮坍缩结果,淡化其他可能性 游戏设计应用与最佳实践 1. 叙事设计中的量子态 动态故事线 # 伪代码:量子化的故事节点 class QuantumStoryNode: def __init__(self, name, outcomes): self.name = name self.outcomes = outcomes # {outcome: probability_amplitude} self.state = "superposition" def observe(self, player_choice): # 玩家选择作为观测行为 if self.state == "superposition": # 根据玩家选择和隐藏概率决定结果 final_outcome = self.collapse(player_choice) self.state = "collapsed" return final_outcome 示例:道德困境 叠加态 :NPC同时是盟友和敌人 观测 :玩家的对话选择导致NPC立场坍缩 结果 :不同的后续剧情和奖励 2. 战斗系统的量子化 不确定的攻击结果 # 攻击伤害的量子叠加 attack_damage = { "critical": 2.0, # 暴击伤害 "normal": 1.0, # 正常伤害 "miss": 0.0 # 未命中 } # 在攻击前,所有结果叠加 # 攻击动画展示叠加态 # 命中时坍缩为具体结果 3. 环境互动的量子态 未打开的宝箱 叠加态 :宝箱内物品处于”有/无”或”好/坏”的叠加 观测 :玩家打开宝箱的动作 坍缩 :随机决定内容,但可受玩家属性影响(调整概率幅) 4. 多人游戏中的量子纠缠 玩家命运纠缠 # 玩家A和玩家B的命运纠缠 # 玩家A的选择会影响玩家B的状态 # 即使分离,测量一个会立即确定另一个 性能优化与高级技巧 1. 动画性能优化 # 使用blitting技术提高动画性能 def animate_optimized(self, frame): # 只更新变化的部分 self.ring.set_radius(0.5 + 0.3 * np.sin(frame * 0.1)) self.ring.set_alpha(0.5 + 0.3 * np.cos(frame * 0.1)) # 返回需要重绘的对象 return self.ring, self.status_text 2. 3D量子态可视化 from mpl_toolkits.mplot3d import Axes3D # 对于复杂的量子态,可以使用3D布洛赫球面 def plot_bloch_sphere(self, state_vector): fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # 绘制布洛赫球 u = np.linspace(0, 2 * np.pi, 30) v = np.linspace(0, np.pi, 30) x = np.outer(np.cos(u), np.sin(v)) y = np.outer(np.sin(u), np.sin(v)) z = np.outer(np.ones(np.size(u)), np.cos(v)) ax.plot_surface(x, y, z, color='cyan', alpha=0.1) # 绘制量子态 # ... (根据state_vector计算坐标) 3. 交互式概率调整 # 使用滑块实时调整概率幅 from matplotlib.widgets import Slider ax_slider = plt.axes([0.2, 0.02, 0.6, 0.03]) slider = Slider(ax_slider, '正义概率', 0.0, 1.0, valinit=0.5) def update_slider(val): game.probabilities[0] = val game.probabilities[1] = 1 - val game.update_probability_bars() slider.on_changed(update_slider) 结论 通过Matplotlib将量子力学概念可视化,我们不仅能够直观地展示薛定谔的猫和量子叠加态,还能将这些抽象概念转化为有趣的游戏机制。从简单的单量子比特系统到复杂的多量子比特决策树,这些可视化工具为游戏设计师提供了全新的创意空间。 关键收获: 量子态可视化 :使用动画和交互元素展示叠加到坍缩的过程 游戏机制融合 :将量子不确定性转化为玩家决策的趣味性 多层级实现 :从简单的猫实验到复杂的道德决策系统 性能与美学 :平衡视觉效果与计算效率 未来发展方向: VR/AR集成 :在虚拟现实中体验量子叠加 网络量子游戏 :利用量子通信实现真正的随机性 AI辅助设计 :使用机器学习优化概率分布和叙事分支 量子游戏不仅是科学教育的工具,更是探索人类决策、自由意志和确定性的艺术媒介。通过Matplotlib这样的工具,我们能够将深奥的物理概念转化为人人都能体验的互动艺术。