Skip to content

Instantly share code, notes, and snippets.

@hyperbotauthor
Last active October 22, 2020 14:14
Show Gist options
  • Save hyperbotauthor/dd7544502c3f44988d26fd0954504626 to your computer and use it in GitHub Desktop.
Save hyperbotauthor/dd7544502c3f44988d26fd0954504626 to your computer and use it in GitHub Desktop.
pondering uci engine
const path = require('path')
class UciEngine{
constructor(path){
this.process = require('child_process').spawn(path)
this.buffer = ""
this.process.stdout.on('data', data =>{
let content = data.toString()
//console.log(`out: ${content}`)
this.buffer += content
if(this.buffer.match(/\n/)){
let lines = this.buffer.split("\n")
this.buffer = lines.pop()
for(let line of lines) this.processLine(line)
}
})
this.process.stderr.on('data', data =>{
let content = data.toString()
console.log(`err: ${content}`)
})
this.initInfo()
}
processLine(line){
if(this.logProcessLine) console.log(`processing ( ${line.length} ): ${line}`)
if(line.match(/^info string/)) return
let m
if(m=line.match(/depth (-?\d+)/)){
this.currentDepth = parseInt(m[1])
}
while(this.info.depthInfos.length <= this.currentDepth){
this.info.depthInfos.push({})
}
this.info.depthInfos[this.currentDepth].depth = this.currentDepth
if(m=line.match(/time (\d+)/)){
this.info.depthInfos[this.currentDepth].time = parseInt(m[1])
}
if(m=line.match(/score cp (-?\d+)/)){
this.info.depthInfos[this.currentDepth].score = {unit: "cp", value: parseInt(m[1])}
}
if(m=line.match(/score mate (-?\d+)/)){
this.info.depthInfos[this.currentDepth].score = {unit: "mate", value: parseInt(m[1])}
}
if(m=line.match(/ pv (.*)$/)){
this.info.depthInfos[this.currentDepth].pv = m[1]
this.info.depthInfos[this.currentDepth].pvMoves = m[1].split(" ")
}
if(line.match(/^bestmove/)){
if(m=line.match(/^bestmove ([^\s]+) ponder ([^\s]+)/)){
this.info.bestmove = m[1]
this.info.ponder = m[2]
}else if(m=line.match(/^bestmove ([^\s]+)/)){
this.info.bestmove = m[1]
this.info.ponder = null
}
let elapsed = Math.floor(new Date().getTime() - this.startedThinkingAt) + 1
console.log(`thinking took ${elapsed}`)
if(this.onbestmove){
this.onbestmove()
}
if(this.ponderAfter){
if(this.info.ponder){
if(this.positionSet.moves.length % 2){
this.timecontrol.btime -= elapsed
}else{
this.timecontrol.wtime -= elapsed
}
this.position(this.positionSet.specifier, this.positionSet.moves.concat([this.info.bestmove, this.info.ponder]))
this.go({wtime: this.timecontrol.wtime, winc: this.timecontrol.winc, btime: this.timecontrol.btime, binc: this.timecontrol.binc, ponder: true})
}
}
}
}
issueCommand(command){
this.process.stdin.write(`${command}\n`)
console.log(`issue command: ${command}`)
return this
}
quit(){
console.log(`quitting engine`)
return this.issueCommand("quit")
}
setoption(key, value){
if(this.pondering){
console.log(`refused to set option ${key} to ${value} during pondering`)
return this
}
return this.issueCommand(`setoption name ${key} value ${value}`)
}
position(specifier, moves){
let command = `position ${specifier}`
if(moves) command += ` moves ${moves.join(" ")}`
this.positionSet = {
specifier: specifier,
moves: moves || []
}
if(this.pondering) return this
return this.issueCommand(command)
}
initInfo(){
this.currentDepth = 0
this.info = {
bestmove: null,
ponder: null,
depthInfos: [{}]
}
}
go(props){
this.onbestmove = props.onbestmove
if(this.pondering){
if((this.positionSet.specifier == this.pondering.specifier)&&(this.positionSet.moves.join(" ") == this.pondering.moves.join(" "))){
this.pondering = false
this.ponderhit(this.onbestmove)
}else{
console.log(`pondermiss`)
this.pondering = false
this.stop(_=>{
this.position(this.positionSet.specifier, this.positionSet.moves)
this.go(props)
})
}
return
}
let command = `go`
const keys = ["depth", "wtime", "winc", "btime", "binc"]
this.timecontrol = {}
for(let key of keys){
if(typeof props[key] != "undefined"){
command += ` ${key} ${props[key]}`
this.timecontrol[key] = props[key]
}
}
this.pondering = false
if(props.ponder){
command += ` ponder`
this.pondering = this.positionSet
}
this.initInfo()
this.ponderAfter = props.ponderAfter
this.startedThinkingAt = new Date().getTime()
return this.issueCommand(command)
}
ponderhit(onbestmove){
this.onbestmove = onbestmove
console.log(`ponderhit`)
return this.issueCommand(`ponderhit`)
}
stop(onbestmove){
this.onbestmove = onbestmove
return this.issueCommand(`stop`)
}
gothen(props){
return new Promise(resolve=>{
this.go({...props, ...{onbestmove: _=>resolve(this.info)}})
})
}
}
module.exports = UciEngine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment