JAVA - Go游戏算法

时间:2012-04-10 10:36:00

标签: java algorithm baduk

我正在尝试实施一种算法来清除我的Go游戏中的死石。

我听说洪水填充是最好的,因为递归使用它会更有效,更容易实现。

我在代码中使用它时遇到了麻烦,并且想知道我应该如何实现它。

这是我的课程之一,非常自我解释。

import java.io.*;

public class GoGame implements Serializable {

    int size;
    char[][] pos; // This is the array that stores whether a Black (B) or White (W) piece is stored, otherwise its an empty character.

    public GoGame(int s){
        size = s;
    }

    public void init() {
        pos = new char[size][size];
        for (int i=0;i<size;i++) {
            for (int j=0;j<size;j++) {
                pos[i][j] = ' ';
            }
        }
    }

    public void ClearAll() {
        for (int i=0;i<size;i++) {
            for (int j=0;j<size;j++) {
                pos[i][j] = ' ';
            }
        }
    }

    public void clear(int x, int y) {
        pos[x][y]=' ';
    }

    public void putB(int x, int y) { //places a black stone on the board+array
        pos[x][y]='B';
        floodfill(x,y,'B','W');

    }

    public void putW(int x, int y) { //places a white stone on the board+array
        pos[x][y]='W';
        floodfill(x,y,'W','B');

    }

    public char get(int x, int y) {
        return pos[x][y];
    }

    public void floodfill(int x, int y, char placed, char liberty){


        floodfill(x-1, y, placed, liberty);
        floodfill(x+1, y, placed, liberty);
        floodfill(x, y-1, placed, liberty);
        floodfill(x, y+1, placed, liberty);

    }

}

xy是方格的坐标,placed是放下的石头的字符,liberty是另一个字符

任何帮助都会很棒!

4 个答案:

答案 0 :(得分:2)

虽然其他答案在技术上是正确的,但您也缺少与go相关的更多逻辑。你需要做的是,我认为(在B行动中):

for each W neighbour of the move:
   check that W group to see if it has any liberties (spaces)
       remove it if not

洪水填充对于找到一组石头的范围很有用,但是你的日常工作需要的不仅仅是那些(我在这里简化,并试图猜测这个例程的用途 - 请参阅下面的评论这个答案)。

鉴于上述情况,识别群组中所有宝石的洪水填充将是这样的(请注意,它使用第二个阵列进行填充,因为您不希望仅更改pos找一个小组):

public void findGroup(int x, int y, char colour, char[][] mask) {
    // if this square is the colour expected and has not been visited before
    if (pos[x][y] == colour && mask[x][y] == ' ') {
        // save this group member
        mask[x][y] = pos[x][y];
        // look at the neighbours
        findGroup(x+1, y, colour, mask);
        findGroup(x-1, y, colour, mask);
        findGroup(x, y+1, colour, mask);
        findGroup(x, y-1, colour, mask);
    }
}

您可以调用它来识别单个组(并将其复制到掩码中),这样它将帮助您识别与B移动相邻的W组的成员(例如),但它只是一小部分你需要的总逻辑。

最后,请注意,如果你想对一组中的每一块石头做些什么,你有两个选择。您可以调用上面的例程,然后循环mask以查找该组,或者您可以将您想要执行的操作直接放在例程中(在这种情况下,您仍然使用mask控制测试&& mask[x][y] == ' '中洪水填充的程度,但不要将其作为结果使用 - 所有工作都在例程返回时完成。

(按照所有规则编程要正确处理的东西,实际上非常复杂 - 你需要做很多工作......:o)

答案 1 :(得分:0)

我会使用虚假证明。以下是我找到捕获的石头的方法:

private static final int SIZE = 8;
private static final int VACANT = 0;       //empty point
private static final int MY_COLOR = 1;     //Black
private static final int ENEMY_COLOR = 2;  //White
private static final int CHECKED = 50;     //Mark for processed points
private static final int OUT = 100;        //points out of the board

private static boolean isCaptured(int col, int row, int[][] board) {
    boolean result = !isNotCaptured(col, row, board);
    cleanBoard(board);
    return result;
}

private static boolean isNotCaptured(int col, int row, int[][] board) {
    int value = board[col][row];
    if (!(value == MY_COLOR || value == CHECKED))
        return true;

    int top = row < SIZE - 1 ? board[col][row + 1] : OUT;
    int bottom = row > 0 - 1 ? board[col][row - 1] : OUT;
    int left = col > 0 ? board[col - 1][row] : OUT;
    int right = col < SIZE - 1 ? board[col + 1][row] : OUT;

    if (top == VACANT || right == VACANT || left == VACANT || bottom == VACANT)
        return true;

    board[col][row] = CHECKED;

    return (top == MY_COLOR && isNotCaptured(col, row + 1, board))
            || (bottom == MY_COLOR && isNotCaptured(col, row - 1, board))
            || (left == MY_COLOR && isNotCaptured(col - 1, row, board))
            || (right == MY_COLOR && isNotCaptured(col + 1, row, board));
}

private static void cleanBoard(int[][] board) {
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == CHECKED)
                board[i][j] = MY_COLOR;
        }
    }
}

然后你可以调用这样的方法:

isCaptured(5, 4, board)

答案 2 :(得分:0)

我认为BFS对于这种情况会更好,因为你需要首先探索邻居,这样如果捕获了其中任何一个,那么捕获该点。

答案 3 :(得分:0)

正如其他人指出的那样,Go中也有一个“ ko规则”,这大致意味着您在捕获单个石头(简化)时不允许立即捕获。总之,您可能要为此使用现有的库。

我建议使用 brugo 存储库,该存储库在maven中可用。

<!-- https://mvnrepository.com/artifact/be.brugo/brugo -->
<dependency>
    <groupId>be.brugo</groupId>
    <artifactId>brugo</artifactId>
    <version>0.1.0</version>
</dependency>

大致如此。 (警告:代码未经测试)

// create a starting position
Position position = new Position(boardSize, komi);

// play a move
Intersection whereToPlay = Intersection.valueOf(4,4);
IntStatus colorToPlay = IntStatus.BLACK;
Position position2 = position.play(whereToPlay, colorToPlay);

// watch the result.
IntStatus[][] matrix = position2.getMatrix()

它还包含要导出到“加载/保存SGF”的对象。 SGF文件的加载不仅支持UTF-8,而且还支持亚洲编码。下面的屏幕快照显示了实现自己的难度:

A picture of some of the code

如果您还计划使用javafx,请运行此演示:brugo.go.ui.javafx.goban.GobanComponentDemo

足够让您入门。

关于布鲁戈的一些背景

http://www.brugo.be是一个PHP网站,大约在2008年左右创建,它是一个在线的joseki字典,显然需要一些代码才能使用Go职位。因此,所有这些都是用PHP编写的。大约一年后,它开始使用一些Java applet(当时还是“还可以”),这将使浏览位置更加容易。因此,用于go逻辑的代码已移植到Java。

后来,人们做出了一些努力,也将其用于AI和客户端/服务器应用程序,但没有成功。大约在这个时候,Java代码得到了重大改进,以支持不同的文件编码。

但是在2015年左右的某个地方,代码被移植到javascript,2017年左右被移植到打字稿,以尝试升级brugo网站。再次没有直接成功。

在2018年左右的某个时候,有计划将Java代码用于特定的开放源代码项目(再次与AI相关),但这没有成功。但是正是在那时,它才被集中在Maven中。

由于LeeLa Zero的成功,在这段时间里,开源Go项目大量涌现。这也是为什么将brugo的Java代码开源的原因。

2019年初http://www.zbaduk.com推出(现在是非官方的),它重新使用了BruGo的打字稿代码,并且还将LeeLa Zero项目用于AI。