- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我接到了一项任务,要编写一个由人类玩家和 AI 玩家组成的 NIM 游戏。游戏是“Misere”(最后一个必须拿起一根棍子的人输了)。 AI 应该使用 Minimax 算法,但它正在采取使其输得更快的 Action ,我不明白为什么。几天来我一直处于死胡同。Minimax算法的重点是不输,如果处于亏损位置,尽量延迟亏损,对吧?
考虑以下几点:
NIMBoard board = new NIMBoard(34, 2);
所以我们从这个场景开始,* 字符代表一根棍子:
Row 0: **
Row 1: **
在这种特殊的棋盘情况下,Minimax 算法总是提出“从第 1 行移除 2 根木棍”这一着法。这显然是一个糟糕的举动,因为它在第 0 行留下了 2 个木棍,然后人类玩家可以从第 0 行选择 1 个木棍并赢得游戏。
AI 玩家应该选择从任一堆中挑选一根棍子。这留给了人类玩家:
Row 0: *
Row 1: **
所以无论人类玩家现在下哪一步棋,当计算机在那之后下下一步棋时,人类玩家总是会输。显然是更好的策略,但为什么算法不建议采取这一行动?
public class Minimax
{
public Move nextMove;
public int evaluateComputerMove(NIMBoard board, int depth)
{
int maxValue = -2;
int calculated;
if(board.isFinal())
{
return -1;
}
for(Move n : this.generateSuccessors(board))
{
NIMBoard newBoard = new NIMBoard(board.getPos(), board.getNumPiles());
newBoard.parseMove(n);
calculated = this.evaluateHumanMove(newBoard, depth + 1);
if(calculated > maxValue)
{
maxValue = calculated;
if(depth == 0)
{
System.out.println("Setting next move");
this.nextMove = n;
}
}
}
if(maxValue == -2)
{
return 0;
}
return maxValue;
}
public int evaluateHumanMove(NIMBoard board, int depth)
{
int minValue = 2;
int calculated;
if(board.isFinal())
{
return 1;
}
for(Move n : this.generateSuccessors(board))
{
NIMBoard newBoard = new NIMBoard(board.getPos(), board.getNumPiles());
newBoard.parseMove(n);
calculated = this.evaluateComputerMove(newBoard, depth + 1);
// minValue = Integer.min(this.evaluateComputerMove(newBoard, depth + 1), minValue);
if(calculated < minValue)
{
minValue = calculated;
}
}
if(minValue == 2)
{
return 0;
}
return minValue;
}
public ArrayList<Move> generateSuccessors(NIMBoard start)
{
ArrayList<Move> successors = new ArrayList<Move>();
for(int i = start.getNumPiles() - 1; i >= 0; i--)
{
for(long j = start.getCountForPile(i); j > 0; j--)
{
Move newMove = new Move(i, j);
successors.add(newMove);
}
}
return successors;
}
}
public class NIMBoard
{
/**
* We use 4 bits to store the number of sticks which gives us these
* maximums:
* - 16 piles
* - 15 sticks per pile
*/
private static int PILE_BIT_SIZE = 4;
private long pos;
private int numPiles;
private long pileMask;
/**
* Instantiate a new NIM board
* @param pos Number of sticks in each pile
* @param numPiles Number of piles
*/
public NIMBoard(long pos, int numPiles)
{
super();
this.pos = pos;
this.numPiles = numPiles;
this.pileMask = (long) Math.pow(2, NIMBoard.PILE_BIT_SIZE) - 1;
}
/**
* Is this an endgame board?
* @return true if there's only one stick left
*/
public boolean isFinal()
{
return this.onePileHasOnlyOneStick();
}
/**
* Figure out if the board has a pile with only one stick in it
* @return true if yes
*/
public boolean onePileHasOnlyOneStick()
{
int count = 0;
for(int i = 0; i < this.numPiles; i++)
{
count += this.getCountForPile(i);
}
if(count > 1)
{
return false;
}
return true;
}
public int getNumPiles()
{
return this.numPiles;
}
public long getPos()
{
return this.pos;
}
public long getCountInPile(int pile)
{
return this.pos & (this.pileMask << (pile * NIMBoard.PILE_BIT_SIZE));
}
public long getCountForPile(int pile)
{
return this.getCountInPile(pile) >> (pile * NIMBoard.PILE_BIT_SIZE);
}
public void parseMove(Move move)
{
this.pos = this.pos - (move.getCount() << (move.getPile() * NIMBoard.PILE_BIT_SIZE));
}
@Override
public String toString()
{
String tmp = "";
for(int i = 0; i < this.numPiles; i++)
{
tmp += "Row " + i + "\t";
for(int j = 0; j < this.getCountForPile(i); j++)
{
tmp += "*";
}
tmp += System.lineSeparator();
}
return tmp.trim();
}
}
最佳答案
您认为对 AI 来说更好的一步实际上并不是更好的一步。在那种棋盘情况下,人类玩家会从第 1 行拿走两根棍子,而计算机仍然在拿最后一根棍子。这并不能保证你的程序能正常工作,但我认为你应该尝试一些不同的测试用例。例如,如果您给它假设人类玩家会输的情况,看看 AI 会怎么做。
关于java - 使用 Minimax 算法的 NIM 游戏和 AI 玩家 - AI 会输棋,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32773102/
以下代码无法编译,但说明了我想做的事情:totalTests 应该保存调用 assertEquals() 的次数(assertEquals() 可能应该是一个宏才能实现这一点,但我不熟悉Nim 的这方
macros 中有可用的解析器包,如 parseExpr和 parseStmt但他们是{.compileTime.}过程。 有没有办法在运行时解析一串 Nim 代码,产生可以分析的 AST? 最佳答案
是否可以为字符串定义类似枚举的类型?我知道枚举,但在这种情况下不想使用枚举,我想使用它就好像它只是一个字符串一样。 type Blog = object text: string pr
我目前正在尝试计算我请求某个网站的次数。在 python 中,我只会使用一个全局变量,但我不知道如何在 nim 中编写它。 import httpclient proc threadMain(a: i
我正在尝试学习 Nim 及其特性,例如迭代器;我发现下面的例子运行良好。 for i in countup(1,10): # Or its equivalent 'for i in 1..10:'
我一直在读到 Nim 的内存管理是可选的,但文档似乎很薄,我发现的唯一资源主要与 FFI 到 C 和 https://nim-lang.org/docs/gc.html 相关。 除了编写新的 GC 之
我是 Nim 的新手,所以这可能是一个迟钝的问题,但是如何创建一个速记别名变量来简化代码? 例如: import sdl2 import sdl2.gfx type Vector[T] = obj
有没有可能在 Nim 中获取前 N 个元素?像这样的东西: let [a, b, ...rest] = "a/b/c".split("/") 附言 用例我正在尝试解析“NYSE:MSFT”字符串 pr
是的,我们可以通过 Sublime、VS-Code 等将制表符转换为空格,这不是什么大问题。 但是如果我想摆脱这个额外的 Action 怎么办? Found通过将此行添加到 .nim 文件来回答: #
像 Rust 这样的语言有 Cargo,它通过调用 cargo new 来构建一个新项目。 . Nim 语言中是否有类似的工具或命令可用于搭建新项目?符合 new 的内容? 最佳答案 Nimbl
我想在 Nim 中不跳行地打印。 这是我的代码 int i = 1 for i in countup(1, 10): echo "number: " echo i 我希望输出为: number
假设我们有两个模块:一个定义一个 Object,一个定义一个 ObjectFactory。 Object 需要访问 ObjectFactory 才能使用它的一些功能,并且 ObjectFactory
假设我有一个程序 getTuple(): (int, int, int) .如何迭代返回的元组?看起来不像 items定义为 tuple ,所以我做不到 for i in getTuple() . 我
是否有任何常量变量或proc允许以字符串或数字形式访问编译器版本? 最佳答案 The version can be obtained as a string通过 system.NimVersion (
我将如何通过 nimscript 从标准输入读取? 我试过: if readLine(stdin) == "yes": exec buildCommand 我已经用 运行了脚本 nim c bu
在 NIM 0.17 中 toLower 已弃用。那么,在 NIM 中更改字符串大小写的正确方法是什么? 最佳答案 strutils.toLowerAscii 或 unicode.toLower 取决
我创造了一个敏捷 图书馆按照 documentation 包装.当我尝试使用 nimble build 构建它时我收到以下错误。 Error: Nothing to build. Did you sp
这是getStackTrace()从当前异常获取堆栈跟踪的函数。 但它不适用于特定的异常,此代码不起作用 error.getStackTrace()我需要它 log功能 proc error*(mes
下面的代码将无法编译,因为 let a 的重新声明多变的。 但是如果第二个test模板注释掉它会起作用。 为什么它会这样工作,以及如何解决它? playground template test*(na
如果我运行 nim c -r test.nim Nim 中的以下代码 echo "Hi" 它将打印带有附加信息的结果 $ nim c -r test.nim Hint: used config fil
我是一名优秀的程序员,十分优秀!