我正在为一款游戏编写一些决策 AI,我想出了以下代码。
if(pushedLeft && leftFree && leftExists)
GoLeft();
else if(pushedRight && rightFree && rightExists)
GoRight();
else if(leftFree && leftExists)
GoLeft();
else if(rightFree && rightExists)
GoRight();
else if(pushedLeft && leftExists)
GoLeft();
else if(pushedRight && rightExists)
GoRight();
else if(leftExists)
GoLeft();
else if(rightExists)
GoRight();
// else do nothing...
这是一个相当长的 if 语句流,具有类似的条件!
请注意,它构成了这个漂亮的图案:
L1 L2 L3 -> L
R1 R2 R3 -> R
L2 L3 -> L
R2 R3 -> R
L1 L3 -> L
R1 R3 -> R
L3 -> L
R3 -> R
(nothing) -> 0
这段代码的目的是根据一些传入的状态信息来决定对象应该向左还是向右移动(或者根本不移动)。每条信息都有不同的优先级。我可以将它写在这样的有序列表中:
Highest Priority
----------------
Don't ever move into an invalid space
Prefer to move into an unoccupied space
Prefer to move in the push direction
Prefer to move left
----------------
Lowest Priority
很明显,添加额外的信息输入来做出此决定将使条件的数量增加一倍。并且将这些输入的潜在值数量加倍(例如:允许上/下/左/右)也会使条件数量加倍。 (所以这是 n×m2 条件,对吧?)
所以我的问题是:
是否有一种漂亮、令人满意、优雅的编码方式?
我在想一定有一个很好的“n×m”方式来做到这一点(编辑:我最初在这里有“n+m”,但这似乎是不可能的,因为有 n×m 个输入条件)。适用于我的代码和一般问题的东西?
最好是性能与上述条件版本一样好或更好的东西。理想情况下,避免堆分配的东西 - 对于在游戏开发场景中使用很重要(尽管如有必要,这些总是可以通过缓存等进行优化)。
还有:这个问题是否有任何“Googleable 术语”?我怀疑这不是一个罕见的问题 - 但我不知道它的名字。
更新:一个想法感谢Superpig's answer , 是计算各种选项的分数。像这样:
int nothingScore = 1 << 4;
int leftScore = (1 << 1) + (pushedLeft ? 1 << 2 : 0) + (leftFree ? 1 << 3 : 0) + (leftExists ? 1 << 5 : 0);
int rightScore = (pushedRight ? 1 << 2 : 0) + (rightFree ? 1 << 3 : 0) + (rightExists ? 1 << 5 : 0);
当然有更好的方法来编写评分代码(以及其他评分方法)。然后仍然是计算分数后选择要做什么的问题。当然,可能还有更好的完全不涉及评分的方法。
更新 2: 我有 posted and accepted my own answer here (因为 Superpig 的不是一个完整的解决方案,而且到目前为止,甚至远程都没有其他答案在正确的轨道上)。我没有对各种输出进行评分,而是选择了一种使用位域的选项消除方法。这允许仅使用单个整数进行内存决策。
最佳答案
这本质上是一个分类问题;您想要决策树(或行为树)之类的东西。您正在尝试针对情况(有效性、自由度、插入方向等)获取一堆离散输入,并将结果分类为“上、下、左或右”。
我怀疑,如果您希望 if 语句的长链具有更好或相同的性能 - 至少在指令计数/完成的比较次数方面 - 那么您将必须使您的以你在那里做的方式进行比较。计算所有方向的分数然后检查最大值,或递归地将移动列表划分为首选和非首选等方法最终都会比纯比较序列做更多的工作。
我认为,您可以只构建一个查找表。你有 4 位指示方向是否有效,4 位指示方向是否被占用,2 位指示插入方向,总共 10 位 - 所以有 1024 种不同的情况,每种情况下的行为可以是仅用 2 位(因此,1 字节)进行描述 - 使总表大小为 1024 字节。
单个条目将是这样的结构:
union DecisionSituation
{
unsigned short Index;
struct
{
bool ValidLeft : 1;
bool ValidRight : 1;
bool ValidUp : 1;
bool ValidDown : 1;
bool OccupiedLeft : 1;
bool OccupiedRight : 1;
bool OccupiedUp : 1;
bool OccupiedDown : 1;
Direction PushDirection : 2;
} Flags;
}
您可以通过填写该结构中的标志来描述您的情况,然后读取“索引”值以获得您的查找表索引。
编辑:此外,关于您的评分函数,因为您执行的是严格的位模式,我认为您可以跳过所有三元运算符:
int leftScore = (leftExists << 4) | (leftFree << 3) | (pushedLeft << 2) | 1;
int rightScore = (rightExists << 4) | (rightFree << 3) | (pushedRight << 2) | 0;
// Find the highest scoring direction here
// If none of the scores are at least (1 << 4) it means none of them existed
if(highest score < (1 << 4)) return nothing;
// otherwise just return the highest scoring direction
关于c# - 一种更优雅的编写决策代码的方式来评估具有不同优先级的多个输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10968197/
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur