<commit_before>'use strict';

var CONST = require('../../constants');
var Grid = require('../../Grid');
var Heuristic = require('./Heuristic');
var inherits = require('util').inherits;
var log = require('../../log');

var ALWAYS = CONST.ALWAYS;

var MIN_FACTOR = 0.1; // REVIEW ME; under this factor we ignore threats for now


/** @class */
function MoveInfo(player) {
    Heuristic.call(this, player);

    this.debug = false;
    this.grid = new Grid(this.gsize, null);
    this.groupDeath = [];
    this.what = '';

    this.spaceInvasionCoeff = this.getGene('spaceInvasion', 2.0, 0.01, 4.0);
    this.pressureCoeff = this.getGene('pressure', 1, 0.01, 2);
    this.eyeCloserCoeff = this.getGene('eyeCloser', 1, 0.01, 1);
    this.wideSpaceToEyeCoeff = this.getGene('wideSpaceToEye', 0.5, 0.01, 1);
    this.potEyeCoeff2 = this.getGene('potEyeCoeff2', 0.78, 0.75, 0.85);
}
inherits(MoveInfo, Heuristic);
module.exports = MoveInfo;


MoveInfo.prototype.evalBoard = function () {
    this.debug = this.player.debugMode;
    this.grid.init(null);
    this.groupDeath.length = 0;
};

MoveInfo.prototype.collectScores = function (stateYx, scoreYx) {
    Heuristic.prototype.evalBoard.call(this, stateYx, scoreYx); // calls _evalMove below
};

MoveInfo.prototype._evalMove = function (i, j) {
    var cell = this.grid.yx[j][i];
    if (!cell) return 0;
    return cell.computeScore();
};

// Redefines Heuristic#getMoveSurvey
MoveInfo.prototype.getMoveSurvey = function (i, j, survey) {
    var cell = this.grid.yx[j][i];
    if (cell) cell.computeScore(survey, this.name);
};

function CellInfo() {
    this.fakeEyeForColor = null;
    this.score = 0;
    this.goals = [];
    this.goalFactors = [];
    this.goalNumMoves = [];
}

CellInfo.prototype.computeScore = function (survey, hName) {
    var score = this.score;
    if (survey && score) survey[hName + '-base'] = score;
    var goals = this.goals;
    if (!goals.length) return score;

    for (var n = goals.length - 1; n >= 0; n--) {
        var goal = goals[n];
        var goalScore = goal.score * this.goalFactors[n];
        score += goalScore;

        if (survey) {
            var f = this.goalFactors[n], factorStr = f !== 1 ? ' factor:' + f.toFixed(2) : '';
            survey[hName + '-' + goal + factorStr] = goalScore;
        }
    }
    return score;
};

MoveInfo.prototype.getCellInfo = function (i, j) {
    return this.grid.yx[j][i]; // can be null
};

MoveInfo.prototype._getCell = function (i, j) {
    var cell = this.grid.yx[j][i];
    if (cell) return cell;

    this.grid.yx[j][i] = cell = new CellInfo();
    return cell;
};

MoveInfo.prototype.setAsFakeEye = function (stone, color) {
    this._getCell(stone.i, stone.j).fakeEyeForColor = color;
};

MoveInfo.prototype.isFakeEye = function (stone, color) {
    var cell = this.getCellInfo(stone.i, stone.j);
    return cell && cell.fakeEyeForColor === color;
};


//---

function Goal(name, score, g) {
    this.name = name;
    this.score = score;
    this.group = g;
    this.minMoves = Infinity;
}

Goal.prototype.toString = function () {
    return this.name +
        (this.minMoves !== Infinity ? ' minMoves:' + this.minMoves : '') +
        ' score:' + this.score.toFixed(2);
};

//---

MoveInfo.prototype._enter = function (name, g, stone) {
    this.what = name + ' on ' + g.toString(1);

    if (stone.i === this.player.testI && stone.j === this.player.testJ) {
        log.setLevel(log.DEBUG); // set your breakpoint here if needed
        log.debug('MoveInfo scoring ' + stone + ': ' + this.what);
    } else {
        if (log.level === log.DEBUG) log.setLevel(log.INFO);
    }
};

MoveInfo.prototype._groupDeathGoal = function (g) {
    var goal = this.groupDeath[g.ndx];
    if (goal) return goal;

    var cost = 2 * g.stones.length; // 2 points are pretty much granted for the prisonners
    // TODO: instead of below, evaluate the damage caused by an *invasion* by taking group g
    var lives = g.allLives();
    var numEmpties = 0;
    for (var i = lives.length - 1; i >= 0; i--) {
        numEmpties += lives[i].numEmpties(); // TODO: count only empties not in "lives"
    }
    cost += this.spaceInvasionCoeff * Math.max(0, numEmpties - 1); //...and the "open gate" to territory will count a lot

    this.groupDeath[g.ndx] = goal = new Goal('death of ' + g.toString(1), cost, g);
    return goal;
};

