KoTH:Gomoku(连续5个)


10

五子棋连续五人棋盘游戏是由两个玩家在网格中用黑白石头玩的。能够连续放置5块石头(水平,垂直或对角线)的人将赢得比赛。15×155

规则

在此KoTH中,我们将玩Swap2规则,这意味着游戏包含两个阶段:在初始阶段,两个玩家确定谁先打谁/谁打黑,然后从玩家开始每回合放置一块石头谁选了黑色。

初始阶段

让玩家成为ABA将打开游戏:

  • A在板上放置了两个黑色和白色的石头
  • B可以选择以下三个动作之一:
    • 玩家B决定玩黑棋:初始阶段结束
    • 玩家B决定放置白色石头并进行白色游戏:初始阶段结束
    • 玩家B决定玩一个黑色和白色的石头:A可以选择颜色

游戏阶段

每位玩家将一块彩色的棋子放在棋盘上,从扮演黑色的棋手开始,直到没有更多的可用空间(在这种情况下是平局)或一位棋手设法在棋盘中玩枚棋子为止行(在这种情况下该玩家获胜)。5

行表示水平,垂直或对角线。胜利就是胜利-玩家是否得分超过一排并不重要。

KoTH游戏规则

  • 每个玩家对战两次:
    • 最初将随机决定谁先
    • 在接下来的游戏中,最后玩的玩家先行
  • 一场胜利值得2分,平局1输0
  • 目标是获得尽可能多的分数

你的机器人

为了使这一挑战适用于尽可能多的语言,将通过stdin / stdout(基于行)来进行输入 / 输出。判断程序将通过在您的机器人的stdin上打印一行来提示您的程序,而您的机器人将在stdout上打印一行。

收到EXIT消息后,您将有半秒钟的时间来完成对文件的写入,然后法官将终止该过程。

随机性

为了使锦标赛可验证,裁判使用种子随机化,并且出于同样的原因,您的机器人也必须这样做。该机器人将通过应使用的命令行参数获得种子,请参考下一节。

争论

该漫游器接收两个命令行参数:

  1. 对手的名字
  2. 随机种子

用户状态

由于您的程序始终会针对每个游戏重新启动,因此您需要使用文件来保存要保留的所有信息。您可以在当前目录中读取/写入任何文件或创建/删除子文件夹。您无权访问任何父目录中的任何文件!

输入/输出格式

BOARD((X,Y),COLOR)XY[015COLOR"B""W"

SPXY(X,Y)[015|

在初始阶段,有三种不同类型的消息:

Prompt (judge) -> Answer (bot)
"A" SP "[]"  -> XY XY XY
"B" SP BOARD -> "B" | "W" SP XY | XY XY
"C" SP BOARD -> "B" | "W"
  • 第一个消息要求三个元组,前两个将是黑色石头的位置,第三个是白色石头的位置。
  • 第二条消息要求:
    • "B" ->选黑
    • "W" SP XY ->选择白色并在上面放置白色石头 XY
    • XY XY ->放置两块石头(第一块黑色和第二块白色)
  • 最后一个只是询问您要播放哪种颜色

之后,常规游戏将开始,消息将变得更加简单

N BOARD -> XY

N0XY


还有另外一条消息,它不希望得到答案

"EXIT" SP NAME | "EXIT TIE"

NAME获胜机器人的名称在哪里。如果游戏由于没有人获胜而没有更多的可用空间放置石头而结束,则将发送第二条消息(这意味着您的机器人无法命名TIE)。

格式化

由于漫游器发出的消息可以不带任何空格地进行解码,因此所有空格都将被忽略(例如(0 , 0) (0,12),与相同(0,0)(0,12))。法官发来的消息仅包含一个空格,用于分隔不同的部分(即,如上文中用标记的SP),您可以将行分隔为空格。

任何无效的回复都会导致该回合失败(您仍然会收到一条EXIT消息),请参阅规则。

以下是一些实际消息的示例:

A []
B [((0,0),"B"),((0,1),"W"),((14,14),"B")]
1 [((0,0),"B"),((0,1),"W"),((1,0),"B"),((1,1),"W"),((14,14),"B")]

法官

您可以在此处找到评判程序:要在其中添加bot,只需在该bots文件夹中创建一个新文件夹,然后将文件放在其中,然后添加一个meta包含namecommandarguments和标志0/1的文件(禁用/启用 stderr)的文件。在单独的行上。

要运行锦标赛,只需运行./gomoku并调试单个机器人运行./gomoku -d BOT

注意:您可以在Github存储库中找到有关如何设置和使用评判器的更多信息。还有三个示例机器人(HaskellPythonJavaScript))。

规则

  • 每次机器人更改*比赛将重新进行,并且得分最高的玩家将获胜(抢先赛是平局)
  • 您可以提交多个机器人,只要它们没有采取共同的策略
  • 您不得触摸目录外的文件(例如,操作其他播放器的文件)
  • 如果您的漫游器崩溃或发送无效响应,则当前游戏将终止,您将失去该回合
  • 尽管法官(目前)没有对每一轮实施时间限制,但建议您将花费的时间保持在低水平,因为这可能无法测试所有提交的内容 **
  • 法官程序中的滥用错误被视为漏洞

*建议您使用Github直接在bots目录中单独提交您的bot (并可能进行修改util.sh)!

**万一出现问题,您会收到通知,我想说500ms以下的任何东西(很多!)现在都可以。

聊天室

如果您有任何疑问或想谈论此KoTH,请随时加入聊天室



在您的示例中包含空格,然后是元空格字符,这真让我大吃一惊。一些更多的例子将是不错的。
维斯卡

@Veskah:链接了三个示例漫游器,我将添加一些消息示例。
ბიმო

@Veskah:添加了一些示例。顺便说一句。您还可以尝试调试示例漫游器,以查看它们将采用的格式并测试有效的响应。
ბიმო

您没有授予推送权限,因此我无法将我的机器人推送到git
Kaito Kid,

Answers:


3

KaitoBot

它使用MiniMax原理的非常粗糙的实现。搜索的深度也很低,因为否则它将花费太长时间。

可能需要编辑以以后改进。

如果可能的话,它也尝试玩Black,因为维基百科似乎说Black有优势。

我本人从未玩过Gomoku,因此由于缺乏更好的主意,我随机设置了前三块石头。

const readline = require('readline');
const readLine = readline.createInterface({ input: process.stdin });

var debug = true;
var myColor = '';
var opponentColor = '';
var board = [];
var seed = parseInt(process.argv[3]);

function random(min, max) {
    changeSeed();
    var x = Math.sin(seed) * 10000;
    var decimal = x - Math.floor(x);
    var chosen = Math.floor(min + (decimal * (max - min)));
    return chosen;
}

function changeSeed() {
    var x = Math.sin(seed++) * 10000;
    var decimal = x - Math.floor(x);
    seed = Math.floor(100 + (decimal * 9000000));
}

function KaitoBot(ln) {
    var ws = ln.split(' ');

    if (ws[0] === 'A') {
        // Let's play randomly, we don't care.
        var nums = [];
        nums[0] = [ random(0, 15), random(0, 15) ];
        nums[1] = [ random(0, 15), random(0, 15) ];
        nums[2] = [ random(0, 15), random(0, 15) ];
        while (nums[1][0] == nums[0][0] && nums[1][1] == nums[0][1])
        {
            nums[1] = [ random(0, 15), random(0, 15) ];
        }
        while ((nums[2][0] == nums[0][0] && nums[2][1] == nums[0][1]) || (nums[2][0] == nums[1][0] && nums[2][1] == nums[1][1]))
        {
            nums[2] = [ random(0, 15), random(0, 15) ];
        }
        console.log('(' + nums[0][0] + ',' + nums[0][1] + ') (' + nums[1][0] + ',' + nums[1][1] + ') (' + nums[2][0] + ',' + nums[2][1] + ')');
    }
    else if (ws[0] === 'B') {
        // we're second to play, let's just pick black
        myColor = 'B';
        opponentColor = 'W';
        console.log('B');
    }
    else if (ws[0] === 'C') {
        // the other player chose to play 2 stones more, we need to pick..
        // I would prefer playing Black
        myColor = 'B';
        opponentColor = 'W';
        console.log('B');
    }
    else if (ws[0] === 'EXIT') {
        process.exit();
    }
    else {
        board = [];
        var json = JSON.parse(ws[1].replace(/\(\(/g,'{"xy":[')
                .replace(/"\)/g,'"}')
                .replace(/\),/g,'],"colour":'));
        // loop over all XYs and make a board object I can use
        for (var x = 0; x < 15; x++) {
            var newRow = []
            for (var y = 0; y < 15; y++) {
                var contains = false;
                json.forEach(j => {
                    if (j.xy[0] == x && j.xy[1] == y) {
                        contains = true;
                        newRow[newRow.length] = j.colour;
                    }
                });
                if (!contains) {
                    newRow[newRow.length] = ' ';
                }
            }
            board[board.length] = newRow;
        }
        // If we never picked Black, I assume we're White
        if (myColor == '') {
            myColor = 'W';
            opponentColor = 'B';
        }
        var bestMoves = ChooseMove(board, myColor, opponentColor);
        var chosenMove = bestMoves[random(0, bestMoves.length)];
        console.log('(' + chosenMove.X + ',' + chosenMove.Y + ')');
    }
}

function IsSquareRelevant(board, x, y) {
    return (board[x][y] == ' ' && 
        ((x > 0 && board[x - 1][y] != ' ') 
        || (x < 14 && board[x + 1][y] != ' ') 
        || (y > 0 && board[x][y - 1] != ' ') 
        || (y < 14 && board[x][y + 1] != ' ')
        || (x > 0 && y > 0 && board[x - 1][y - 1] != ' ') 
        || (x < 14 && y < 14 && board[x + 1][y + 1] != ' ') 
        || (y > 0 && x < 14 && board[x + 1][y - 1] != ' ') 
        || (y < 14 && x > 0 && board[x - 1][y + 1] != ' ')));
}

function ChooseMove(board, colorMe, colorOpponent) {
    var possibleMoves = [];
    for (var x = 0; x < 15; x++) {
        for (var y = 0; y < 15; y++) {
            if (IsSquareRelevant(board, x, y)) {
                possibleMoves[possibleMoves.length] = {X:x, Y:y};
            }
        }
    }
    var bestValue = -9999;
    var bestMoves = [possibleMoves[0]];
    for (var k in possibleMoves) {
        var changedBoard = JSON.parse(JSON.stringify(board));
        changedBoard[possibleMoves[k].X][possibleMoves[k].Y] = colorMe;
        var value = analyseBoard(changedBoard, colorMe, colorOpponent, colorOpponent, 2);
        if (value > bestValue) {
            bestValue = value;
            bestMoves = [possibleMoves[k]];
        } else if (value == bestValue) {
            bestMoves[bestMoves.length] = possibleMoves[k];
        }
    }
    return bestMoves;
}

function analyseBoard(board, color, opponent, nextToPlay, depth) {
    var tBoard = board[0].map((x,i) => board.map(x => x[i]));
    var score = 0.0;
    for (var x = 0; x < board.length; x++) {
        var inARow = 0;
        var tInARow = 0;
        var opponentInARow = 0;
        var tOpponentInARow = 0;
        var inADiago1 = 0;
        var opponentInADiago1 = 0;
        var inADiago2 = 0;
        var opponentInADiago2 = 0;

        for (var y = 0; y < board.length; y++) {
            if (board[x][y] == color) {
                inARow++;
                score += Math.pow(2, inARow);
            } else {
                inARow = 0;
            }
            if (board[x][y] == opponent) {
                opponentInARow++;
                score -= Math.pow(2, opponentInARow);
            } else {
                opponentInARow = 0;
            }
            if (tBoard[x][y] == color) {
                tInARow++;
                score += Math.pow(2, tInARow);
            } else {
                tInARow = 0;
            }
            if (tBoard[x][y] == opponent) {
                tOpponentInARow++;
                score -= Math.pow(2, tOpponentInARow);
            } else {
                tOpponentInARow = 0;
            }

            var xy = (y + x) % 15;
            var xy2 = (x - y + 15) % 15;
            if (xy == 0) {
                inADiago1 = 0;
                opponentInADiago1 = 0;
            }
            if (xy2 == 0) {
                inADiago2 = 0;
                opponentInADiago2 = 0;
            }

            if (board[xy][y] == color) {
                inADiago1++;
                score += Math.pow(2, inADiago1);
            } else {
                inADiago1 = 0;
            }
            if (board[xy][y] == opponent) {
                opponentInADiago1++;
                score -= Math.pow(2, opponentInADiago1);
            } else {
                opponentInADiago1 = 0;
            }
            if (board[xy2][y] == color) {
                inADiago2++;
                score += Math.pow(2, inADiago2);
            } else {
                inADiago2 = 0;
            }
            if (board[xy2][y] == opponent) {
                opponentInADiago2++;
                score -= Math.pow(2, opponentInADiago2);
            } else {
                opponentInADiago2 = 0;
            }


            if (inARow == 5 || tInARow == 5) {
                return 999999999.0;
            } else if (opponentInARow == 5 || tOpponentInARow == 5) {
                return -99999999.0;
            }
            if (inADiago1 == 5 || inADiago2 == 5) {
                return 999999999.0;
            } else if (opponentInADiago1 == 5 || opponentInADiago2 == 5) {
                return -99999999.0;
            }
        }
    }

    if (depth > 0) {
        var bestMoveValue = 999999999;
        var nextNextToPlay = color;
        if (nextToPlay == color) {
            nextNextToPlay = opponent;
            bestMoveValue = -999999999;
        }
        for (var x = 0; x < board.length; x++) {
            for (var y = 0; y < board.length; y++) {
                if (IsSquareRelevant(board, x, y)) {
                    var changedBoard = JSON.parse(JSON.stringify(board));
                    changedBoard[x][y] = nextToPlay;
                    var NextMoveValue = (analyseBoard(changedBoard, color, opponent, nextNextToPlay, depth - 1) * 0.1);

                    if (nextToPlay == color) {
                        if (NextMoveValue > bestMoveValue) {
                            bestMoveValue = NextMoveValue;
                        }
                    } else {
                        if (NextMoveValue < bestMoveValue) {
                            bestMoveValue = NextMoveValue;
                        }
                    }
                }
            }
        }
        score += bestMoveValue * 0.1;
    }
    return score;
}

readLine.on('line', (ln) => {

    KaitoBot(ln);

});

编辑:使种子动态变化,因为否则种子超过2 ^ 52时javascript无法正确处理增量

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.