<commit_before>const EventEmitter = require('events')
const {dirname, resolve} = require('path')
const gtp = require('@sabaki/gtp')
const sgf = require('@sabaki/sgf')
const argvsplit = require('argv-split')
const gametree = require('./gametree')
const helper = require('./helper')
const Board = require('./board')

const alpha = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'
const defaultStateJSON = JSON.stringify({
    dirty: true,
    komi: null,
    size: null,
    moves: []
})

function coord2vertex(coord, size) {
    if (coord === 'pass') return null

    let x = alpha.indexOf(coord[0].toUpperCase())
    let y = size - +coord.slice(1)

    return [x, y]
}

class EngineSyncer extends EventEmitter {
    constructor(engine) {
        super()

        let {path, args, commands} = engine

        this.engine = engine
        this.commands = []
        this.state = JSON.parse(defaultStateJSON)

        this.controller = new gtp.Controller(path, argvsplit(args), {
            cwd: dirname(resolve(path))
        })

        this.controller.on('started', () => {
            Promise.all([
                this.controller.sendCommand({name: 'name'}),
                this.controller.sendCommand({name: 'version'}),
                this.controller.sendCommand({name: 'protocol_version'}),
                this.controller.sendCommand({name: 'list_commands'}).then(response => {
                    this.commands = response.content.split('\n')
                }),
                ...(
                    commands != null
                    && commands.trim() !== ''
                    ? commands.split(';').filter(x => x.trim() !== '').map(command =>
                        this.controller.sendCommand(gtp.Command.fromString(command))
                    )
                    : []
                )
            ]).catch(helper.noop)
        })

        this.controller.on('stopped', () => {
            this.state = JSON.parse(defaultStateJSON)
        })

        this.controller.on('command-sent', async ({command, getResponse, subscribe}) => {
            // Track engine state

            let res = null

            if (!['lz-genmove_analyze', 'genmove_analyze'].includes(command.name)) {
                try {
                    res = await getResponse()
                    if (res.error) return
                } catch (err) {
                    return
                }
            }

            if (command.name === 'boardsize' && command.args.length >= 1) {
                this.state.size = +command.args[0]
                this.state.dirty = true
            } else if (command.name === 'clear_board') {
                this.state.moves = []
                this.state.dirty = false
            } else if (command.name === 'komi' && command.args.length >= 1) {
                this.state.komi = +command.args[0]
            } else if (['fixed_handicap', 'place_free_handicap'].includes(command.name)) {
                let vertices = res.content.trim().split(/\s+/)
                    .map(coord => coord2vertex(coord, this.state.size))
                    .filter(x => x != null)

                if (vertices.length > 0) this.state.moves.push({sign: 1, vertices})
            } else if (command.name === 'set_free_handicap') {
                let vertices = command.args
                    .map(coord => coord2vertex(coord, this.state.size))
                    .filter(x => x != null)

                if (vertices.length > 0) this.state.moves.push({sign: 1, vertices})
            } else if (command.name === 'play' && command.args.length >= 2) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = coord2vertex(command.args[1], this.state.size)

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (command.name === 'genmove' && command.args.length >= 1) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = coord2vertex(res.content.trim(), this.state.size)

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (
                ['lz-genmove_analyze', 'genmove_analyze'].includes(command.name)
                && command.args.length >= 1
            ) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = await new Promise(resolve => {
                    getResponse()
                    .then(() => resolve(null))
                    .catch(() => resolve(null))

                    subscribe(({line}) => {
                        let match = line.trim().match(/^play (.*)$/)
                        if (match) resolve(coord2vertex(match[1], this.state.size))
                    })
                })

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (command.name === 'undo') {
                this.state.moves.length -= 1
            } else if (command.name === 'loadsgf') {
                this.state.dirty = true
            }
        })
    }
<commit_msg>Add busy-changed event to EngineSyncer<commit_after>const EventEmitter = require('events')
const {dirname, resolve} = require('path')
const gtp = require('@sabaki/gtp')
const sgf = require('@sabaki/sgf')
const argvsplit = require('argv-split')
const gametree = require('./gametree')
const helper = require('./helper')
const Board = require('./board')

const alpha = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'
const defaultStateJSON = JSON.stringify({
    dirty: true,
    komi: null,
    size: null,
    moves: []
})

function coord2vertex(coord, size) {
    if (coord === 'pass') return null

    let x = alpha.indexOf(coord[0].toUpperCase())
    let y = size - +coord.slice(1)

    return [x, y]
}

class EngineSyncer extends EventEmitter {
    constructor(engine) {
        super()

        let {path, args, commands} = engine

        this._busy = false
        this.engine = engine
        this.commands = []
        this.state = JSON.parse(defaultStateJSON)

        this.controller = new gtp.Controller(path, argvsplit(args), {
            cwd: dirname(resolve(path))
        })

        this.controller.on('started', () => {
            Promise.all([
                this.controller.sendCommand({name: 'name'}),
                this.controller.sendCommand({name: 'version'}),
                this.controller.sendCommand({name: 'protocol_version'}),
                this.controller.sendCommand({name: 'list_commands'}).then(response => {
                    this.commands = response.content.split('\n')
                }),
                ...(
                    commands != null
                    && commands.trim() !== ''
                    ? commands.split(';').filter(x => x.trim() !== '').map(command =>
                        this.controller.sendCommand(gtp.Command.fromString(command))
                    )
                    : []
                )
            ]).catch(helper.noop)
        })

        this.controller.on('stopped', () => {
            this.state = JSON.parse(defaultStateJSON)
        })

        this.controller.on('command-sent', async ({command, getResponse, subscribe}) => {
            // Track engine state

            let res = null

            this.busy = this.controller.busy

            if (!['lz-genmove_analyze', 'genmove_analyze'].includes(command.name)) {
                try {
                    res = await getResponse()
                    if (res.error) return
                } catch (err) {
                    return
                }
            }

            if (command.name === 'boardsize' && command.args.length >= 1) {
                this.state.size = +command.args[0]
                this.state.dirty = true
            } else if (command.name === 'clear_board') {
                this.state.moves = []
                this.state.dirty = false
            } else if (command.name === 'komi' && command.args.length >= 1) {
                this.state.komi = +command.args[0]
            } else if (['fixed_handicap', 'place_free_handicap'].includes(command.name)) {
                let vertices = res.content.trim().split(/\s+/)
                    .map(coord => coord2vertex(coord, this.state.size))
                    .filter(x => x != null)

                if (vertices.length > 0) this.state.moves.push({sign: 1, vertices})
            } else if (command.name === 'set_free_handicap') {
                let vertices = command.args
                    .map(coord => coord2vertex(coord, this.state.size))
                    .filter(x => x != null)

                if (vertices.length > 0) this.state.moves.push({sign: 1, vertices})
            } else if (command.name === 'play' && command.args.length >= 2) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = coord2vertex(command.args[1], this.state.size)

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (command.name === 'genmove' && command.args.length >= 1) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = coord2vertex(res.content.trim(), this.state.size)

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (
                ['lz-genmove_analyze', 'genmove_analyze'].includes(command.name)
                && command.args.length >= 1
            ) {
                let sign = command.args[0][0].toLowerCase() === 'w' ? -1 : 1
                let vertex = await new Promise(resolve => {
                    getResponse()
                    .then(() => resolve(null))
                    .catch(() => resolve(null))

                    subscribe(({line}) => {
                        let match = line.trim().match(/^play (.*)$/)
                        if (match) resolve(coord2vertex(match[1], this.state.size))
                    })
                })

                if (vertex) this.state.moves.push({sign, vertex})
            } else if (command.name === 'undo') {
                this.state.moves.length -= 1
            } else if (command.name === 'loadsgf') {
                this.state.dirty = true
            }
        })

        this.controller.on('response-received', () => {
            this.busy = this.controller.busy
        })
    }

    get busy() {
        return this._busy
    }

    set busy(value) {
        if (value !== this._busy) {
            this._busy = value
            this.emit('busy-changed')
        }
    }

{'fraction_non_alphanumeric': 0.14816928197812648, 'fraction_numerical': 0.006466951973371374, 'mean_word_length': 1.5856896975657733, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5420665', 'n_tokens_mistral': 3023, 'n_tokens_neox': 2855, 'n_words': 569}