MoveInfo.prototype._goalReachedByMove = function (goal, stone, factor, numMoves) {
    if (!stone)  throw new Error('Unexpected'); //return goal;
    factor = factor || 1;
    numMoves = numMoves || 1;
    if (log.debug) log.debug('Goal reached by ' + stone + ': ' + goal +
        (factor ? ' factor:' + factor.toFixed(2) : '') + (numMoves ? ' numMoves:' + numMoves : ''));

    var cell = this._getCell(stone.i, stone.j);
    var goals = cell.goals, goalFactors = cell.goalFactors, goalNumMoves = cell.goalNumMoves;
    var n = goals.indexOf(goal);
    if (n < 0) {
        goals.push(goal);
        goalFactors.push(factor);
        goalNumMoves.push(numMoves);
    } else {
        goalFactors[n] = Math.max(goalFactors[n], factor);
        goalNumMoves[n] = Math.min(goalNumMoves[n], numMoves);
    }
    goal.minMoves = Math.min(goal.minMoves, numMoves);
    return goal;
};

MoveInfo.prototype._countWideSpace = function (g) {
    var count = 0, color = g.color;
    var enemyInf = this.infl[1 - color];
    var lives = g.allLives();
    for (var i = lives.length - 1; i >= 0; i--) {
        var s = lives[i];
        if (enemyInf[s.j][s.i] > 0) continue;
        var v = this.player.boan.getVoidAt(s);
        if (v.owner && v.owner.group.color === color) continue;
        count++;
    }
    return count;
};

MoveInfo.prototype._twoEyeChance = function (potEyeCount) {
    if (potEyeCount >= 2.5) return 1;
    if (potEyeCount < 1.5) return 0;
    if (potEyeCount < 2) return 0.5;
    return this.potEyeCoeff2;
};

MoveInfo.prototype.groupChance = function (gi) {
    var band = gi.band;
    return this._bandChance(band ? band.brothers : [gi]);
};<commit_msg>MoveInfo._countWideSpace also skips non-eye owned voids<commit_after>'use strict';

var CONST = require('../../constants');
var Grid = require('../../Grid');
var Heuristic = require('./Heuristic');
var inherits = require('util').inherits;
var log = require('../../log');

var ALWAYS = CONST.ALWAYS;

var MIN_FACTOR = 0.1; // REVIEW ME; under this factor we ignore threats for now


/** @class */
function MoveInfo(player) {
    Heuristic.call(this, player);

    this.debug = false;
    this.grid = new Grid(this.gsize, null);
    this.groupDeath = [];
    this.what = '';

    this.spaceInvasionCoeff = this.getGene('spaceInvasion', 2.0, 0.01, 4.0);
    this.pressureCoeff = this.getGene('pressure', 1, 0.01, 2);
    this.eyeCloserCoeff = this.getGene('eyeCloser', 1, 0.01, 1);
    this.wideSpaceToEyeCoeff = this.getGene('wideSpaceToEye', 0.5, 0.01, 1);
    this.potEyeCoeff2 = this.getGene('potEyeCoeff2', 0.78, 0.75, 0.85);
}
inherits(MoveInfo, Heuristic);
module.exports = MoveInfo;


MoveInfo.prototype.evalBoard = function () {
    this.debug = this.player.debugMode;
    this.grid.init(null);
    this.groupDeath.length = 0;
};

MoveInfo.prototype.collectScores = function (stateYx, scoreYx) {
    Heuristic.prototype.evalBoard.call(this, stateYx, scoreYx); // calls _evalMove below
};

MoveInfo.prototype._evalMove = function (i, j) {
    var cell = this.grid.yx[j][i];
    if (!cell) return 0;
    return cell.computeScore();
};

// Redefines Heuristic#getMoveSurvey
MoveInfo.prototype.getMoveSurvey = function (i, j, survey) {
    var cell = this.grid.yx[j][i];
    if (cell) cell.computeScore(survey, this.name);
};

function CellInfo() {
    this.fakeEyeForColor = null;
    this.score = 0;
    this.goals = [];
    this.goalFactors = [];
    this.goalNumMoves = [];
}

CellInfo.prototype.computeScore = function (survey, hName) {
    var score = this.score;
    if (survey && score) survey[hName + '-base'] = score;
    var goals = this.goals;
    if (!goals.length) return score;

    for (var n = goals.length - 1; n >= 0; n--) {
        var goal = goals[n];
        var goalScore = goal.score * this.goalFactors[n];
        score += goalScore;

        if (survey) {
            var f = this.goalFactors[n], factorStr = f !== 1 ? ' factor:' + f.toFixed(2) : '';
            survey[hName + '-' + goal + factorStr] = goalScore;
        }
    }
    return score;
};

MoveInfo.prototype.getCellInfo = function (i, j) {
    return this.grid.yx[j][i]; // can be null
};

MoveInfo.prototype._getCell = function (i, j) {
    var cell = this.grid.yx[j][i];
    if (cell) return cell;

    this.grid.yx[j][i] = cell = new CellInfo();
    return cell;
};

MoveInfo.prototype.setAsFakeEye = function (stone, color) {
    this._getCell(stone.i, stone.j).fakeEyeForColor = color;
};

MoveInfo.prototype.isFakeEye = function (stone, color) {
    var cell = this.getCellInfo(stone.i, stone.j);
    return cell && cell.fakeEyeForColor === color;
};


//---

function Goal(name, score, g) {
    this.name = name;
    this.score = score;
    this.group = g;
    this.minMoves = Infinity;
}

Goal.prototype.toString = function () {
    return this.name +
        (this.minMoves !== Infinity ? ' minMoves:' + this.minMoves : '') +
        ' score:' + this.score.toFixed(2);
};

//---

MoveInfo.prototype._enter = function (name, g, stone) {
    this.what = name + ' on ' + g.toString(1);

    if (stone.i === this.player.testI && stone.j === this.player.testJ) {
        log.setLevel(log.DEBUG); // set your breakpoint here if needed
        log.debug('MoveInfo scoring ' + stone + ': ' + this.what);
    } else {
        if (log.level === log.DEBUG) log.setLevel(log.INFO);
    }
};

MoveInfo.prototype._groupDeathGoal = function (g) {
    var goal = this.groupDeath[g.ndx];
    if (goal) return goal;

    var cost = 2 * g.stones.length; // 2 points are pretty much granted for the prisonners
    // TODO: instead of below, evaluate the damage caused by an *invasion* by taking group g
    var lives = g.allLives();
    var numEmpties = 0;
    for (var i = lives.length - 1; i >= 0; i--) {
        numEmpties += lives[i].numEmpties(); // TODO: count only empties not in "lives"
    }
    cost += this.spaceInvasionCoeff * Math.max(0, numEmpties - 1); //...and the "open gate" to territory will count a lot

    this.groupDeath[g.ndx] = goal = new Goal('death of ' + g.toString(1), cost, g);
    return goal;
};

MoveInfo.prototype._goalReachedByMove = function (goal, stone, factor, numMoves) {
    if (!stone)  throw new Error('Unexpected'); //return goal;
    factor = factor || 1;
    numMoves = numMoves || 1;
    if (log.debug) log.debug('Goal reached by ' + stone + ': ' + goal +
        (factor ? ' factor:' + factor.toFixed(2) : '') + (numMoves ? ' numMoves:' + numMoves : ''));

    var cell = this._getCell(stone.i, stone.j);
    var goals = cell.goals, goalFactors = cell.goalFactors, goalNumMoves = cell.goalNumMoves;
    var n = goals.indexOf(goal);
    if (n < 0) {
        goals.push(goal);
        goalFactors.push(factor);
        goalNumMoves.push(numMoves);
    } else {
        goalFactors[n] = Math.max(goalFactors[n], factor);
        goalNumMoves[n] = Math.min(goalNumMoves[n], numMoves);
    }
    goal.minMoves = Math.min(goal.minMoves, numMoves);
    return goal;
};

MoveInfo.prototype._countWideSpace = function (g) {
    var count = 0, color = g.color;
    var enemyInf = this.infl[1 - color];
    var lives = g.allLives();
    for (var i = lives.length - 1; i >= 0; i--) {
        var s = lives[i];
        if (enemyInf[s.j][s.i] > 0) continue;
        var v = this.player.boan.getVoidAt(s);
        if (v.color !== undefined) continue;
        count++;
    }
    return count;
};

MoveInfo.prototype._twoEyeChance = function (potEyeCount) {
    if (potEyeCount >= 2.5) return 1;
    if (potEyeCount < 1.5) return 0;
    if (potEyeCount < 2) return 0.5;
    return this.potEyeCoeff2;
};

MoveInfo.prototype.groupChance = function (gi) {
    var band = gi.band;
    return this._bandChance(band ? band.brothers : [gi]);
};
{'fraction_non_alphanumeric': 0.16091097308488614, 'fraction_numerical': 0.011925465838509317, 'mean_word_length': 2.8717537672330877, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, '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': '484066', 'n_tokens_mistral': 4429, 'n_tokens_neox': 4199, 'n_words': 1002}