我正在开发一种棋盘游戏算法,其中使用递归遍历大型树,但是,它的行为并不像预期的那样。我如何处理这些以及您对这些情况的体验?
更糟糕的是,它正在使用alpha-beta修剪,这意味着树的整个部分永远不会被访问,并且它只是在满足某些条件时停止递归。我也无法将搜索深度更改为较低的数字,因为虽然它是确定性的,但结果确实会因搜索的深度而异,并且可能会在较低的搜索深度(和确实如此)下按预期运行。
现在,我不会问你“我的代码中的问题在哪里?”但我正在寻找一般技巧,工具,可视化,以及调试此类代码的任何东西。就个人而言,我正在开发C#,但欢迎使用任何和所有工具。虽然我认为这可能最适用于命令式语言。
答案 0 :(得分:23)
记录日志。广泛登录您的代码。根据我的经验,日志记录是解决这些类型问题的方法。当很难弄清楚你的代码在做什么时,广泛记录它是一个非常好的解决方案,因为它允许你从代码中输出内部状态是什么;它确实不是一个完美的解决方案,但就我所见,它比使用任何其他方法更好。
答案 1 :(得分:6)
也许您可以将递归转换为具有参数显式堆栈的迭代。以这种方式进行测试更容易,因为您可以直接记录值,访问堆栈,而不必在每次自我评估中传递数据/变量,或防止它们超出范围。
答案 2 :(得分:4)
我过去做的一件事是格式化日志以反映递归深度。因此,您可以为每个递归或其他一些分隔符执行新的缩进。然后创建一个调试dll,记录每次迭代需要知道的所有内容。在这两者之间,你应该能够阅读执行路径并希望告诉我们什么是错的。
答案 3 :(得分:3)
我通常会使用一个或多个具有明确定义结果的预定义数据集对这些算法进行单元测试。我通常会按照复杂程度的顺序进行几次这样的测试。
如果你坚持调试,有时使用检查给定值的语句对代码进行操作是有用的,这样你就可以在那个时候附加一个断点并放在代码中:
if ( depth = X && item.id = 32) {
// Breakpoint here
}
答案 4 :(得分:2)
当我开发AI算法来玩俄罗斯方块游戏时,我曾遇到过类似的问题。在尝试了许多事情之后,在阅读我自己的日志和调试以及进入和退出函数时失去了很多时间对我来说有用的是编写快速可视化器并使用FIXED输入来测试我的代码。
所以,如果时间不是问题而且你真的想了解发生了什么,请获得一个固定的电路板状态,并使用调试日志/输出和某种类型的混合查看您的程序对数据执行的操作自己的工具,显示每个步骤的信息。
一旦找到可以解决这个问题的电路板状态,请尝试将其启动的功能定位,然后您就可以修复它了。
答案 5 :(得分:1)
我知道这可能是多么痛苦。在我的工作中,我们目前正在使用第三方应用程序,它基本上表现为黑盒子,因此我们必须设计一些有趣的调试技术来帮助我们解决问题。
当我在大学学习编译理论课程时,我们使用了一个软件库来可视化我们的树木;这对你也有帮助,因为它可以帮助你看到树的样子。实际上,你可以自己构建一个WinForms / WPF应用程序来将树的内容转储到TreeView控件中 - 它很乱,但是它可以完成工作。
您可能也想考虑某种调试输出。我知道你提到你的树很大,但是在执行过程中关键点的调试语句或中断可能会让你无法想象。
请记住,使用Visual Studio进行智能调试可以创造奇迹。很难看到状态如何在多个休息时间内发生变化,但Visual Studio 2010实际上应该对此有所帮助。
不幸的是,如果没有进一步的信息,帮助您进行调试并不是特别容易。你有没有确定它开始破裂的第一个深度?是否继续以更高的搜索深度打破?您可能想要评估您的工作案例,并尝试确定它的不同之处。
答案 6 :(得分:0)
由于您说遍历没有按预期工作,我假设您已经知道出现问题的地方。然后检查代码以验证您没有忽略基本的东西。
之后我建议你设置一些简单的单元测试。如果它们通过,则继续添加测试直到它们失败。如果它们失败了,那么减少测试直到它们通过或尽可能简单。这应该可以帮助您查明问题。
如果你想调试,我建议你使用条件断点。 Visual Studio允许您修改断点,因此您可以设置何时应触发断点的条件。这可以减少您需要查看的迭代次数。
答案 7 :(得分:0)
我将从检测功能开始。在每个递归调用日志中,数据结构和任何其他有助于您识别问题的信息。
打印转储以及源代码,然后离开计算机,在一杯咖啡上进行基于纸张的良好调试。
答案 8 :(得分:0)
从您提到的 if else 语句的基本情况开始,然后尝试通过将其写在笔和纸上+在使用值生成递归函数的前几个实例时在控制台上打印值来引导您的想法。
我们的座右铭是在您打印的值之间找到正确的趋势,并将它们与您在递归算法的最初几步中写在纸上的值进行匹配。