基于α-β剪枝的含禁手AI五子棋

前言:

        正常的五子棋应当设有禁手规则,否则先手黑棋必赢,基于此点设计出一款包含禁手的AI五子棋项目,该项目代码已在github开源,感兴趣的友友可以自取试玩:ace-trump-tech/AI-Gomoku-with-Prohibition-Moves: 含禁手的AI五子棋 (github.com)

一、五子棋的禁手规则

        禁手规则的设立有其深刻的原因。在五子棋中,黑方作为先手方,具有明显的优势。如果没有禁手规则,黑方可以通过一些特定的战术轻松取得胜利,使得比赛失去悬念和公平性。因此,禁手规则的出现,有效地限制了黑方的先手优势,增加了比赛的策略深度和观赏性。

        该规则主要针对黑方,白方则没有禁手限制。黑方在一子落下时,如果同时形成了两个或两个以上的活三、双四、长连等特定棋形,那么这一步棋就被视为禁手。具体来说,禁手包括以下几种情况:

  1. 三三禁手:黑方一子落下同时形成两个或两个以上的活三。活三指的是两端都没有被封闭,且再走一着可以形成活四的三子连线。如果黑方一步棋形成了两个这样的活三,就构成了三三禁手。

  2. 四四禁手:黑方一子落下同时形成两个或两个以上的四。这里的“四”包括活四(两端都没有被封闭,且任一端落子都可以立即形成连五)和冲四(只有一端没有被封闭,且该端落子可以形成连五)。如果黑方一步棋形成了两个这样的四,就构成了四四禁手。

  3. 长连禁手:黑方一子落下形成超过五子的连续棋形,通常是六子或更多。这种情况下,黑方的这一步棋被视为禁手。

        在实际比赛中,禁手的判定通常由裁判或计算机程序完成。当黑方出现禁手时,白方必须立即指出,否则禁手失效。如果白方没有及时指出,而是继续落子,那么禁手点将不再有效。此外,如果黑方在形成五连的同时也形成了禁手,那么禁手失效,黑方获胜。在实际比赛中,禁手的判定通常由裁判或计算机程序完成。当黑方出现禁手时,白方必须立即指出,否则禁手失效。如果白方没有及时指出,而是继续落子,那么禁手点将不再有效。此外,如果黑方在形成五连的同时也形成了禁手,那么禁手失效,黑方获胜。

二、α-β剪枝算法

        α-β剪枝算法是一种在博弈树搜索中广泛应用的优化算法,它基于极小化极大算法(Minimax算法),通过巧妙地剪去一些不必要的分支来减少搜索空间,从而提高搜索效率。在两人对弈的游戏中,通常有两个玩家:极大方(Max)和极小方(Min)。极大方的目标是最大化自己的得分,而极小方则试图最小化极大方的得分。极小化极大算法的核心思想是递归地评估每个可能的走法,从当前局面开始,极大方会尝试所有可能的走法,计算每种走法的得分,并选择得分最高的走法;极小方则会尝试所有可能的走法,选择得分最低的走法。然而,这种算法的搜索空间非常庞大,尤其是对于复杂的棋类游戏,计算量巨大,因此需要一种更高效的搜索方法,这就是α-β剪枝算法。

        α-β剪枝算法的核心在于引入了两个关键参数:α值和β值。α值是极大方的下界,表示极大方在当前搜索路径中已经找到的最优值;β值是极小方的上界,表示极小方在当前搜索路径中已经找到的最优值。这两个参数在搜索过程中不断更新,通过比较α值和β值来判断是否可以剪掉某些分支。具体来说,如果在搜索过程中,极小方发现某个分支的值小于等于α值,那么这个分支可以被剪掉,因为极大方不会选择这个分支;同样,如果极大方发现某个分支的值大于等于β值,那么这个分支也可以被剪掉,因为极小方不会选择这个分支。这种剪枝机制大大减少了需要搜索的节点数量,从而提高了搜索效率。

        在实现α-β剪枝算法时,搜索过程是从根节点开始的,根节点通常是当前的游戏局面。在搜索开始时,α值被初始化为负无穷大,β值被初始化为正无穷大。然后,算法递归地遍历博弈树的每个节点。对于极大方,它会尝试所有可能的走法,计算每种走法的得分,并更新α值。如果某个分支的得分大于等于β值,那么后续的分支就可以被剪掉。对于极小方,它会尝试所有可能的走法,计算每种走法的得分,并更新β值。如果某个分支的得分小于等于α值,那么后续的分支也可以被剪掉。通过这种方式,算法逐步向上回溯,最终返回根节点的最优值。

        为了更好地理解α-β剪枝算法,可以考虑一个简单的博弈树示例。假设有一个博弈树,极大方和极小方交替进行决策,每个节点的值表示该局面的得分。在搜索过程中,算法会从根节点开始,逐步向下搜索每个分支。当遇到极小方的节点时,算法会计算该节点的子节点的得分,并选择最小值作为该节点的得分;当遇到极大方的节点时,算法会计算该节点的子节点的得分,并选择最大值作为该节点的得分。在这个过程中,如果某个分支的得分已经超出了当前的α或β值范围,那么这个分支就可以被剪掉,从而避免了进一步的搜索。通过这种方式,算法最终能够找到最优的走法,同时大大减少了搜索的计算量。

        α-β剪枝算法的优势在于它能够在不牺牲最优解的前提下,显著减少搜索空间。虽然剪枝减少了需要搜索的节点数量,但算法仍然能够找到最优的决策路径。然而,α-β剪枝算法的性能也受到搜索顺序的影响。如果搜索顺序不合理,可能会导致剪枝效果不佳。例如,如果先搜索到的分支值较差,可能会错过更好的剪枝机会。此外,尽管α-β剪枝算法提高了搜索效率,但对于非常复杂的棋类游戏,搜索空间仍然可能非常庞大,因此可能需要结合其他优化技术,如启发式评估函数,来进一步提高搜索效率。

        在五子棋AI中,α-β剪枝算法可以有效地评估棋局的得分,并选择最优的落子点。通过结合启发式评估函数,如棋型评估、局势评估等,AI可以更快速地找到最佳走法,从而在对弈中表现出色。以下是一个简单的 Python 示例代码,展示了如何实现基于 α-β 剪枝算法的极小化极大搜索。这个示例使用了一个简单的博弈树结构,模拟了一个两人对弈的游戏场景:

# 定义一个简单的博弈树节点类
class Node:def __init__(self, value=None):self.value = value  # 节点的值,表示该局面的得分self.children = []  # 子节点列表def add_child(self, child):self.children.append(child)# α-β剪枝算法的实现
def alpha_beta(node, depth, alpha, beta, is_maximizing):# 如果到达叶子节点或达到搜索深度限制,返回节点的值if depth == 0 or not node.children:return node.valueif is_maximizing:  # 极大方max_val = float('-inf')  # 初始化为负无穷大for child in node.children:val = alpha_beta(child, depth - 1, alpha, beta, False)max_val = max(max_val, val)alpha = max(alpha, max_val)if beta <= alpha:  # 剪枝条件breakreturn max_valelse:  # 极小方min_val = float('inf')  # 初始化为正无穷大for child in node.children:val = alpha_beta(child, depth - 1, alpha, beta, True)min_val = min(min_val, val)beta = min(beta, min_val)if beta <= alpha:  # 剪枝条件breakreturn min_val# 构建一个简单的博弈树
root = Node()
child1 = Node(3)
child2 = Node(5)
child3 = Node(2)
child4 = Node(9)
child5 = Node(1)
child6 = Node(7)root.add_child(child1)
root.add_child(child2)
root.add_child(child3)child1.add_child(child4)
child1.add_child(child5)
child2.add_child(child6)# 调用 α-β 剪枝算法
result = alpha_beta(root, 2, float('-inf'), float('inf'), True)
print("最优值:", result)

        在示例中,使用 Node 类来表示博弈树的节点,每个节点可以有多个子节点,其构建了一个简单的博弈树,包含一个根节点和若干子节点,每个节点的值表示该局面的得分。

三、UI界面设计

1.核心功能设计

在设计基于α-β剪枝的含禁手AI五子棋的用户界面(UI)时,我们需要综合考虑用户体验、功能布局、视觉效果以及交互逻辑等多个方面,以确保游戏界面既美观又实用,能够让玩家轻松上手并享受游戏过程。

首先,界面的整体布局是设计的核心。棋盘作为游戏的核心区域,应该占据界面的中心位置,占据较大的空间比例,以确保玩家能够清晰地看到棋局的每一个细节。棋盘的格子线条应该清晰且均匀,颜色选择上要与棋子形成鲜明对比,以增强视觉效果。例如,可以选择深色的棋盘背景搭配白色和黑色的棋子,这样不仅符合传统的五子棋视觉习惯,还能让棋子在棋盘上更加突出。棋盘的边缘可以添加一些轻微的阴影效果,以增加立体感和深度感,让棋盘看起来更加真实和吸引人。

在棋盘的周围,我们需要合理安排功能按钮区域。这些按钮应该包括模式选择、难度选择、先手选择、悔棋、重置、认输以及显示禁手等功能。为了保持界面的整洁和美观,可以将这些按钮设计成简洁的图标形式,并在鼠标悬停时显示相应的文字提示。例如,悔棋按钮可以设计成一个带有箭头的圆形图标,当鼠标悬停在该按钮上时,显示“悔棋”字样。这样的设计既节省了空间,又方便玩家快速识别和操作。

class Board(QWidget):# ...之前的代码...def is_three_three(self, x, y, player):# 检查是否形成三三禁手directions = [(0, 1), (1, 0), (1, 1), (1, -1)]count = 0for dx, dy in directions:live_three = self.check_direction(x, y, dx, dy, player)if live_three:count += 1if count >= 2:return Truereturn Falsedef check_direction(self, x, y, dx, dy, player):# 检查某个方向是否形成活三count = 0for i in range(1, 3):  # 检查两个方向nx, ny = x + dx * i, y + dy * iif 0 <= nx < self.board_size and 0 <= ny < self.board_size:if self.grid[ny][nx] == player:count += 1else:breakelse:breakfor i in range(1, 3):  # 检查相反方向nx, ny = x - dx * i, y - dy * iif 0 <= nx < self.board_size and 0 <= ny < self.board_size:if self.grid[ny][nx] == player:count += 1else:breakelse:breakreturn count == 2def mousePressEvent(self, event):if event.button() == Qt.LeftButton:x = (event.pos().x() - self.margin) // self.cell_size

2.美观娱乐设置

除了功能按钮,状态栏也是界面设计中不可或缺的一部分。状态栏可以放置在界面的底部或顶部,用于显示当前轮到谁落子、棋局的当前状态(如“黑方胜”“白方胜”“和棋”)以及一些提示信息。状态栏的字体应该清晰易读,颜色与背景形成对比,以确保信息能够被玩家快速捕捉到。例如,当玩家尝试在禁手点落子时,状态栏可以显示一条醒目的提示信息,如“该点为禁手,禁止落子”,同时可以弹出一个提示框,进一步解释禁手规则,帮助玩家更好地理解游戏规则。

在视觉设计方面,我们需要注重颜色搭配和图标设计。颜色是影响用户体验的重要因素之一,因此我们需要选择一组既美观又符合游戏氛围的颜色方案。棋盘的背景色可以选择深棕色或深灰色,给人一种沉稳的感觉;棋子则采用传统的黑白两色,以突出棋子在棋盘上的视觉效果。对于功能按钮和状态栏,可以选择明亮的颜色,如蓝色或绿色,以吸引玩家的注意力。图标设计也应该简洁明了,具有直观性。例如,重置按钮可以设计成一个带有循环箭头的图标,表示重新开始;显示禁手按钮可以设计成一个带有“禁”字的图标,直观地传达其功能。

在交互设计方面,我们需要确保玩家能够通过简单的鼠标操作来完成游戏中的各种操作。玩家可以通过鼠标点击棋盘上的格子来落子,点击功能按钮来执行相应的操作。为了增强用户体验,我们还可以为一些操作添加键盘快捷键。例如,“Ctrl+Z”可以用于悔棋,“Ctrl+R”用于重置棋盘,这样可以让玩家在熟悉操作后更加高效地进行游戏。

