从零构建游戏AIOpenSpiel强化学习实战指南为什么选择OpenSpiel作为强化学习入门工具第一次接触强化学习时很多开发者都会面临一个困境理论看似简单但真正动手实现时却无从下手。OpenSpiel恰好解决了这个痛点——它就像一套精心设计的乐高积木让你能够快速搭建起自己的第一个游戏AI系统而无需从零开始处理底层逻辑。这个由Google DeepMind维护的开源项目提供了超过60种经典游戏的标准化接口从简单的井字棋到复杂的围棋、扑克应有尽有。更难得的是它还内置了主流的强化学习算法实现让你可以专注于算法调优和策略设计而不是重复造轮子。记得我第一次用OpenSpiel实现井字棋AI时仅用不到50行代码就让AI学会了基本策略这种即时反馈的成就感正是初学者最需要的。OpenSpiel的另一个优势在于它的跨语言支持——核心用C实现保证了性能同时提供Python接口让开发更加便捷。环境配置与基础验证系统准备与依赖安装让我们从最基础的环节开始。OpenSpiel支持主流操作系统但为了减少环境问题推荐使用Ubuntu 22.04或更新版本。以下是完整的安装流程# 克隆官方仓库 git clone https://github.com/google-deepmind/open_spiel cd open_spiel # 创建并激活Python虚拟环境 python3 -m venv ./venv source venv/bin/activate # 安装依赖 python3 -m pip install -r requirements.txt # 安装OpenSpiel核心库 python3 -m pip install open_spiel安装完成后可以通过一个简单命令验证是否成功import pyspiel print(pyspiel.registered_games())如果看到一长列游戏名称输出如tic_tac_toe、go、poker等说明环境已经准备就绪。项目结构速览了解OpenSpiel的代码组织方式对后续开发很有帮助open_spiel/ ├── games/ # C实现的各类游戏 ├── algorithms/ # 核心算法实现 ├── python/ │ ├── examples/ # Python示例代码 │ └── algorithms/ # Python版算法 └── tests/ # 测试用例特别值得注意的是python/examples目录里面包含了从基础到进阶的各种示例是我们学习的最佳参考资料。第一个AI玩家随机策略实现井字棋游戏基础让我们从最简单的井字棋开始。在OpenSpiel中创建一个游戏实例只需要一行代码game pyspiel.load_game(tic_tac_toe) state game.new_initial_state()游戏状态(state)是OpenSpiel的核心概念它有三种节点类型决策节点玩家需要做出选择机会节点由随机事件决定下一步终止节点游戏结束井字棋没有随机因素所以只会遇到决策节点和终止节点。我们可以通过以下方法获取当前状态信息print(f当前玩家: {state.current_player()}) print(f合法动作: {state.legal_actions()}) print(f棋盘状态:\n{state})实现随机策略AI随机策略虽然简单但却是很好的起点。下面展示如何创建一个随机对战的完整流程import random import pyspiel def random_vs_random(): game pyspiel.load_game(tic_tac_toe) state game.new_initial_state() while not state.is_terminal(): print(f\n当前棋盘:\n{state}) if state.is_chance_node(): # 井字棋不会执行到这里 pass else: legal_actions state.legal_actions() action random.choice(legal_actions) print(f玩家{state.current_player()}执行动作: {state.action_to_string(state.current_player(), action)}) state.apply_action(action) print(\n最终棋盘:) print(state) print(f游戏结果: {state.returns()}) random_vs_random()运行这段代码你会看到两个随机策略玩家自动对战的过程输出类似这样当前棋盘: ... ... ... 玩家0执行动作: x(1,1) 当前棋盘: ... .x. ... 玩家1执行动作: o(0,0) 当前棋盘: o.. .x. ... ...从随机到智能强化学习实战强化学习环境搭建OpenSpiel提供了强化学习专用环境类比直接操作Game对象更方便from open_spiel.python import rl_environment env rl_environment.Environment(tic_tac_toe) state env.reset()RL环境的核心概念是TimeStep它包含以下信息observations当前观察状态rewards即时奖励discounts折扣因子step_type步骤类型FIRST, MID, LASTQ-Learning算法实现让我们实现一个简单的Q-Learning算法import numpy as np from collections import defaultdict class QLearningAgent: def __init__(self, env, learning_rate0.1, discount_factor0.9, exploration_rate0.3): self.q_values defaultdict(lambda: np.zeros(env.action_spec()[num_actions])) self.lr learning_rate self.df discount_factor self.er exploration_rate def step(self, time_step): if time_step.step_type.first(): return None obs time_step.observations[info_state][0] legal_actions time_step.observations[legal_actions][0] if np.random.random() self.er: # 探索 action np.random.choice(legal_actions) else: # 利用 q_values self.q_values[str(obs)] action legal_actions[np.argmax(q_values[legal_actions])] self.last_obs obs self.last_action action return action def learn(self, time_step): if time_step.step_type.first(): return obs str(time_step.observations[info_state][0]) last_obs str(self.last_obs) reward time_step.rewards[0] max_q_value np.max(self.q_values[obs]) td_target reward self.df * max_q_value td_error td_target - self.q_values[last_obs][self.last_action] self.q_values[last_obs][self.last_action] self.lr * td_error训练与评估现在我们可以训练这个Q-Learning智能体了env rl_environment.Environment(tic_tac_toe) agent QLearningAgent(env) for episode in range(1000): time_step env.reset() while not time_step.step_type.last(): action agent.step(time_step) time_step env.step([action]) agent.learn(time_step) # 每100轮评估一次 if episode % 100 0: wins 0 for _ in range(100): time_step env.reset() while not time_step.step_type.last(): action agent.step(time_step) time_step env.step([action]) if time_step.rewards[0] 0: wins 1 print(fEpisode {episode}, 胜率: {wins}%)经过足够训练后这个简单的Q-Learning智能体就能在井字棋中达到不错的水平。进阶技巧蒙特卡洛树搜索实战MCTS基本原理蒙特卡洛树搜索(MCTS)是游戏AI中的强大算法它通过模拟对局来评估动作价值。OpenSpiel已经内置了MCTS实现我们可以直接调用from open_spiel.python.algorithms import mcts # 创建MCTS机器人 evaluator mcts.RandomRolloutEvaluator(n_rollouts10) bot mcts.MCTSBot( game, uct_c2, # 探索系数 max_simulations100, # 每次决策的模拟次数 evaluatorevaluator )MCTS与强化学习结合我们可以用MCTS来改进我们的强化学习class MCTSAgent: def __init__(self, env): self.env env game pyspiel.load_game(env.game_name) self.evaluator mcts.RandomRolloutEvaluator(n_rollouts10) self.bot mcts.MCTSBot(game, uct_c2, max_simulations50, evaluatorself.evaluator) def step(self, time_step): if time_step.step_type.first(): self.state self.env._state # 获取内部状态 return None action self.bot.step(self.state) self.state.apply_action(action) return action这种结合方式能让智能体在训练初期就有不错的表现加速学习过程。性能优化与调试技巧状态表示优化OpenSpiel中的状态有多种表示方式选择合适的形式能提升性能表示形式方法特点适用场景字符串state.to_string()可读性好调试显示信息状态state.information_state_tensor()包含玩家视角信息不完全信息游戏观察状态state.observation_tensor()玩家所见信息强化学习常见问题排查动作不合法错误总是先检查legal_actions()使用action_to_string()验证动作含义训练不收敛调整学习率和折扣因子增加探索率检查奖励设置是否合理性能瓶颈减少不必要的状态拷贝使用C版本处理核心逻辑批量处理状态更新# 性能对比示例 import time def test_performance(): game pyspiel.load_game(tic_tac_toe) state game.new_initial_state() start time.time() for _ in range(1000): state.clone() print(f克隆耗时: {time.time()-start:.4f}s) start time.time() for _ in range(1000): str(state) print(f字符串转换耗时: {time.time()-start:.4f}s)从井字棋到复杂游戏掌握了井字棋后我们可以挑战更复杂的游戏。OpenSpiel支持的游戏主要分为几类完全信息游戏国际象棋(chess)围棋(go)五子棋(connect_four)不完全信息游戏德州扑克(texas_holdem)斗地主(dou_dizhu)多人合作/竞争游戏囚徒困境(prisoners_dilemma)协作推箱子(cooperative_box_pushing)切换到新游戏通常只需要修改一行代码# 切换到国际象棋 game pyspiel.load_game(chess) # 切换到德州扑克 game pyspiel.load_game(texas_holdem)不同游戏的主要区别在于状态表示和动作空间。例如国际象棋的状态表示state game.new_initial_state() print(state.observation_tensor()) # 8x8x73的张量 print(state.legal_actions()) # 可能多达200多个合法动作项目实战构建完整的AI对战系统系统架构设计一个完整的AI对战系统通常包含以下组件游戏引擎OpenSpiel核心AI模块多种算法实现评估系统胜负统计、策略分析用户界面可选的可视化class AITournament: def __init__(self, game_nametic_tac_toe): self.game pyspiel.load_game(game_name) self.agents { random: RandomAgent(), q_learning: QLearningAgent(), mcts: MCTSAgent() } def run_match(self, agent1, agent2, n_games100): results {win: 0, lose: 0, draw: 0} for _ in range(n_games): state self.game.new_initial_state() agents [agent1, agent2] current 0 while not state.is_terminal(): action agents[current].step(state) state.apply_action(action) current 1 - current returns state.returns() if returns[0] 0: results[win] 1 elif returns[0] 0: results[lose] 1 else: results[draw] 1 return results多算法对比分析运行不同AI之间的对战可以深入了解它们的特性tournament AITournament() # Q-Learning vs 随机 result tournament.run_match(tournament.agents[q_learning], tournament.agents[random]) print(fQ-Learning vs 随机: {result}) # MCTS vs Q-Learning result tournament.run_match(tournament.agents[mcts], tournament.agents[q_learning]) print(fMCTS vs Q-Learning: {result})典型输出可能如下Q-Learning vs 随机: {win: 85, lose: 2, draw: 13} MCTS vs Q-Learning: {win: 72, lose: 8, draw: 20}可视化与交互虽然OpenSpiel主要面向算法研究但我们也可以添加简单的可视化def display_board(state): board str(state).split(\n) print( a b c) for i, row in enumerate(board): print(f{i1} {row})对于更复杂的可视化可以考虑集成PyGame等图形库。最佳实践与经验分享调试技巧状态检查定期打印游戏状态验证是否符合预期动作验证使用action_to_string()理解动作含义奖励监控记录每步奖励确保奖励设计合理def debug_episode(agent, env): time_step env.reset() while not time_step.step_type.last(): print(f当前状态: {time_step.observations[info_state][0]}) print(f合法动作: {time_step.observations[legal_actions][0]}) action agent.step(time_step) print(f选择动作: {action}) time_step env.step([action]) print(f获得奖励: {time_step.rewards[0]})性能调优向量化处理使用NumPy批量处理状态并行模拟利用多线程进行MCTS模拟缓存策略缓存常用状态的计算结果from concurrent.futures import ThreadPoolExecutor class ParallelMCTSBot(mcts.MCTSBot): def __init__(self, game, uct_c, max_simulations, evaluator, n_threads4): super().__init__(game, uct_c, max_simulations, evaluator) self.executor ThreadPoolExecutor(max_workersn_threads) def parallel_simulate(self, state): futures [] for _ in range(self.max_simulations // self.n_threads): future self.executor.submit(self.simulate, state.clone()) futures.append(future) return futures常见陷阱忽视游戏对称性如井字棋的旋转对称性奖励设计不当稀疏奖励导致学习困难过度拟合特定游戏算法缺乏通用性# 处理对称性的示例 def augment_state(state): board state.observation_tensor() # 添加旋转和镜像变换 augmented [board] for _ in range(3): board np.rot90(board) augmented.append(board) return augmented扩展应用超越传统棋盘游戏OpenSpiel虽然以传统游戏为主但其设计理念可以扩展到更多领域商业策略模拟价格竞争、市场博弈网络安全攻防对抗模拟机器人控制多智能体协作例如创建一个简单的资源竞争游戏class ResourceGame: def __init__(self, num_players2, resources10): self.num_players num_players self.resources resources def new_initial_state(self): return ResourceState(self) class ResourceState: def __init__(self, game): self.game game self.current_player 0 self.resources game.resources self.collected [0] * game.num_players def legal_actions(self, player): return list(range(1, min(4, self.resources 1))) def apply_action(self, action): self.collected[self.current_player] action self.resources - action self.current_player (self.current_player 1) % self.game.num_players def is_terminal(self): return self.resources 0 def returns(self): total sum(self.collected) return [c/total for c in self.collected]这个简单游戏可以用于研究合作与竞争策略。
用OpenSpiel打造你的第一个强化学习游戏AI:从井字棋到实战
从零构建游戏AIOpenSpiel强化学习实战指南为什么选择OpenSpiel作为强化学习入门工具第一次接触强化学习时很多开发者都会面临一个困境理论看似简单但真正动手实现时却无从下手。OpenSpiel恰好解决了这个痛点——它就像一套精心设计的乐高积木让你能够快速搭建起自己的第一个游戏AI系统而无需从零开始处理底层逻辑。这个由Google DeepMind维护的开源项目提供了超过60种经典游戏的标准化接口从简单的井字棋到复杂的围棋、扑克应有尽有。更难得的是它还内置了主流的强化学习算法实现让你可以专注于算法调优和策略设计而不是重复造轮子。记得我第一次用OpenSpiel实现井字棋AI时仅用不到50行代码就让AI学会了基本策略这种即时反馈的成就感正是初学者最需要的。OpenSpiel的另一个优势在于它的跨语言支持——核心用C实现保证了性能同时提供Python接口让开发更加便捷。环境配置与基础验证系统准备与依赖安装让我们从最基础的环节开始。OpenSpiel支持主流操作系统但为了减少环境问题推荐使用Ubuntu 22.04或更新版本。以下是完整的安装流程# 克隆官方仓库 git clone https://github.com/google-deepmind/open_spiel cd open_spiel # 创建并激活Python虚拟环境 python3 -m venv ./venv source venv/bin/activate # 安装依赖 python3 -m pip install -r requirements.txt # 安装OpenSpiel核心库 python3 -m pip install open_spiel安装完成后可以通过一个简单命令验证是否成功import pyspiel print(pyspiel.registered_games())如果看到一长列游戏名称输出如tic_tac_toe、go、poker等说明环境已经准备就绪。项目结构速览了解OpenSpiel的代码组织方式对后续开发很有帮助open_spiel/ ├── games/ # C实现的各类游戏 ├── algorithms/ # 核心算法实现 ├── python/ │ ├── examples/ # Python示例代码 │ └── algorithms/ # Python版算法 └── tests/ # 测试用例特别值得注意的是python/examples目录里面包含了从基础到进阶的各种示例是我们学习的最佳参考资料。第一个AI玩家随机策略实现井字棋游戏基础让我们从最简单的井字棋开始。在OpenSpiel中创建一个游戏实例只需要一行代码game pyspiel.load_game(tic_tac_toe) state game.new_initial_state()游戏状态(state)是OpenSpiel的核心概念它有三种节点类型决策节点玩家需要做出选择机会节点由随机事件决定下一步终止节点游戏结束井字棋没有随机因素所以只会遇到决策节点和终止节点。我们可以通过以下方法获取当前状态信息print(f当前玩家: {state.current_player()}) print(f合法动作: {state.legal_actions()}) print(f棋盘状态:\n{state})实现随机策略AI随机策略虽然简单但却是很好的起点。下面展示如何创建一个随机对战的完整流程import random import pyspiel def random_vs_random(): game pyspiel.load_game(tic_tac_toe) state game.new_initial_state() while not state.is_terminal(): print(f\n当前棋盘:\n{state}) if state.is_chance_node(): # 井字棋不会执行到这里 pass else: legal_actions state.legal_actions() action random.choice(legal_actions) print(f玩家{state.current_player()}执行动作: {state.action_to_string(state.current_player(), action)}) state.apply_action(action) print(\n最终棋盘:) print(state) print(f游戏结果: {state.returns()}) random_vs_random()运行这段代码你会看到两个随机策略玩家自动对战的过程输出类似这样当前棋盘: ... ... ... 玩家0执行动作: x(1,1) 当前棋盘: ... .x. ... 玩家1执行动作: o(0,0) 当前棋盘: o.. .x. ... ...从随机到智能强化学习实战强化学习环境搭建OpenSpiel提供了强化学习专用环境类比直接操作Game对象更方便from open_spiel.python import rl_environment env rl_environment.Environment(tic_tac_toe) state env.reset()RL环境的核心概念是TimeStep它包含以下信息observations当前观察状态rewards即时奖励discounts折扣因子step_type步骤类型FIRST, MID, LASTQ-Learning算法实现让我们实现一个简单的Q-Learning算法import numpy as np from collections import defaultdict class QLearningAgent: def __init__(self, env, learning_rate0.1, discount_factor0.9, exploration_rate0.3): self.q_values defaultdict(lambda: np.zeros(env.action_spec()[num_actions])) self.lr learning_rate self.df discount_factor self.er exploration_rate def step(self, time_step): if time_step.step_type.first(): return None obs time_step.observations[info_state][0] legal_actions time_step.observations[legal_actions][0] if np.random.random() self.er: # 探索 action np.random.choice(legal_actions) else: # 利用 q_values self.q_values[str(obs)] action legal_actions[np.argmax(q_values[legal_actions])] self.last_obs obs self.last_action action return action def learn(self, time_step): if time_step.step_type.first(): return obs str(time_step.observations[info_state][0]) last_obs str(self.last_obs) reward time_step.rewards[0] max_q_value np.max(self.q_values[obs]) td_target reward self.df * max_q_value td_error td_target - self.q_values[last_obs][self.last_action] self.q_values[last_obs][self.last_action] self.lr * td_error训练与评估现在我们可以训练这个Q-Learning智能体了env rl_environment.Environment(tic_tac_toe) agent QLearningAgent(env) for episode in range(1000): time_step env.reset() while not time_step.step_type.last(): action agent.step(time_step) time_step env.step([action]) agent.learn(time_step) # 每100轮评估一次 if episode % 100 0: wins 0 for _ in range(100): time_step env.reset() while not time_step.step_type.last(): action agent.step(time_step) time_step env.step([action]) if time_step.rewards[0] 0: wins 1 print(fEpisode {episode}, 胜率: {wins}%)经过足够训练后这个简单的Q-Learning智能体就能在井字棋中达到不错的水平。进阶技巧蒙特卡洛树搜索实战MCTS基本原理蒙特卡洛树搜索(MCTS)是游戏AI中的强大算法它通过模拟对局来评估动作价值。OpenSpiel已经内置了MCTS实现我们可以直接调用from open_spiel.python.algorithms import mcts # 创建MCTS机器人 evaluator mcts.RandomRolloutEvaluator(n_rollouts10) bot mcts.MCTSBot( game, uct_c2, # 探索系数 max_simulations100, # 每次决策的模拟次数 evaluatorevaluator )MCTS与强化学习结合我们可以用MCTS来改进我们的强化学习class MCTSAgent: def __init__(self, env): self.env env game pyspiel.load_game(env.game_name) self.evaluator mcts.RandomRolloutEvaluator(n_rollouts10) self.bot mcts.MCTSBot(game, uct_c2, max_simulations50, evaluatorself.evaluator) def step(self, time_step): if time_step.step_type.first(): self.state self.env._state # 获取内部状态 return None action self.bot.step(self.state) self.state.apply_action(action) return action这种结合方式能让智能体在训练初期就有不错的表现加速学习过程。性能优化与调试技巧状态表示优化OpenSpiel中的状态有多种表示方式选择合适的形式能提升性能表示形式方法特点适用场景字符串state.to_string()可读性好调试显示信息状态state.information_state_tensor()包含玩家视角信息不完全信息游戏观察状态state.observation_tensor()玩家所见信息强化学习常见问题排查动作不合法错误总是先检查legal_actions()使用action_to_string()验证动作含义训练不收敛调整学习率和折扣因子增加探索率检查奖励设置是否合理性能瓶颈减少不必要的状态拷贝使用C版本处理核心逻辑批量处理状态更新# 性能对比示例 import time def test_performance(): game pyspiel.load_game(tic_tac_toe) state game.new_initial_state() start time.time() for _ in range(1000): state.clone() print(f克隆耗时: {time.time()-start:.4f}s) start time.time() for _ in range(1000): str(state) print(f字符串转换耗时: {time.time()-start:.4f}s)从井字棋到复杂游戏掌握了井字棋后我们可以挑战更复杂的游戏。OpenSpiel支持的游戏主要分为几类完全信息游戏国际象棋(chess)围棋(go)五子棋(connect_four)不完全信息游戏德州扑克(texas_holdem)斗地主(dou_dizhu)多人合作/竞争游戏囚徒困境(prisoners_dilemma)协作推箱子(cooperative_box_pushing)切换到新游戏通常只需要修改一行代码# 切换到国际象棋 game pyspiel.load_game(chess) # 切换到德州扑克 game pyspiel.load_game(texas_holdem)不同游戏的主要区别在于状态表示和动作空间。例如国际象棋的状态表示state game.new_initial_state() print(state.observation_tensor()) # 8x8x73的张量 print(state.legal_actions()) # 可能多达200多个合法动作项目实战构建完整的AI对战系统系统架构设计一个完整的AI对战系统通常包含以下组件游戏引擎OpenSpiel核心AI模块多种算法实现评估系统胜负统计、策略分析用户界面可选的可视化class AITournament: def __init__(self, game_nametic_tac_toe): self.game pyspiel.load_game(game_name) self.agents { random: RandomAgent(), q_learning: QLearningAgent(), mcts: MCTSAgent() } def run_match(self, agent1, agent2, n_games100): results {win: 0, lose: 0, draw: 0} for _ in range(n_games): state self.game.new_initial_state() agents [agent1, agent2] current 0 while not state.is_terminal(): action agents[current].step(state) state.apply_action(action) current 1 - current returns state.returns() if returns[0] 0: results[win] 1 elif returns[0] 0: results[lose] 1 else: results[draw] 1 return results多算法对比分析运行不同AI之间的对战可以深入了解它们的特性tournament AITournament() # Q-Learning vs 随机 result tournament.run_match(tournament.agents[q_learning], tournament.agents[random]) print(fQ-Learning vs 随机: {result}) # MCTS vs Q-Learning result tournament.run_match(tournament.agents[mcts], tournament.agents[q_learning]) print(fMCTS vs Q-Learning: {result})典型输出可能如下Q-Learning vs 随机: {win: 85, lose: 2, draw: 13} MCTS vs Q-Learning: {win: 72, lose: 8, draw: 20}可视化与交互虽然OpenSpiel主要面向算法研究但我们也可以添加简单的可视化def display_board(state): board str(state).split(\n) print( a b c) for i, row in enumerate(board): print(f{i1} {row})对于更复杂的可视化可以考虑集成PyGame等图形库。最佳实践与经验分享调试技巧状态检查定期打印游戏状态验证是否符合预期动作验证使用action_to_string()理解动作含义奖励监控记录每步奖励确保奖励设计合理def debug_episode(agent, env): time_step env.reset() while not time_step.step_type.last(): print(f当前状态: {time_step.observations[info_state][0]}) print(f合法动作: {time_step.observations[legal_actions][0]}) action agent.step(time_step) print(f选择动作: {action}) time_step env.step([action]) print(f获得奖励: {time_step.rewards[0]})性能调优向量化处理使用NumPy批量处理状态并行模拟利用多线程进行MCTS模拟缓存策略缓存常用状态的计算结果from concurrent.futures import ThreadPoolExecutor class ParallelMCTSBot(mcts.MCTSBot): def __init__(self, game, uct_c, max_simulations, evaluator, n_threads4): super().__init__(game, uct_c, max_simulations, evaluator) self.executor ThreadPoolExecutor(max_workersn_threads) def parallel_simulate(self, state): futures [] for _ in range(self.max_simulations // self.n_threads): future self.executor.submit(self.simulate, state.clone()) futures.append(future) return futures常见陷阱忽视游戏对称性如井字棋的旋转对称性奖励设计不当稀疏奖励导致学习困难过度拟合特定游戏算法缺乏通用性# 处理对称性的示例 def augment_state(state): board state.observation_tensor() # 添加旋转和镜像变换 augmented [board] for _ in range(3): board np.rot90(board) augmented.append(board) return augmented扩展应用超越传统棋盘游戏OpenSpiel虽然以传统游戏为主但其设计理念可以扩展到更多领域商业策略模拟价格竞争、市场博弈网络安全攻防对抗模拟机器人控制多智能体协作例如创建一个简单的资源竞争游戏class ResourceGame: def __init__(self, num_players2, resources10): self.num_players num_players self.resources resources def new_initial_state(self): return ResourceState(self) class ResourceState: def __init__(self, game): self.game game self.current_player 0 self.resources game.resources self.collected [0] * game.num_players def legal_actions(self, player): return list(range(1, min(4, self.resources 1))) def apply_action(self, action): self.collected[self.current_player] action self.resources - action self.current_player (self.current_player 1) % self.game.num_players def is_terminal(self): return self.resources 0 def returns(self): total sum(self.collected) return [c/total for c in self.collected]这个简单游戏可以用于研究合作与竞争策略。