我一直在考虑为棋盘游戏制作AI很长一段时间,最近我开始收集资源和算法。游戏是非随机的,并且大多数时候,<对于玩家来说有3个动作,有时会有> 20个动作。我想存储关键动作或模糊动作,以便AI从错误中学习并且下次不会犯同样的错误。无需存储肯定会赢或输的动作。所以我实际上有一个稀疏的决策树用于游戏的开始。 我想知道如何将这个决策树存储在数据库中?数据库不需要是SQL,我不知道哪个数据库适合这个特定问题。
编辑:请不要告诉我将决策树解析为内存,只要想象游戏就像国际象棋一样复杂。答案 0 :(得分:1)
当你走遍树时,neo4j对我来说似乎是一个很好的解决方案。 SQL不是一个好选择,因为查询需要很多连接。正如我理解这个问题,你要求一种方法将一些图形存储在数据库中,neo4j是一个明确表示图形的数据库。对于稀疏性,您可以使用PropertyContainers将基元或字符串数组附加到图形的边缘以编码移动序列(我是正确的,通过稀疏性和跳过节点,您的意思是树边缘是移动序列而不是单个移动?)。
答案 1 :(得分:0)
首先,您尝试做的事情听起来像基于案例的推理(CBR)问题,请参阅:http://en.wikipedia.org/wiki/Case-based_reasoning#Prominent_CBR_systems。 CBR将拥有一个决策数据库,您的系统理论上会选择最佳结果。
因此我建议使用neo4j,这是一个nosql图数据库。 http://neo4j.org/
因此,为了表示您的游戏,每个位置都是图表中的一个节点,每个节点应该包含来自所述位置的潜在移动。您可以跟踪作为游戏进度学习的评分指标,以便AI更加了解情况。
答案 2 :(得分:0)
我会使用像RavenDB这样的文档数据库(NOSQL),因为您可以在数据库中存储任何数据结构。
文档不像普通的SQL数据库那样平坦,并且允许您直接存储像树一样的分层数据:
{
decision: 'Go forward',
childs: [
{ decision: 'Go backwards' },
{
decision: 'Stay there',
childs: [
{ decision: 'Go backwards' }
]
}
]
}
在这里,您可以看到可以存储在RavenDB中的示例JSON树。
RavenDB还具有查询分层数据的内置功能: http://ravendb.net/faq/hierarchies
请查看documentation以获取有关RavenDB如何工作的更多信息。
资源:
答案 3 :(得分:0)
您可以使用memory mapped file作为存储空间。 首先,创建“编译器”。此编译器将解析文本文件并将其转换为紧凑的二进制表示形式。主应用程序将此二进制优化文件映射到内存中。这将解决您的内存大小限制问题
答案 4 :(得分:0)
从简单的数据库表设计开始。
决定: CurrentState BINARY(57)| NewState BINARY(57)|分数INT
CurrentState和NewState是游戏状态的序列化版本。分数是给予NewState的权重(正分数是好的动作,负分数是不好的动作)你的AI可以适当地更新这些分数。
> Renju,使用15x15板,每个位置可以是黑色,白色或空白,因此您需要Ceiling((2bits * 15 * 15)/ 8)字节来序列化电路板。在SQL中,它将是T-SQL中的BINARY(57)你的AI会选择它存储的当前动作......
SELECT NewState FROM Decisions WHERE CurrentState = @SerializedState ORDER BY Score DESC
您将获得当前游戏状态中所有存储的下一步动作的列表,按照最佳得分至最低得分的顺序。
您的表结构将在(CurrentState,NewState)上具有复合唯一索引(主键),以便于搜索并避免重复。
这不是最佳/最佳解决方案,但由于您缺乏数据库知识,我相信它最容易实现并为您提供良好的开端。
答案 5 :(得分:0)
如果我与国际象棋引擎进行比较,那些可以从内存中进行比较,也许除了打开库之外。国际象棋太复杂,无法存储一个解决决策树。国际象棋引擎通过将启发式评估分配给潜在和瞬态未来位置(而不是移动)来发挥作用。未来的位置是通过某种有限的深度搜索找到的,可以在内存中缓存一段时间,但通常每回合都会明确重新计算,因为搜索空间太大而无法以比重新计算更快的方式存储。
答案 6 :(得分:0)
你知道Chinook - 解决问题的人工智能吗?它通过编译每个可能的最终游戏的数据库来实现这一点。虽然这不完全是你在做什么,但你可以从中学习。
答案 7 :(得分:0)
我无法清楚地想到你在树中处理的数据结构及其复杂性。
但是这里有一些你可能感兴趣的想法:
答案 8 :(得分:0)
我会用传统方式在国际象棋引擎中处理一本开头书:
国际象棋引擎通常通过Zobrist hashing计算当前游戏状态的哈希函数,这是为游戏状态构建良好哈希函数的简单方法。
这种方法的最大优点是它可以处理transpositions,也就是说,如果可以通过备用路径访问相同的状态,则无需担心这些备用路径,只需关注游戏陈述自己。
大多数国际象棋引擎使用从记录游戏编译的静态开放书籍,因此使用简单的二进制文件将这些哈希值映射到分数; e.g。
struct book_entry {
uint64_t hash
uint32_t score
}
然后按哈希对条目进行排序,并且由于操作系统缓存,文件中的简单binary search将很快找到所需的条目。
但是,如果您希望引擎不断学习,则需要更复杂的数据结构;在这一点上,通常不值得做自己,你应该使用一个可用的库。我可能会使用LevelDB,但任何可以存储键值对的东西都很好(Redis,SQLite,GDBM等)
您如何更新分数取决于您的游戏。在拥有大量数据的游戏中,一种简单的方法,例如存储移动后赢得的游戏百分比,这导致了该位置的工作;如果您的数据较少,则可以将游戏树搜索的结果存储在相关位置作为分数。机器学习技术如Q learning也是可能的,虽然我不知道在实践中实际执行此操作的程序。
答案 9 :(得分:-1)
我假设您的问题是询问如何将决策树转换为可以写入某个位置的串行格式,然后用于重建树。
尝试使用树的预先遍历遍历,使用toString()函数(或其等效函数)将存储在决策树的每个节点的数据转换为文本描述符。通过预先遍历,我的意思是实现一种算法,该算法首先在节点上执行toString()操作,并将输出写入数据库或文件,然后以指定的顺序递归地在其子节点上执行相同的操作。因为您正在处理稀疏树,所以toString()操作还应包含有关子树是否存在的信息。
重建树很简单 - 第一个存储值是根节点,第二个是左子树的根成员,依此类推。为每个节点存储的串行数据应提供下一个输入节点应属于哪个子树的信息。