此外,我们还可以考虑添加一些额外的功能来丰富游戏体验。例如,棋谱记录功能可以让玩家保存对局记录,方便后续回顾和分析。在人机对战模式下,当AI思考下一步棋时,可以显示一个加载动画,如一个旋转的棋子或一个闪烁的光点,以提示玩家AI正在计算,增加游戏的趣味性和互动性。

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QPushButton, QLabel, QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QPainter, QPen, QBrush, QColor, QFont
from PyQt5.QtCore import Qt, QRectclass GobangUI(QMainWindow):def __init__(self):super().__init__()self.initUI()def initUI(self):self.setWindowTitle('五子棋')self.setGeometry(100, 100, 800, 600)# 主窗口布局main_widget = QWidget(self)self.setCentralWidget(main_widget)main_layout = QVBoxLayout(main_widget)# 棋盘区域self.board = Board(self)main_layout.addWidget(self.board)# 功能按钮区域button_layout = QHBoxLayout()self.mode_button = QPushButton('人机对战', self)self.mode_button.clicked.connect(self.toggle_mode)button_layout.addWidget(self.mode_button)self.difficulty_button = QPushButton('难度:初级', self)self.difficulty_button.clicked.connect(self.change_difficulty)button_layout.addWidget(self.difficulty_button)self.reset_button = QPushButton('重置', self)self.reset_button.clicked.connect(self.reset_game)button_layout.addWidget(self.reset_button)main_layout.addLayout(button_layout)# 状态栏self.status_bar = QLabel(self)self.status_bar.setAlignment(Qt.AlignCenter)self.status_bar.setFont(QFont("Arial", 14))main_layout.addWidget(self.status_bar)self.update_status("黑方回合")def toggle_mode(self):# 切换模式if self.mode_button.text() == "人机对战":self.mode_button.setText("人人对战")else:self.mode_button.setText("人机对战")self.reset_game()def change_difficulty(self):# 切换难度difficulties = ["初级", "中级", "高级"]current_difficulty = self.difficulty_button.text().split(":")[1]next_difficulty = difficulties[(difficulties.index(current_difficulty) + 1) % len(difficulties)]self.difficulty_button.setText(f"难度:{next_difficulty}")def reset_game(self):# 重置游戏self.board.reset()self.update_status("黑方回合")def update_status(self, message):self.status_bar.setText(message)class Board(QWidget):def __init__(self, parent):super().__init__()self.parent = parentself.initBoard()def initBoard(self):self.board_size = 15self.cell_size = 40self.margin = 20self.width = self.board_size * self.cell_size + 2 * self.marginself.height = self.board_size * self.cell_size + 2 * self.marginself.resize(self.width, self.height)self.grid = [[0 for _ in range(self.board_size)] for _ in range(self.board_size)]self.black_turn = Truedef paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)# 绘制棋盘背景painter.setBrush(QBrush(QColor(240, 217, 181)))painter.drawRect(self.rect())# 绘制棋盘格子painter.setPen(QPen(Qt.black, 2))for i in range(self.board_size):painter.drawLine(self.margin, self.margin + i * self.cell_size,self.margin + (self.board_size - 1) * self.cell_size, self.margin + i * self.cell_size)painter.drawLine(self.margin + i * self.cell_size, self.margin,self.margin + i * self.cell_size, self.margin + (self.board_size - 1) * self.cell_size)# 绘制棋子painter.setPen(Qt.black)for i in range(self.board_size):for j in range(self.board_size):if self.grid[i][j] == 1:  # 黑棋painter.setBrush(QBrush(Qt.black))painter.drawEllipse(self.margin + j * self.cell_size - self.cell_size // 2,self.margin + i * self.cell_size - self.cell_size // 2,self.cell_size, self.cell_size)elif self.grid[i][j] == 2:  # 白棋painter.setBrush(QBrush(Qt.white))painter.drawEllipse(self.margin + j * self.cell_size - self.cell_size // 2,self.margin + i * self.cell_size - self.cell_size // 2,self.cell_size, self.cell_size)def mousePressEvent(self, event):if event.button() == Qt.LeftButton:x = (event.pos().x() - self.margin) // self.cell_sizey = (event.pos().y() - self.margin) // self.cell_sizeif 0 <= x < self.board_size and 0 <= y < self.board_size and self.grid[y][x] == 0:self.grid[y][x] = 1 if self.black_turn else 2self.black_turn = not self.black_turnself.update()self.parent.update_status("黑方回合" if self.black_turn else "白方回合")def reset(self):self.grid = [[0 for _ in range(self.board_size)] for _ in range(self.board_size)]self.black_turn = Trueself.update()if __name__ == '__main__':app = QApplication(sys.argv)ex = GobangUI()ex.show()sys.exit(app.exec_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 集成 Redis中@Cacheable 和 @CachePut 的详细对比,涵盖功能、执行流程、适用场景、参数配置及代码示例

以下是 Cacheable 和 CachePut 的详细对比&#xff0c;涵盖功能、执行流程、适用场景、参数配置及代码示例&#xff1a; 1. 核心对比表格 特性CacheableCachePut作用缓存方法的返回结果&#xff0c;避免重复计算执行方法并更新缓存&#xff0c;不覆盖原有缓存执行流程缓存命中…

可以使用费曼学习法阅读重要的书籍

书本上画了很多线&#xff0c;回头看等于没画出任何重点。 不是所有的触动都是有效的。就像你曾经看过很多好文章&#xff0c;当时被触动得一塌糊涂&#xff0c;还把它们放进了收藏夹&#xff0c;但一段时间之后&#xff0c;你就再也记不起来了。如果让你在一本书上画出令自己…

Nginx之https重定向为http

为了将Nginx中443端口的请求重定向到80端口&#xff0c;你可以按照以下步骤进行操作&#xff1a; ‌确认Nginx已经正确安装并运行‌&#xff1a; 确保Nginx服务已经在你的系统上安装并运行。你可以通过运行以下命令来检查Nginx的状态&#xff08;具体命令可能因操作系统而异&a…

【ARTS】【LeetCode-2873】有序三元组中的最大值!

前言 仅做学习使用&#xff0c;侵删 什么是ARTS&#xff1f; 算法(Algorithm): 每周至少一道LeetCode算法题&#xff0c;加强编程训练和算法学习 阅读(Review)&#xff1a; 阅读并点评至少一篇英文技术文章&#xff0c;提高英文水平 技巧 (Tip)&#xff1a;学习至少一个技…

基于spring boot 鲜花销售系统PPT(源码+lw+部署文档+讲解),源码可白嫖!

课题意义 随着网络不断的普及发展&#xff0c;鲜花销售系统依靠网络技术的支持得到了快速的发展&#xff0c;首先要从用户的实际需求出发&#xff0c;通过了解用户的需求开发出具有针对性的信息管理系统&#xff0c;利用目前网络给用户带来的方便快捷这一特点对系统进行调整&am…

Redis常用的数据结构及其使用场景

字符串(String) string 是 redis 最基本的类型&#xff0c;你可以理解成与 Memcached 一模一样的类型&#xff0c;一个 key 对应一个 value。 string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据&#xff0c;比如jpg图片或者序列化的对象。 string 类型是 R…

设计模式简述(五)建造者模式

建造者模式 描述基本要素协调类使用 描述 建造者模式属于创造型设计模式。 通常用于构建一系列复杂对象&#xff0c;这些对象有一定的共性。 我们可以通过不同的建造者&#xff0c;组装不同的对象 与工厂模式的区别&#xff0c;建造者模式更侧重与基于基础构件组装而非直接创…

Java基础 4.6

1.成员方法练习 //编写类A&#xff1a;判断一个数是奇数还是偶数&#xff0c;返回boolean //根据行、列、字符打印对应行数和列数的字符&#xff0c;比如&#xff1a;行4 列4 字符# 则打印相应的效果 public class MethodExercise01 {public static void main(String[] args) …

前端快速入门学习4——CSS盒子模型、浮动、定位

一、盒子模型 所有HTML元素可以看作盒子&#xff0c;在CSS中&#xff0c;"box model"这一术语是用来设计和布局时使用。 CSS盒模型本质上是一个盒子&#xff0c;封装周围的HTML元素&#xff0c;它包括&#xff1a;边距&#xff0c;边框&#xff0c;填充&#xff0c…

瑞数信息发布《BOTS自动化威胁报告》,揭示AI时代网络安全新挑战

近日&#xff0c;瑞数信息正式发布《BOTS自动化威胁报告》&#xff0c;力求通过全景式观察和安全威胁的深度分析&#xff0c;为企业在AI时代下抵御自动化攻击提供安全防护策略&#xff0c;从而降低网络安全事件带来的影响&#xff0c;进一步增强业务韧性和可持续性。 威胁一&am…

Docker设置代理

目录 前言创建代理文件重载守护进程并重启Docker检查代理验证 前言 拉取flowable/flowable-ui失败&#xff0c;用DaoCloud源也没拉下来&#xff0c;不知道是不是没同步。索性想用代理拉镜像。在此记录一下。 创建代理文件 创建docker代理配置 sudo mkdir -p /etc/systemd/s…

Debezium嵌入式连接postgresql封装服务

文章目录 1.项目结构&#xff1a;2.依赖&#xff1a;3.application.properties4.DebeziumConnectorConfig类5.TableEnum类6.TableHandler接口&#xff08;表处理抽象&#xff09;7.DefaultTableHandler默认实现类8.UserTableHandler处理类9.TableHandlerFactory工厂10.Debezium…

ER-图,详情和画法

一、E-R图的核心元素 1.实体 表示现实中对象或概念&#xff0c;用矩形表示 示例&#xff1a;用户、老师、学生 2.属性 描述实体的特征&#xff0c;用椭圆表示。 分为主键&#xff08;用户id&#xff09; 和非主键&#xff08;用户昵称&#xff09; 3.关系 表示实体间的…

Windows Flip PDF Plus Corporate PDF翻页工具

软件介绍 Flip PDF Plus Corporate是一款功能强大的PDF翻页工具&#xff0c;也被称为名编辑电子杂志大师。这款软件能够迅速将PDF文件转换为具有翻页动画效果的电子书&#xff0c;同时保留原始的超链接和书签。无论是相册、视频、音频&#xff0c;还是Flash、视频和链接&#…

Linux文件系统中的Page Cache和内存管理中的Page之间的关系

Linux文件系统中的Page Cache和内存管理中的Page之间有密切的关联&#xff0c;两者在底层机制上紧密结合&#xff0c;共同实现高效的内存和文件系统管理。以下是它们的关系和关键点&#xff1a; 核心关系 Page Cache的底层是内存Page Page Cache是由内存管理中的物理内存页&…

每日一个小病毒(C++)EnumChildWindows+shellcode

这里写目录标题 1. `EnumChildWindows` 的基本用法2. 如何利用 `EnumChildWindows` 执行 Shellcode?关键点:完整 Shellcode 执行示例3. 为什么 `EnumChildWindows` 能执行 Shellcode?4. 防御方法5. 总结EnumChildWindows 是 Windows API 中的一个函数,通常用于枚举所有子窗…

AI爬虫?爬!

1.你是否还在为大模型的key而感到忧伤和囊中羞涩&#xff0c;openrouter.ai&#xff0c;目前可免费白嫖多个大模型&#xff0c;代码如下 from openai import OpenAIclient OpenAI(base_url"https://openrouter.ai/api/v1",api_key"", )completion clien…

洛谷题单3-P5720 【深基4.例4】一尺之棰-python-流程图重构

题目描述 《庄子》中说到&#xff0c;“一尺之棰&#xff0c;日取其半&#xff0c;万世不竭”。第一天有一根长度为 a a a 的木棍&#xff0c;从第二天开始&#xff0c;每天都要将这根木棍锯掉一半&#xff08;每次除 2 2 2&#xff0c;向下取整&#xff09;。第几天的时候木…

c++中的auto关键字

在 C 中&#xff0c;auto 是一个类型推断关键字&#xff08;C11 引入&#xff09;&#xff0c;允许编译器根据变量的初始化表达式自动推导其类型。它极大地简化了代码编写&#xff0c;尤其在涉及复杂类型或模板的场景中。以下是 auto 的详细说明&#xff1a; 1. 基本用法 1.1 …

开发指南111-关闭所有打开的子窗口

门户系统是通过window.open通过单点登录的模式打开子系统的&#xff0c;这就要求门户系统退出时&#xff0c;关闭所有打开的子系统。 平台处理这一问题的核心原理如下&#xff1a; 主窗口定义&#xff1a; allChildWindows:[], //所有子窗口 pushChildWindow(childWindow){ …