python实现井字棋小游戏(使用蒙特卡洛搜索树进行训练)

简介: python实现井字棋小游戏(使用蒙特卡洛搜索树进行训练)

需要源码请点赞关注收藏后评论区留言或私信博主

蒙特卡洛搜索树是一类算法的统称,它适用于零和且确定环境的游戏,现在用蒙特卡洛搜索树算法对井字棋进行训练。

训练要求将模拟次数设定为2000次,即每个状态都模拟2000次到达终点,到到达胜利叶子节点则回溯得一分,失败或平局则不得分。

代码运行效果如下

 

部分代码如下

# 深度强化学习——原理、算法与PyTorch实战,代码名称:代31-例8.6-基于蒙特卡洛树的井字棋实例.py
import numpy as np
import sys
import math
import random
# 初始化环境
class environment():
    def __init__(self):
        self.start_env = np.array([[0] * 3] * 3)
class State(object):
    def __init__(self):
        self.current_env = [[]]
        self.current_value = 0
        self.current_round_index = 0
        self.cumulative_choices = [[]]
        self.available_choice = [[]]
    # 定义结束情况
    def is_end(self):
        tiaojian = True
        for i in range(0, 3):
            for j in range(0, 3):
                if self.current_env[i][j] == 0:
                    tiaojian = False
        for i in range(0, 3):
            if (np.array(self.current_env)[i] == np.array([1, 1, 1])).all() or (
                    np.array(self.current_env)[i] == np.array([2, 2, 2])).all():
                tiaojian = True
        if (np.array(self.current_env)[:, 0] == np.array([1, 1, 1])).all() or (
                np.array(self.current_env)[:, 0] == np.array([2, 2, 2])).all() or (
                np.array(self.current_env)[:, 1] == np.array([1, 1, 1])).all() or (
                np.array(self.current_env)[:, 1] == np.array([2, 2, 2])).all() or (
                np.array(self.current_env)[:, 2] == np.array([1, 1, 1])).all() or (
                np.array(self.current_env)[:, 2] == np.array([2, 2, 2])).all():
            tiaojian = True
        elif np.array(self.current_env)[0, 0] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 2] != 0:
            tiaojian = True
        elif np.array(self.current_env)[0, 2] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 0] != 0:
            tiaojian = True
        return tiaojian
    # 定义自家胜利情况
    def i_win(self):
        tiaojian = False
        for i in range(0, 3):
            if ((np.array(self.current_env)[i] == np.array([1, 1, 1])).all()):
                tiaojian = True
        if (np.array(self.current_env)[:, 0] == np.array([1, 1, 1])).all() or (
                np.array(self.current_env)[:, 1] == np.array([1, 1, 1])).all() or (
                np.array(self.current_env)[:, 2] == np.array([1, 1, 1])).all():
            tiaojian = True
        if np.array(self.current_env)[0, 0] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 2] == 1:
            tiaojian = True
        if np.array(self.current_env)[0, 2] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 0] == 1:
            tiaojian = True
        return tiaojian
    # 定义自家失败情况
    def i_lose(self):
        tiaojian = False
        for i in range(0, 3):
            if ((np.array(self.current_env)[i] == np.array([2, 2, 2])).all()):
                tiaojian = True
        if (np.array(self.current_env)[:, 0] == np.array([2, 2, 2])).all() or (
                np.array(self.current_env)[:, 1] == np.array([2, 2, 2])).all() or (
                np.array(self.current_env)[:, 2] == np.array([2, 2, 2])).all():
            tiaojian = True
        if np.array(self.current_env)[0, 0] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 2] == 2:
            tiaojian = True
        if np.array(self.current_env)[0, 2] == np.array(self.current_env)[1, 1] == np.array(self.current_env)[
            2, 0] == 2:
            tiaojian = True
        return tiaojian
    # 设置/获取可用动作
    def set_available_choice(self, choice):
        self.available_choice = choice
    def get_available_choice(self):
        return self.available_choice
    # 设置/获取当前环境
    def get_current_env(self):
        return self.current_env
    def set_current_env(self, env):
        self.current_env = env
    # 设置/获取累计奖赏
    def get_current_value(self):
        return self.current_value
    def set_current_value(self, value):
        self.current_value = value
    def get_current_round_index(self):
        return self.current_round_index
    def set_current_round_index(self, turn):
        self.current_round_index = turn
    # 设置/获取累积动作
    def get_cumulative_choices(self):
        return self.cumulative_choices
    def set_cumulative_choices(self, choices):
        self.cumulative_choices = choices
    # 判断是否结束
    def is_terminal(self):
        # The round index starts from 1 to max round number
        return self.is_end()
    # 计算累计奖赏
    def compute_reward(self):
        return self.current_value
    # 随机策略得到下一状态
    def get_next_state_with_random_choice(self):
        a = np.array([[0] * 3] * 3)
        b = [0] * len(self.available_choice)
        random_choice = random.choice([choice for choice in self.available_choice])
        next_state = State()
        next_state.set_current_round_index(self.current_round_index + 1)
        next_state.set_cumulative_choices(self.cumulative_choices + [random_choice])
        for i in range(0, len(self.available_choice)):
            b[i] = self.available_choice[i]
        next_state.available_choice = b
        next_state.available_choice.remove(random_choice)
        if next_state.current_round_index != 0 and next_state.current_round_index % 2 == 0:
            for i in range(0, 3):
                for j in range(0, 3):
                    a[i][j] = self.current_env[i][j]
            a[random_choice[0]][random_choice[1]] = 1
            next_state.set_current_env(a)
        if next_state.current_round_index != 0 and next_state.current_round_index % 2 == 1:
            for i in range(0, 3):
                for j in range(0, 3):
                    a[i][j] = self.current_env[i][j]
            a[random_choice[0]][random_choice[1]] = 2
            next_state.set_current_env(a)
        if next_state.i_win():
            next_state.set_current_value(1)
        if next_state.i_lose():
            next_state.set_current_value(-0.5)
        if next_state.i_lose() != True and next_state.i_win() != True:
            next_state.set_current_value(0)
        return next_state
    def __repr__(self):
        return "State: {}, value: {},  choices: {}".format(hash(self), self.current_value,
                                                           self.available_choice)
# 建立节点
class Node(object):
    def __init__(self):
        self.env = [[]]
        self.parent = None
        self.children = []
        self.visit_times = 0
        self.quality_value = 0.0
        self.state = None
    def avanum(self):
        num = 0
        a = self.get_state().current_env
        for i in range(0, 3):
            for j in range(0, 3):
                if a[i][j] == 0:
                    num += 1
        return num
    def set_state(self, state):
        self.state = state
    def get_state(self):
        return self.state
    def get_parent(self):
        return self.parent
    def set_parent(self, parent):
        self.parent = parent
    def get_children(self):
        return self.children
    def get_visit_times(self):
        return self.visit_times
    def set_visit_times(self, times):
        self.visit_times = times
    def visit_times_add_one(self):
        self.visit_times += 1
    def get_quality_value(self):
        return self.quality_value
    def set_quality_value(self, value):
        self.quality_value = value
    def quality_value_add_n(self, n):
        self.quality_value += n
    def is_all_expand(self):
        return len(self.children) == self.avanum()
    def add_child(self, sub_node):
        sub_node.set_parent(self)
        self.children.append(sub_node)
    def __repr__(self):
        return "Node: {}, Q/N: {}/{}, state: {}".format(hash(self), self.quality_value, self.visit_times, self.state)
# *************************************
# 搜索树策略
def tree_policy(node):
    # Check if the current node is the leaf node
    while node.get_state().is_terminal() == False:
        if node.is_all_expand():
            node_best = best_child(node, True)
        else:
            # Return the new sub node
            sub_node = expand(node)
            return sub_node
        # Return the leaf node
        return node_best
# 默认策略
def default_policy(node):
    # Get the state of the game
    current_state = node.get_state()
    # Run until the game over
    while current_state.is_terminal() == False:
        # Pick one random action to play and get next state
        current_state = current_state.get_next_state_with_random_choice()
    final_state_reward = current_state.compute_reward()
    return final_state_reward
# 扩展
def expand(node):
    tried_sub_node_states = [sub_node.get_state().current_env for sub_node in node.get_children()]
    # Check until get the new state which has the different action from others
    noin = False
    while noin == False:
        noin = True
        new_state = node.get_state().get_next_state_with_random_choice()
        for i in range(0, len(tried_sub_node_states)):
            if (new_state.current_env == tried_sub_node_states[i]).all():
                noin = False
    sub_node = Node()
    sub_node.set_state(new_state)
    node.add_child(sub_node)
    return sub_node
def best_child(node, is_exploration):
    # TODO: Use the min float value
    best_score = -sys.maxsize
    best_sub_node = None
    # Travel all sub nodes to find the best one
    for sub_node in node.get_children():
        # Ignore exploration for inference
        if is_exploration:
            C = 1 / math.sqrt(2.0)
        else:
            C = 0.0
        # UCB = quality / times + C * sqrt(2 * ln(total_times) / times)
        left = sub_node.get_quality_value() / sub_node.get_visit_times()
        right = 2.0 * math.log(node.get_visit_times()) / sub_node.get_visit_times()
        score = left + C * math.sqrt(right)
        if score > best_score:
            best_sub_node = sub_node
            best_score = score
    return best_sub_node
# 回传
def backup(node, reward):
    # Update util the root node
    while node != None:
        # Update the visit times
        node.visit_times_add_one()
        # Update the quality value
        node.quality_value_add_n(reward)
        # Change the node to the parent node
        node = node.parent
# 蒙特卡洛搜索树算法
def monte_carlo_tree_search(node):
    computation_budget = 4000
    # Run as much as possible under the computation budget
    for i in range(computation_budget):
        # 1. Find the best node to expand
        expand_node = tree_policy(node)
        # 2. Random run to add node and get reward
        reward = default_policy(expand_node)
        # 3. Update all passing nodes with reward
        backup(expand_node, reward)
    # N. Get the best next node
    best_next_node = best_child(node, False)
    a = [[sub_node.quality_value, sub_node.get_state().current_env] for sub_node in node.get_children()]
    print(a)
    return best_next_node
# *************************************
def main():
    # Create the initialized state and initialized node
    init_state = State()
    init_state.set_current_env(np.array([[0] * 3] * 3))
    init_state.set_current_round_index(1)
    init_state.set_available_choice([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]])
    init_node = Node()
    init_node.state = init_state
    init_env = environment()
    current_node = init_node
    # Set the rounds to play
    d = 0
    while (current_node.get_state().is_terminal() != True):
        if d % 2 == 0:
            print("Play round: {}".format(d + 1))
            print("你好,这是我下的步骤,来与我一战")
            current_node = monte_carlo_tree_search(current_node)
            print(current_node.get_state().current_env)
        else:
            new = Node()
            bb = State()
            new.set_state(bb)
            print("你的回合,请君下棋")
            n = 3
            a = [[0] * n] * n
            for i in range(n):
                a[i] = input().split(" ")
            for i in range(0, 3):
                for j in range(0, 3):
                    a[i][j] = int(a[i][j])
        = current_node.get_state().current_round_index + 1
            current_node = new
        d += 1
    if current_node.get_state().i_win():
        print("我赢了!你真菜")
    if current_node.get_state().i_lose():
        print("我输了,快给我调力度")
    if current_node.get_state().i_win() != True and current_node.get_state().i_lose() != True:
        print("平局,你还不错")
if __name__ == "__main__":
    main()
