go-engine-tester
Advanced tools
Comparing version 0.2.1 to 0.3.0
"use strict"; | ||
const {Controller, Command, Response} = require("@sabaki/gtp"); | ||
const got = require("got"); | ||
const fs = require("fs"); | ||
const crypto = require("crypto"); | ||
@@ -23,2 +25,3 @@ let args = process.argv.slice(); | ||
let times = config.times; | ||
let report = config.report; | ||
@@ -28,3 +31,4 @@ if (!( | ||
typeof config2.command === "string" && | ||
typeof times === "number" | ||
typeof times === "number" && times >= 0 && times <= 10000 && | ||
(report === undefined || (typeof report.minutes === "number" && report.minutes >= 1)) | ||
)) { | ||
@@ -38,2 +42,4 @@ console.error("Some config attributes are wrong."); | ||
let testerId = crypto.randomBytes(4).toString("hex"); | ||
let results = []; | ||
@@ -47,2 +53,19 @@ | ||
let convertMoveToSgf = move => { | ||
if (move === "pass") { | ||
return ""; | ||
} | ||
else { | ||
let part1 = move[0]; | ||
let part2 = move.substr(1); | ||
let r1 = part1; | ||
if (r1 >= "j") { | ||
r1 = String.fromCharCode(r1.charCodeAt(0) - 1); | ||
} | ||
let r2 = String.fromCharCode(116 - parseInt(part2)); | ||
let r = r1 + r2; | ||
return r; | ||
} | ||
}; | ||
let playGame = async swaps => { | ||
@@ -78,4 +101,5 @@ let controller1 = new Controller(config1.command, config1.args, config1.spawnOptions); | ||
let moves = []; | ||
let gtpMoves = []; | ||
let sgfMoves = []; | ||
let winnerColor = null; | ||
let sgfResult = null; | ||
@@ -89,11 +113,14 @@ while (true) { | ||
move = await sendCommand(controller, {name: "genmove", args: [color]}); | ||
move = move.toLowerCase(); | ||
console.log(moveIndex, color, move); | ||
moves.push(move); | ||
gtpMoves.push(`play ${color} ${move}`); | ||
if (move === "resign") { | ||
winnerColor = moveIndex % 2 === 0 ? "w" : "b"; | ||
sgfResult = winnerColor.toUpperCase() + "+R"; | ||
break; | ||
} | ||
moves.push(move); | ||
sgfMoves.push(";" + color.toUpperCase() + "[" + convertMoveToSgf(move) + "]"); | ||
if (move === "pass" && moves.length >= 2 && moves[moves.length - 2] === "pass") { | ||
winnerColor = (await sendCommand(controller, {name: "final_score"})).startsWith("B+") ? "b" : "w"; | ||
sgfResult = await sendCommand(controller, {name: "final_score"}); | ||
winnerColor = sgfResult.startsWith("B+") ? "b" : "w"; | ||
break; | ||
@@ -111,3 +138,6 @@ } | ||
gtpMoves.forEach(m => console.log(m)); | ||
let sgf = | ||
`(;GM[1]FF[4]CA[UTF-8]SZ[19]PB[${blackConfig.name}]PW[${whiteConfig.name}]RE[${sgfResult}]` + | ||
sgfMoves.join("") + ")"; | ||
console.log(sgf); | ||
console.log(`Winner: ${winnerConfig.name} ${winnerColor}`); | ||
@@ -119,4 +149,6 @@ | ||
let getFirstWinCount = () => results.filter(m => m.winnerConfig.name === "first").length; | ||
let printFirstWinRate = () => { | ||
let firstWinCount = results.filter(m => m.winnerConfig.name === "first").length; | ||
let firstWinCount = getFirstWinCount(); | ||
let winRate = (firstWinCount / results.length).toFixed(4); | ||
@@ -126,2 +158,14 @@ console.log(`First wins ${firstWinCount} out of ${results.length}. Win rate: ${winRate}`); | ||
let sendReport = () => { | ||
return got( | ||
`${report.uriPrefix}?testerId=${testerId}&firstWins=${getFirstWinCount()}&total=${results.length}`, | ||
{retry: 0, timeout: 30000} | ||
).catch(ex => console.log(ex)); | ||
}; | ||
let reportTimer = null; | ||
if (report !== undefined) { | ||
reportTimer = setInterval(sendReport, report.minutes * 60 * 1000); | ||
} | ||
(async () => { | ||
@@ -136,2 +180,7 @@ for (let i = 0; i < times; i++) { | ||
printFirstWinRate(); | ||
if (report !== undefined) { | ||
clearInterval(reportTimer); // avoid possible duplicate sending | ||
await sendReport(); | ||
} | ||
process.exit(); | ||
})(); |
{ | ||
"name": "go-engine-tester", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Go (围棋, 囲碁, 바둑) engine tester", | ||
"keywords": ["go", "engine", "test"], | ||
"keywords": ["go", "engine", "test", "gtp", "bot", "weiqi", "baduk", "game"], | ||
"author": "Zhenzhen Zhan <zhanzhenzhen@hotmail.com>", | ||
@@ -13,3 +13,4 @@ "license": "MIT", | ||
"dependencies": { | ||
"@sabaki/gtp": "1.x" | ||
"@sabaki/gtp": "1.x", | ||
"got": "9.x" | ||
}, | ||
@@ -16,0 +17,0 @@ "engines": { |
@@ -47,2 +47,13 @@ # Introduction | ||
You may do tests on a computer that can shut down at any random time. An example is EC2 Spot Instances. In this situation, you may want to send reports regularly to a server to prevent data loss. The optional `report` property is for that: | ||
``` | ||
{ | ||
... | ||
"report": {"minutes": 10, "uriPrefix": "https://example.com/report"} | ||
} | ||
``` | ||
Every 10 minutes, it will send an HTTP GET request to `https://example.com/report?testerId=<testerId>&firstWins=<firstWins>&total=<total>`, where `<testerId>` is a random (but fixed during the tester process) string. | ||
There's an optional `spawnOptions` property, so that it could pass environment variables to the engine processes. For example: | ||
@@ -49,0 +60,0 @@ |
11177
150
101
2
+ Addedgot@9.x
+ Added@sindresorhus/is@0.14.0(transitive)
+ Added@szmarczak/http-timer@1.1.2(transitive)
+ Addedcacheable-request@6.1.0(transitive)
+ Addedclone-response@1.0.3(transitive)
+ Addeddecompress-response@3.3.0(transitive)
+ Addeddefer-to-connect@1.1.3(transitive)
+ Addedduplexer3@0.1.5(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedget-stream@4.1.05.2.0(transitive)
+ Addedgot@9.6.0(transitive)
+ Addedhttp-cache-semantics@4.1.1(transitive)
+ Addedjson-buffer@3.0.0(transitive)
+ Addedkeyv@3.1.0(transitive)
+ Addedlowercase-keys@1.0.12.0.0(transitive)
+ Addedmimic-response@1.0.1(transitive)
+ Addednormalize-url@4.5.1(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedp-cancelable@1.1.0(transitive)
+ Addedprepend-http@2.0.0(transitive)
+ Addedpump@3.0.2(transitive)
+ Addedresponselike@1.0.2(transitive)
+ Addedto-readable-stream@1.0.0(transitive)
+ Addedurl-parse-lax@3.0.0(transitive)
+ Addedwrappy@1.0.2(transitive)