相关文章
|
2月前
|
Python
蓝桥杯练习题(一):Python组之入门训练题
这篇文章是关于蓝桥杯Python组的入门训练题,包括Fibonacci数列、圆的面积、序列求和和A+B问题的具体代码实现和样例输出。
130 0
|
19天前
|
Python
二分查找变种大赏!Python 中那些让你效率翻倍的搜索绝技!
二分查找是一种高效的搜索算法,适用于有序数组。其基本原理是通过不断比较中间元素来缩小搜索范围,从而快速找到目标值。常见的变种包括查找第一个等于目标值的元素、最后一个等于目标值的元素、第一个大于等于目标值的元素等。这些变种在实际应用中能够显著提高搜索效率,适用于各种复杂场景。
36 9
|
20天前
|
算法 数据处理 开发者
超越传统:Python二分查找的变种策略,让搜索效率再上新台阶!
本文介绍了二分查找及其几种Python实现的变种策略,包括经典二分查找、查找第一个等于给定值的元素、查找最后一个等于给定值的元素以及旋转有序数组的搜索。通过调整搜索条件和边界处理,这些变种策略能够适应更复杂的搜索场景,提升搜索效率和应用灵活性。
30 5
|
2月前
|
存储 机器学习/深度学习 算法
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
蓝桥杯Python编程练习题的集合,涵盖了从基础到提高的多个算法题目及其解答。
68 3
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
|
2月前
|
算法 数据可视化 Python
使用 Python 模拟蒙特卡洛实验
使用 Python 模拟蒙特卡洛实验
|
3月前
|
存储 大数据 索引
解锁Python隐藏技能:构建高效后缀树Suffix Tree,处理大数据游刃有余!
通过构建高效的后缀树,Python程序在处理大规模字符串数据时能够游刃有余,显著提升性能和效率。无论是学术研究还是工业应用,Suffix Tree都是不可或缺的强大工具。
56 6
|
3月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
58 2
|
3月前
|
存储 算法 数据挖掘
高效文本处理新纪元:Python后缀树Suffix Tree,让数据分析更智能!
在大数据时代,高效处理和分析文本信息成为关键挑战。后缀树作为一种高性能的数据结构,通过压缩存储字符串的所有后缀,实现了高效的字符串搜索、最长公共前缀查询等功能,成为文本处理的强大工具。本文探讨Python中后缀树的应用,展示其在文本搜索、重复内容检测、最长公共子串查找、文本压缩及智能推荐系统的潜力,引领数据分析迈入新纪元。虽然Python标准库未直接提供后缀树,但通过第三方库或自定义实现,可轻松利用其强大功能。掌握后缀树,即掌握开启文本数据宝藏的钥匙。
55 5
|
3月前
|
存储 开发者 Python
从理论到实践:Python中Trie树与Suffix Tree的完美结合,开启编程新篇章!
在编程领域,高效的数据结构对于解决问题至关重要。本文通过一个案例分析,介绍如何在Python中结合使用Trie树(前缀树)和Suffix Tree(后缀树)。案例聚焦于开发具备高效拼写检查和文本相似度检测功能的文本编辑器。首先,通过构建Trie树快速检查单词是否存在;接着,利用Suffix Tree检测文本相似度。尽管Python标准库未直接提供Suffix Tree,但可通过第三方库或自定义实现。本文展示了高级数据结构在实际应用中的强大功能,并强调了理论与实践相结合的重要性。
44 1
|
3月前
|
存储 算法 Python
逆袭之路:掌握Python字典树Trie与后缀树,成为技术圈的耀眼新星!
在编程的征途上,每个人都渴望成为那个能够独当一面、解决复杂问题的技术高手。而掌握高级数据结构,如字典树(Trie)与后缀树(Suffix Tree),无疑是你逆袭路上的重要一步。这些数据结构不仅能够提升你的编码技能,还能让你在解决特定问题时游刃有余,从而在技术圈中脱颖而出,成为那颗耀眼的新星。
34 1