From 92f95bc3490e6eae394513728a4183dae57169a1 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 00:12:10 -0700 Subject: [PATCH 01/19] Remove unused getProbablyDead call. Reasoning: the wait_for promise is not awaited, so the result of getProbablyDead is meaningless here. --- src/GoEngine.ts | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/GoEngine.ts b/src/GoEngine.ts index 4e4b7bf8..2bd41b87 100644 --- a/src/GoEngine.ts +++ b/src/GoEngine.ts @@ -681,28 +681,6 @@ export class GoEngine extends EventEmitter { if (config.removed) { removed = this.decodeMoves(config.removed); } - if ( - CLIENT && - !this.territory_included_in_sgf && - typeof config.removed === "undefined" && - config.original_sgf && - /[0-9.]+/.test(self.outcome) - ) { - // Game data for SGF uploaded games don't always include removed stones, so we use score - // estimator to find probably dead stones to get a closer approximation of what - // territories should be marked in the final board position. - // - // NOTE: Some Go clients (not OGS, at least for now) do include dead stones in their - // SGFs, which we respect if present and skip using the score estimator. - const se = new ScoreEstimator( - this.goban_callback, - this, - AUTOSCORE_TRIALS, - AUTOSCORE_TOLERANCE, - true, - ); - removed = this.decodeMoves(se.getProbablyDead()); - } if (removed) { for (let i = 0; i < removed.length; ++i) { this.setRemoved(removed[i].x, removed[i].y, true, false); From 2015e3b0f03ee0673074fcd0533d305c7e48b780 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 00:21:15 -0700 Subject: [PATCH 02/19] ScoreEstimator.getProbablyDead: Remove the WASM code path. This function is only ever called on a prefer_remote SE. Also, the ownership/tolerance calculation should still be valid with WASM if ever needs to be used like that. --- src/ScoreEstimator.ts | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 4eedc567..9fd796a9 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -579,34 +579,17 @@ export class ScoreEstimator { let ret = ""; const arr = []; - if (remote_scorer) { - for (let y = 0; y < this.height; ++y) { - for (let x = 0; x < this.width; ++x) { - const current = this.board[y][x]; - const estimated = - this.ownership[y][x] < -this.tolerance - ? 2 // white - : this.ownership[y][x] > this.tolerance - ? 1 // black - : 0; // unclear - if (estimated === 0 /* dame */ || (current !== 0 && current !== estimated)) { - arr.push(encodeMove(x, y)); - } - } - } - } else { - // Old WASM - for (let y = 0; y < this.height; ++y) { - for (let x = 0; x < this.width; ++x) { - if ( - //(this.board[y][x] === 0 && this.area[y][x] === 0) /* dame */ - //|| - //(this.board[y][x] !== 0 && this.area[y][x] !== this.board[y][x]) /* captured */ - this.area[y][x] === 0 || - (this.board[y][x] !== 0 && this.area[y][x] !== this.board[y][x]) - ) { - arr.push(encodeMove(x, y)); - } + for (let y = 0; y < this.height; ++y) { + for (let x = 0; x < this.width; ++x) { + const current = this.board[y][x]; + const estimated = + this.ownership[y][x] < -this.tolerance + ? 2 // white + : this.ownership[y][x] > this.tolerance + ? 1 // black + : 0; // unclear + if (estimated === 0 /* dame */ || (current !== 0 && current !== estimated)) { + arr.push(encodeMove(x, y)); } } } From 31729638a763993d3f49f290b70bbc57350c7ef4 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 08:05:07 -0700 Subject: [PATCH 03/19] Remove duplicate removal variable. --- src/ScoreEstimator.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 9fd796a9..ba4d147a 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -285,7 +285,6 @@ export class ScoreEstimator { //score_prisoners:boolean; //score_territory:boolean; //score_territory_in_seki:boolean; - removed: Array>; white: PlayerScore = { total: 0, stones: 0, @@ -308,7 +307,7 @@ export class ScoreEstimator { engine: GoEngine; groups: Array>; currentMarker: number; - removal: Array>; // TODO: This is defined to be a dup of this.removed, can we remove that? + removal: Array>; goban_callback?: GobanCore; tolerance: number; group_list: Array; @@ -343,7 +342,7 @@ export class ScoreEstimator { this.height = engine.height; this.color_to_move = engine.colorToMove(); this.board = dup(engine.board); - this.removal = this.removed = GoMath.makeMatrix(this.width, this.height, 0); + this.removal = GoMath.makeMatrix(this.width, this.height, 0); this.marks = GoMath.makeMatrix(this.width, this.height, 0); this.area = GoMath.makeMatrix(this.width, this.height, 0); this.ownership = GoMath.makeMatrix(this.width, this.height, 0); @@ -625,7 +624,7 @@ export class ScoreEstimator { continue; } this.marks[yy][xx] = this.currentMarker; - if (this.board[yy][xx] === color || (color === 0 && this.removed[yy][xx])) { + if (this.board[yy][xx] === color || (color === 0 && this.removal[yy][xx])) { this.groups[yy][xx] = g; g.add(xx, yy, color); this.foreachNeighbor({ x: xx, y: yy }, push_on_stack); @@ -657,7 +656,7 @@ export class ScoreEstimator { if (g.color) { let liberties = 0; g.foreachNeighboringPoint((pt) => { - if (this.board[pt.y][pt.x] === 0 || this.removed[pt.y][pt.x]) { + if (this.board[pt.y][pt.x] === 0 || this.removal[pt.y][pt.x]) { ++liberties; } }); @@ -799,7 +798,7 @@ export class ScoreEstimator { /* clear removed */ for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { - if (this.removed[y][x]) { + if (this.removal[y][x]) { if (this.board[y][x] === 1) { ++removed_black; } From 54f1008404b3f1e43fada88d9ebe5006deecad16 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 08:08:42 -0700 Subject: [PATCH 04/19] Delete commented code. --- src/ScoreEstimator.ts | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index ba4d147a..2270857d 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -85,19 +85,11 @@ let init_promise: Promise; export function init_score_estimator(): Promise { if (CLIENT) { - /* - if (remote_scorer) { - return Promise.resolve(true); - } - */ - if (OGSScoreEstimator_initialized) { - //console.log("Already initialized"); return Promise.resolve(true); } if (init_promise) { - //console.log("An existing promise"); return init_promise; } @@ -114,22 +106,18 @@ export function init_score_estimator(): Promise { } if (OGSScoreEstimatorModule) { - //console.log("Already loaded"); OGSScoreEstimatorModule = OGSScoreEstimatorModule(); OGSScoreEstimator_initialized = true; return Promise.resolve(true); } - //console.log("Sync script"); const script: HTMLScriptElement = document.getElementById( "ogs_score_estimator_script", ) as HTMLScriptElement; if (script) { let resolve: (tf: boolean) => void; - //let reject; init_promise = new Promise((_resolve, _reject) => { resolve = _resolve; - //reject = _reject; }); script.onload = () => { @@ -247,23 +235,17 @@ class SEGroup { } foreachNeighborGroup(fn: (group: SEGroup) => void): void { for (let i = 0; i < this.neighbors.length; ++i) { - //if (!this.neighbors[i].removed) { fn(this.neighbors[i]); - //} } } foreachNeighborSpaceGroup(fn: (group: SEGroup) => void): void { for (let i = 0; i < this.neighboring_space.length; ++i) { - //if (!this.neighboring_space[i].removed) { fn(this.neighboring_space[i]); - //} } } foreachNeighborEnemyGroup(fn: (group: SEGroup) => void): void { for (let i = 0; i < this.neighboring_enemy.length; ++i) { - //if (!this.neighboring_enemy[i].removed) { fn(this.neighboring_enemy[i]); - //} } } setRemoved(removed: boolean): void { @@ -281,10 +263,6 @@ export class ScoreEstimator { board: Array>; white_prisoners: number = 0; black_prisoners: number = 0; - //score_stones:boolean; - //score_prisoners:boolean; - //score_territory:boolean; - //score_territory_in_seki:boolean; white: PlayerScore = { total: 0, stones: 0, @@ -359,7 +337,6 @@ export class ScoreEstimator { this.resetGroups(); this.when_ready = this.estimateScore(this.trials, this.tolerance); - //this.sealDame(); } public estimateScore(trials: number, tolerance: number): Promise { @@ -494,7 +471,6 @@ export class ScoreEstimator { i = 0; for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { - //ownership[y][x] = ints[i] < 0 ? 2 : ints[i]; ownership[y][x] = ints[i]; ++i; @@ -541,13 +517,11 @@ export class ScoreEstimator { updateEstimate(estimated_score: number, ownership: Array>, score?: number) { /* Build up our heat map and ownership */ /* negative for black, 0 for neutral, positive for white */ - //this.heat = GoMath.makeMatrix(this.width, this.height, 0.0); this.ownership = ownership; for (let y = 0; y < this.height; ++y) { for (let x = 0; x < this.width; ++x) { this.heat[y][x] = ownership[y][x]; this.area[y][x] = ownership[y][x] < 0 ? 2 : ownership[y][x]; - //this.area[y][x] = ownership[y][x]; this.estimated_area[y][x] = this.area[y][x]; } } @@ -564,11 +538,6 @@ export class ScoreEstimator { this.amount_fractional = Math.abs(score).toFixed(1); } - /* - if (this.goban_callback && this.goban_callback.heatmapUpdated) { - this.goban_callback.heatmapUpdated(); - } - */ if (this.goban_callback && this.goban_callback.updateScoreEstimation) { this.goban_callback.updateScoreEstimation(); } @@ -679,7 +648,6 @@ export class ScoreEstimator { this.estimateScore(this.trials, this.tolerance).catch(() => { /* empty */ }); - //this.resetGroups(); } toggleMetaGroupRemoval(x: number, y: number): void { const already_done: { [k: string]: boolean } = {}; @@ -810,14 +778,11 @@ export class ScoreEstimator { } } - //if (this.phase !== "play") { if (this.engine.score_territory) { const groups = new GoStoneGroups(this); - //console.log(gm); groups.foreachGroup((gr) => { if (gr.is_territory) { - //console.log(gr); if (!this.engine.score_territory_in_seki && gr.is_territory_in_seki) { return; } @@ -831,7 +796,6 @@ export class ScoreEstimator { "What should be unreached code is running, should probably be running " + "this[color].territory += markScored(gr.points, false);", ); - //this[color].territory += markScored(gr.points, false); } }); } @@ -851,7 +815,6 @@ export class ScoreEstimator { } } } - //} if (this.engine.score_prisoners) { this.black.prisoners = this.black_prisoners + removed_white; From 9c83a2275b5a806417d2a86dfa46a17ad64ba725 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 08:12:08 -0700 Subject: [PATCH 05/19] Remove SERVER path for init_score_estimator. --- src/ScoreEstimator.ts | 110 +++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 2270857d..5d732aa1 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -25,20 +25,14 @@ import { JGOFNumericPlayerColor } from "./JGOF"; import { _ } from "./translate"; declare const CLIENT: boolean; -declare const SERVER: boolean; -/* This script is used on both the front end and back end, and the way the - * OGSScoreEstimator module is loaded is quite differente between the two. - * - * On the server, the OGSScoreEsimtator module is loaded by score-estimator.ts - * and the set_OGSScoreEstimator function is called with the module. - * - * On the client, the OGSScoreEstimator script is loaded in an async fashion, - * so at some point that global variable becomes not null and can be used. +/* The OGSScoreEstimator method is a wasm compiled C program that + * does simple random playouts. On the client, the OGSScoreEstimator script + * is loaded in an async fashion, so at some point that global variable + * becomes not null and can be used. */ -/* In addition to the OGSScoreEstimator method, which is a wasm compiled - * C program that does simple random playouts, we have a RemoteScoring system +/* In addition to the OGSScoreEstimator method, we have a RemoteScoring system * which needs to be initialized by either the client or the server if we want * remote scoring enabled. */ @@ -47,14 +41,6 @@ declare let OGSScoreEstimator: any; let OGSScoreEstimator_initialized = false; let OGSScoreEstimatorModule: any; -/* This is used on the server side */ -export function set_OGSScoreEstimator(mod: any): void { - OGSScoreEstimatorModule = mod; - init_score_estimator() - .then((tf) => console.info("Score estimator intialized")) - .catch((err) => console.error(err)); -} - export interface ScoreEstimateRequest { player_to_move: "black" | "white"; width: number; @@ -84,64 +70,56 @@ export function set_remote_scorer( let init_promise: Promise; export function init_score_estimator(): Promise { - if (CLIENT) { - if (OGSScoreEstimator_initialized) { - return Promise.resolve(true); - } - - if (init_promise) { - return init_promise; - } - - try { - if ( - !OGSScoreEstimatorModule && - (("OGSScoreEstimator" in window) as any) && - ((window as any)["OGSScoreEstimator"] as any) - ) { - OGSScoreEstimatorModule = (window as any)["OGSScoreEstimator"] as any; - } - } catch (e) { - console.error(e); - } - - if (OGSScoreEstimatorModule) { - OGSScoreEstimatorModule = OGSScoreEstimatorModule(); - OGSScoreEstimator_initialized = true; - return Promise.resolve(true); - } + if (!CLIENT) { + throw new Error("Only initialize WASM library on the client side"); + } - const script: HTMLScriptElement = document.getElementById( - "ogs_score_estimator_script", - ) as HTMLScriptElement; - if (script) { - let resolve: (tf: boolean) => void; - init_promise = new Promise((_resolve, _reject) => { - resolve = _resolve; - }); + if (OGSScoreEstimator_initialized) { + return Promise.resolve(true); + } - script.onload = () => { - OGSScoreEstimatorModule = OGSScoreEstimator; - OGSScoreEstimatorModule = OGSScoreEstimatorModule(); - OGSScoreEstimator_initialized = true; - resolve(true); - }; + if (init_promise) { + return init_promise; + } - return init_promise; - } else { - return Promise.reject("score estimator not available"); + try { + if ( + !OGSScoreEstimatorModule && + (("OGSScoreEstimator" in window) as any) && + ((window as any)["OGSScoreEstimator"] as any) + ) { + OGSScoreEstimatorModule = (window as any)["OGSScoreEstimator"] as any; } + } catch (e) { + console.error(e); } - if (SERVER) { + if (OGSScoreEstimatorModule) { OGSScoreEstimatorModule = OGSScoreEstimatorModule(); OGSScoreEstimator_initialized = true; return Promise.resolve(true); } - // this can't be reached so long as one of CLIENT or SERVER is set, which - // should always be the case. - throw new Error("Unreachable code reached"); + const script: HTMLScriptElement = document.getElementById( + "ogs_score_estimator_script", + ) as HTMLScriptElement; + if (script) { + let resolve: (tf: boolean) => void; + init_promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + }); + + script.onload = () => { + OGSScoreEstimatorModule = OGSScoreEstimator; + OGSScoreEstimatorModule = OGSScoreEstimatorModule(); + OGSScoreEstimator_initialized = true; + resolve(true); + }; + + return init_promise; + } else { + return Promise.reject("score estimator not available"); + } } interface SEPoint { From 4edb6d2a49aff77c88b3ed9612f83f554868dbcf Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 08:16:20 -0700 Subject: [PATCH 06/19] Remove ScoreEstimator.estimated_area This looks like a copy of ScoreEstimator.area and is never used. --- src/ScoreEstimator.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 5d732aa1..fdd214e9 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -274,7 +274,6 @@ export class ScoreEstimator { area: Array>; // hard numeric values territory: Array>; trials: number; - estimated_area: Array>; winner: string = ""; heat: Array>; color_to_move: "black" | "white"; @@ -303,7 +302,6 @@ export class ScoreEstimator { this.area = GoMath.makeMatrix(this.width, this.height, 0); this.ownership = GoMath.makeMatrix(this.width, this.height, 0); this.heat = GoMath.makeMatrix(this.width, this.height, 0.0); - this.estimated_area = GoMath.makeMatrix(this.width, this.height, 0.0); this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height); this.territory = GoMath.makeMatrix(this.width, this.height, 0); this.estimated_score = 0.0; @@ -500,7 +498,6 @@ export class ScoreEstimator { for (let x = 0; x < this.width; ++x) { this.heat[y][x] = ownership[y][x]; this.area[y][x] = ownership[y][x] < 0 ? 2 : ownership[y][x]; - this.estimated_area[y][x] = this.area[y][x]; } } this.estimated_score = estimated_score - this.engine.komi; From d6194f4c46fdf90b8a12de091b038939230b8ca1 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 09:59:32 -0700 Subject: [PATCH 07/19] Extract score adjustment into its own function. This helps with testing, and also will allow us to do the same adjustments with other scoring algorithms. --- src/ScoreEstimator.ts | 112 +++++++++++++++++---------- src/__tests__/ScoreEstimator.test.ts | 41 ++++++++++ 2 files changed, 113 insertions(+), 40 deletions(-) create mode 100644 src/__tests__/ScoreEstimator.test.ts diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index fdd214e9..5aacdcdd 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -428,7 +428,7 @@ export class ScoreEstimator { tr: number, to: number, ) => number; - let estimated_score = estimate( + const estimated_score = estimate( this.width, this.height, ptr, @@ -436,12 +436,6 @@ export class ScoreEstimator { trials, tolerance, ); - estimated_score -= this.engine.getHandicapPointAdjustmentForWhite(); - - // For Japanese rules we use territory counting. Don't even - // attempt to handle rules with score_stones and not - // score_prisoners or vice-versa. - const territory_counting = !this.engine.score_stones && this.engine.score_prisoners; const ownership = GoMath.makeMatrix(this.width, this.height, 0); i = 0; @@ -449,44 +443,13 @@ export class ScoreEstimator { for (let x = 0; x < this.width; ++x) { ownership[y][x] = ints[i]; ++i; - - if (territory_counting && this.board[y][x]) { - // Fix display and count in Japanese rules. - - // Board/ownership being 1/1 or 2/-1 means it's a - // live stone; clear ownership so the display - // looks like it is using territory scoring. - if ( - (this.board[y][x] === 1 && ownership[y][x] === 1) || - (this.board[y][x] === 2 && ownership[y][x] === -1) - ) { - ownership[y][x] = 0; - } - - // Any stone on the board means one less point for - // the corresponding player, whether it's a - // prisoner, a live stone that doesn't count as - // territory, or (does this even happen?) a stone - // of unknown status. - if (this.board[y][x] === 1) { - // black stone gives White a point - estimated_score -= 1; - } else { - // white stone gives Black a point - estimated_score += 1; - } - } } } - // Account for already-captured prisoners in Japanese rules. - if (territory_counting) { - estimated_score += this.engine.getBlackPrisoners(); - estimated_score -= this.engine.getWhitePrisoners(); - } + const adjusted = adjust_estimate(this.engine, this.board, ownership, estimated_score); OGSScoreEstimatorModule._free(ptr); - this.updateEstimate(estimated_score, ownership); + this.updateEstimate(adjusted.score, adjusted.ownership); return Promise.resolve(); } @@ -863,3 +826,72 @@ export class ScoreEstimator { } } } + +/** + * Adjust Estimate to account for Ruleset (i.e. territory vs area) and captures + * @param engine Go engine is required because the ruleset is taken into account + * @param board the current board state + * @param area_map Representation of the ownership, 1=Black, -1=White, 0=Undecided + * using Area rules + * @param score estimated score (not accounting for captures) + */ +export function adjust_estimate( + engine: GoEngine, + board: Array>, + area_map: number[][], + score: number, +) { + let adjusted_score = score - engine.getHandicapPointAdjustmentForWhite(); + const { width, height } = get_dimensions(board); + const ownership = GoMath.makeMatrix(width, height); + + // For Japanese rules we use territory counting. Don't even + // attempt to handle rules with score_stones and not + // score_prisoners or vice-versa. + const territory_counting = !engine.score_stones && engine.score_prisoners; + + for (let y = 0; y < board.length; ++y) { + for (let x = 0; x < board[y].length; ++x) { + ownership[y][x] = area_map[y][x]; + + if (territory_counting && board[y][x]) { + // Fix display and count in Japanese rules. + + // Board/ownership being 1/1 or 2/-1 means it's a + // live stone; clear ownership so the display + // looks like it is using territory scoring. + if ( + (board[y][x] === 1 && area_map[y][x] === 1) || + (board[y][x] === 2 && area_map[y][x] === -1) + ) { + ownership[y][x] = 0; + } + + // Any stone on the board means one less point for + // the corresponding player, whether it's a + // prisoner, a live stone that doesn't count as + // territory, or (does this even happen?) a stone + // of unknown status. + if (board[y][x] === 1) { + // black stone gives White a point + adjusted_score -= 1; + } else { + // white stone gives Black a point + adjusted_score += 1; + } + } + } + + // Account for already-captured prisoners in Japanese rules. + if (territory_counting) { + adjusted_score += engine.getBlackPrisoners(); + adjusted_score -= engine.getWhitePrisoners(); + } + } + + return { score: adjusted_score, ownership }; +} + +function get_dimensions(board: Array>) { + return { width: board[0].length, height: board.length }; +} diff --git a/src/__tests__/ScoreEstimator.test.ts b/src/__tests__/ScoreEstimator.test.ts new file mode 100644 index 00000000..2162c350 --- /dev/null +++ b/src/__tests__/ScoreEstimator.test.ts @@ -0,0 +1,41 @@ +import { GoEngine } from "../GoEngine"; +import { adjust_estimate } from "../ScoreEstimator"; + +describe("adjust_estimate", () => { + const BOARD = [ + [0, 1, 2, 0], + [0, 1, 2, 0], + [0, 1, 2, 0], + [0, 1, 2, 0], + ]; + const OWNERSHIP = [ + [1, 1, -1, -1], + [1, 1, -1, -1], + [1, 1, -1, -1], + [1, 1, -1, -1], + ]; + const SCORE = -0.5; + const KOMI = 0.5; + + test("adjust_estimate area", () => { + const engine = new GoEngine({ komi: KOMI, rules: "chinese" }); + expect(adjust_estimate(engine, BOARD, OWNERSHIP, SCORE)).toEqual({ + score: -0.5, + ownership: OWNERSHIP, + }); + }); + + test("adjust_estimate area", () => { + const ADJUSTED_OWNERSHIP = [ + [1, 0, 0, -1], + [1, 0, 0, -1], + [1, 0, 0, -1], + [1, 0, 0, -1], + ]; + const engine = new GoEngine({ komi: KOMI, rules: "japanese" }); + expect(adjust_estimate(engine, BOARD, OWNERSHIP, SCORE)).toEqual({ + score: -0.5, + ownership: ADJUSTED_OWNERSHIP, + }); + }); +}); From b765fa0f2dbf5983a1e1ef892e60afaf0a1e5df0 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 10:07:49 -0700 Subject: [PATCH 08/19] Remove heat and area from ScoreEstimator. These are both derived from ownership. area appears to be unused, and heat is used in GobanCanvas, but can just be replaced with ownership. --- src/GobanCanvas.ts | 2 +- src/ScoreEstimator.ts | 10 ---------- src/__tests__/GobanCanvas.test.ts | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/GobanCanvas.ts b/src/GobanCanvas.ts index bbe34578..36e4235d 100644 --- a/src/GobanCanvas.ts +++ b/src/GobanCanvas.ts @@ -2100,7 +2100,7 @@ export class GobanCanvas extends GobanCore { if (this.scoring_mode && this.score_estimate) { const se = this.score_estimate; - const est = se.heat[j][i]; + const est = se.ownership[j][i]; ctx.beginPath(); diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 5aacdcdd..3966646c 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -271,11 +271,9 @@ export class ScoreEstimator { amount: number = NaN; amount_fractional: string = "[unset]"; ownership: Array>; - area: Array>; // hard numeric values territory: Array>; trials: number; winner: string = ""; - heat: Array>; color_to_move: "black" | "white"; estimated_score: number; estimated_hard_score: number; @@ -299,9 +297,7 @@ export class ScoreEstimator { this.board = dup(engine.board); this.removal = GoMath.makeMatrix(this.width, this.height, 0); this.marks = GoMath.makeMatrix(this.width, this.height, 0); - this.area = GoMath.makeMatrix(this.width, this.height, 0); this.ownership = GoMath.makeMatrix(this.width, this.height, 0); - this.heat = GoMath.makeMatrix(this.width, this.height, 0.0); this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height); this.territory = GoMath.makeMatrix(this.width, this.height, 0); this.estimated_score = 0.0; @@ -457,12 +453,6 @@ export class ScoreEstimator { /* Build up our heat map and ownership */ /* negative for black, 0 for neutral, positive for white */ this.ownership = ownership; - for (let y = 0; y < this.height; ++y) { - for (let x = 0; x < this.width; ++x) { - this.heat[y][x] = ownership[y][x]; - this.area[y][x] = ownership[y][x] < 0 ? 2 : ownership[y][x]; - } - } this.estimated_score = estimated_score - this.engine.komi; this.estimated_hard_score = estimated_score - this.engine.komi; diff --git a/src/__tests__/GobanCanvas.test.ts b/src/__tests__/GobanCanvas.test.ts index 50a31deb..09dbcb81 100644 --- a/src/__tests__/GobanCanvas.test.ts +++ b/src/__tests__/GobanCanvas.test.ts @@ -411,7 +411,7 @@ describe("onTap", () => { board: GoMath.makeMatrix(4, 2), removal: GoMath.makeMatrix(4, 2), territory: GoMath.makeMatrix(4, 2), - heat: GoMath.makeMatrix(4, 2), + ownership: GoMath.makeMatrix(4, 2), }; goban.engine.estimateScore = jest.fn().mockReturnValue(mock_score_estimate); From dd6bf8fc18a2fe944524cfb6e1308d3abad9160e Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 10:13:18 -0700 Subject: [PATCH 09/19] Remove estimated_score from ScoreEstimator (unused) --- src/ScoreEstimator.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 3966646c..32b588ff 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -275,7 +275,6 @@ export class ScoreEstimator { trials: number; winner: string = ""; color_to_move: "black" | "white"; - estimated_score: number; estimated_hard_score: number; when_ready: Promise; prefer_remote: boolean; @@ -300,7 +299,6 @@ export class ScoreEstimator { this.ownership = GoMath.makeMatrix(this.width, this.height, 0); this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height); this.territory = GoMath.makeMatrix(this.width, this.height, 0); - this.estimated_score = 0.0; this.estimated_hard_score = 0.0; this.group_list = []; this.trials = trials; @@ -453,13 +451,12 @@ export class ScoreEstimator { /* Build up our heat map and ownership */ /* negative for black, 0 for neutral, positive for white */ this.ownership = ownership; - this.estimated_score = estimated_score - this.engine.komi; this.estimated_hard_score = estimated_score - this.engine.komi; if (typeof score === "undefined") { this.winner = this.estimated_hard_score > 0 ? _("Black") : _("White"); this.amount = Math.abs(this.estimated_hard_score); - this.amount_fractional = Math.abs(this.estimated_score).toFixed(1); + this.amount_fractional = Math.abs(this.estimated_hard_score).toFixed(1); } else { this.winner = score > 0 ? _("Black") : _("White"); this.amount = Math.abs(score); From 898bfe7d57349b557ca5ffeddc55571bb48b3e27 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 10:20:26 -0700 Subject: [PATCH 10/19] Remove unused tolerance paramter from estimateScoreRemote. --- src/ScoreEstimator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 32b588ff..ca641dbd 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -315,13 +315,13 @@ export class ScoreEstimator { } if (remote_scorer) { - return this.estimateScoreRemote(tolerance); + return this.estimateScoreRemote(); } else { return this.estimateScoreWASM(trials, tolerance); } } - private estimateScoreRemote(tolerance: number = 0.25): Promise { + private estimateScoreRemote(): Promise { const komi = this.engine.komi; const captures_delta = this.engine.score_prisoners ? this.engine.getBlackPrisoners() - this.engine.getWhitePrisoners() From 1ce68711903daf2cbe95c068935950f48b2d71a1 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 10:24:51 -0700 Subject: [PATCH 11/19] Remove black_prisoners and white_prisoners as these are only ever set to zero. --- src/ScoreEstimator.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index ca641dbd..018e1b12 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -47,8 +47,6 @@ export interface ScoreEstimateRequest { height: number; board_state: Array>; rules: GoEngineRules; - black_prisoners?: number; - white_prisoners?: number; komi?: number; jwt: string; } @@ -239,8 +237,6 @@ export class ScoreEstimator { width: number; height: number; board: Array>; - white_prisoners: number = 0; - black_prisoners: number = 0; white: PlayerScore = { total: 0, stones: 0, @@ -742,8 +738,8 @@ export class ScoreEstimator { } if (this.engine.score_prisoners) { - this.black.prisoners = this.black_prisoners + removed_white; - this.white.prisoners = this.white_prisoners + removed_black; + this.black.prisoners = removed_white; + this.white.prisoners = removed_black; } this.black.total = From ae22ca5a80824ccbf69be741eaf72f0e441d637c Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 10:29:35 -0700 Subject: [PATCH 12/19] Remove estimated_score and estimated_hard_score from SEGroup --- src/ScoreEstimator.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 018e1b12..23d1619d 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -134,8 +134,6 @@ class SEGroup { id: number; color: JGOFNumericPlayerColor; removed: boolean; - estimated_score: number; - estimated_hard_score: number; neighbors: Array; neighbor_map: { [group_id: string]: boolean }; liberties: number = 0; @@ -150,8 +148,6 @@ class SEGroup { this.neighboring_enemy = []; this.neighbor_map = {}; this.removed = false; - this.estimated_score = 0.0; - this.estimated_hard_score = 0.0; // this.liberties is set by ScoreEstimator.resetGroups */ } From e231515b4040a90c9a2da1befbfa01417ba6aeac Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 11:37:50 -0700 Subject: [PATCH 13/19] Use GoMath functions for group logic. --- src/ScoreEstimator.ts | 128 ++++++++---------------------------------- 1 file changed, 24 insertions(+), 104 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 23d1619d..58bd887c 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -15,9 +15,9 @@ */ import { dup } from "./GoUtil"; -import { Intersection, encodeMove, encodeMoves } from "./GoMath"; +import { encodeMove, encodeMoves } from "./GoMath"; import * as GoMath from "./GoMath"; -import { Group } from "./GoStoneGroup"; +import { GoStoneGroup } from "./GoStoneGroup"; import { GoStoneGroups } from "./GoStoneGroups"; import { GobanCore } from "./GobanCore"; import { GoEngine, PlayerScore, GoEngineRules } from "./GoEngine"; @@ -123,7 +123,6 @@ export function init_score_estimator(): Promise { interface SEPoint { x: number; y: number; - color?: JGOFNumericPlayerColor; } class SEGroup { @@ -151,8 +150,8 @@ class SEGroup { // this.liberties is set by ScoreEstimator.resetGroups */ } - add(i: number, j: number, color: JGOFNumericPlayerColor) { - this.points.push({ x: i, y: j, color: color }); + add(i: number, j: number) { + this.points.push({ x: i, y: j }); } foreachPoint(fn: (pt: SEPoint) => void) { for (let i = 0; i < this.points.length; ++i) { @@ -259,7 +258,6 @@ export class ScoreEstimator { goban_callback?: GobanCore; tolerance: number; group_list: Array; - marks: Array>; amount: number = NaN; amount_fractional: string = "[unset]"; ownership: Array>; @@ -287,7 +285,6 @@ export class ScoreEstimator { this.color_to_move = engine.colorToMove(); this.board = dup(engine.board); this.removal = GoMath.makeMatrix(this.width, this.height, 0); - this.marks = GoMath.makeMatrix(this.width, this.height, 0); this.ownership = GoMath.makeMatrix(this.width, this.height, 0); this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height); this.territory = GoMath.makeMatrix(this.width, this.height, 0); @@ -489,51 +486,20 @@ export class ScoreEstimator { this.territory = GoMath.makeMatrix(this.width, this.height, 0); this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height); this.group_list = []; - let stack = null; - for (let y = 0; y < this.height; ++y) { - for (let x = 0; x < this.width; ++x) { - if (!this.groups[y][x]) { - this.incrementCurrentMarker(); /* clear marks */ - const color = this.board[y][x]; - const g = new SEGroup(this, color, this.currentMarker); - this.group_list.push(g); - stack = [x, y]; - while (stack.length) { - const yy = stack.pop(); - const xx = stack.pop(); - if (xx === undefined || yy === undefined) { - throw new Error(`Invalid stack state`); - } - - if (this.marks[yy][xx] === this.currentMarker) { - continue; - } - this.marks[yy][xx] = this.currentMarker; - if (this.board[yy][xx] === color || (color === 0 && this.removal[yy][xx])) { - this.groups[yy][xx] = g; - g.add(xx, yy, color); - this.foreachNeighbor({ x: xx, y: yy }, push_on_stack); - } - } - } - } - } + const go_stone_groups = new GoStoneGroups(this); - function push_on_stack(x: number, y: number) { - stack.push(x); - stack.push(y); - } + go_stone_groups.foreachGroup((gs_grp) => { + const se_grp = make_se_group_from_gs_group(gs_grp, this); + gs_grp.points.forEach((pt) => { + this.groups[pt.y][pt.x] = se_grp; + }); + this.group_list.push(se_grp); + }); - /* compute group neighborhoodship */ - for (let y = 0; y < this.height; ++y) { - for (let x = 0; x < this.width; ++x) { - this.foreachNeighbor({ x: x, y: y }, (xx, yy) => { - if (this.groups[y][x].id !== this.groups[yy][xx].id) { - this.groups[y][x].addNeighbor(this.groups[yy][xx]); - this.groups[yy][xx].addNeighbor(this.groups[y][x]); - } - }); + for (const grp of this.group_list) { + for (const gs_neighbor of go_stone_groups.groups[grp.id].neighbors) { + grp.addNeighbor(this.group_list[gs_neighbor.id - 1]); } } @@ -749,61 +715,6 @@ export class ScoreEstimator { return this; } - private foreachNeighbor( - pt_or_group: Intersection | Group, - fn_of_neighbor_pt: (x: number, y: number) => void, - ): void { - const self = this; - let group: Group; - let done_array: Array; - - if (pt_or_group instanceof Array) { - group = pt_or_group as Group; - done_array = new Array(this.height * this.width); - for (let i = 0; i < group.length; ++i) { - done_array[group[i].x + group[i].y * this.width] = true; - } - for (let i = 0; i < group.length; ++i) { - const pt = group[i]; - if (pt.x - 1 >= 0) { - checkAndDo(pt.x - 1, pt.y); - } - if (pt.x + 1 !== this.width) { - checkAndDo(pt.x + 1, pt.y); - } - if (pt.y - 1 >= 0) { - checkAndDo(pt.x, pt.y - 1); - } - if (pt.y + 1 !== this.height) { - checkAndDo(pt.x, pt.y + 1); - } - } - } else { - const pt = pt_or_group; - if (pt.x - 1 >= 0) { - fn_of_neighbor_pt(pt.x - 1, pt.y); - } - if (pt.x + 1 !== this.width) { - fn_of_neighbor_pt(pt.x + 1, pt.y); - } - if (pt.y - 1 >= 0) { - fn_of_neighbor_pt(pt.x, pt.y - 1); - } - if (pt.y + 1 !== this.height) { - fn_of_neighbor_pt(pt.x, pt.y + 1); - } - } - - function checkAndDo(x: number, y: number): void { - const idx = x + y * self.width; - if (done_array[idx]) { - return; - } - done_array[idx] = true; - - fn_of_neighbor_pt(x, y); - } - } } /** @@ -874,3 +785,12 @@ export function adjust_estimate( function get_dimensions(board: Array>) { return { width: board[0].length, height: board.length }; } + +/** + * SE Group and GoStoneGroup have a slightly different interface. + */ +function make_se_group_from_gs_group(gsg: GoStoneGroup, se: ScoreEstimator) { + const se_group = new SEGroup(se, gsg.color, gsg.id); + se_group.points = gsg.points.map((pt) => pt); + return se_group; +} From 578b62feca4672c0f5b2e0b919e2dcff50dcf830 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 11:53:01 -0700 Subject: [PATCH 14/19] Remove liberties from SEGroup (unused member) --- src/ScoreEstimator.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 58bd887c..0905be08 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -135,7 +135,6 @@ class SEGroup { removed: boolean; neighbors: Array; neighbor_map: { [group_id: string]: boolean }; - liberties: number = 0; constructor(se: ScoreEstimator, color: JGOFNumericPlayerColor, id: number) { this.points = []; @@ -147,8 +146,6 @@ class SEGroup { this.neighboring_enemy = []; this.neighbor_map = {}; this.removed = false; - - // this.liberties is set by ScoreEstimator.resetGroups */ } add(i: number, j: number) { this.points.push({ x: i, y: j }); @@ -502,19 +499,6 @@ export class ScoreEstimator { grp.addNeighbor(this.group_list[gs_neighbor.id - 1]); } } - - /* compute liberties */ - this.foreachGroup((g: SEGroup) => { - if (g.color) { - let liberties = 0; - g.foreachNeighboringPoint((pt) => { - if (this.board[pt.y][pt.x] === 0 || this.removal[pt.y][pt.x]) { - ++liberties; - } - }); - g.liberties = liberties; - } - }); } foreachGroup(fn: (group: SEGroup) => void): void { for (let i = 0; i < this.group_list.length; ++i) { From f7ead99acb464bb72ac1b0d5d6d54943282c2f95 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 15:35:17 -0700 Subject: [PATCH 15/19] Add test for resetGroups --- src/__tests__/ScoreEstimator.test.ts | 57 +++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/__tests__/ScoreEstimator.test.ts b/src/__tests__/ScoreEstimator.test.ts index 2162c350..ee556cb5 100644 --- a/src/__tests__/ScoreEstimator.test.ts +++ b/src/__tests__/ScoreEstimator.test.ts @@ -1,5 +1,5 @@ import { GoEngine } from "../GoEngine"; -import { adjust_estimate } from "../ScoreEstimator"; +import { ScoreEstimator, adjust_estimate, set_remote_scorer } from "../ScoreEstimator"; describe("adjust_estimate", () => { const BOARD = [ @@ -39,3 +39,58 @@ describe("adjust_estimate", () => { }); }); }); + +describe("ScoreEstimator", () => { + const OWNERSHIP = [ + [1, 1, -1, -1], + [1, 1, -1, -1], + ]; + const KOMI = 0.5; + + beforeEach(() => { + set_remote_scorer(async () => { + return { ownership: OWNERSHIP }; + }); + }); + + afterEach(() => { + set_remote_scorer(undefined as any); + }); + + test("resetGroups", async () => { + const engine = new GoEngine({ komi: KOMI, width: 4, height: 2 }); + engine.place(1, 0); + engine.place(2, 0); + engine.place(1, 1); + engine.place(2, 1); + + // It might seem weird to set prefer_remote = true just to test + // resetGroups, but is a necessary hack to bypass initialization of + // the OGSScoreEstimation library + const prefer_remote = true; + const se = new ScoreEstimator(undefined, engine, 10, 0.5, prefer_remote); + + await se.when_ready; + + expect(se.group_list).toHaveLength(4); + expect(se.group_list.map((group) => group.points)).toEqual([ + [ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + ], + [ + { x: 1, y: 0 }, + { x: 1, y: 1 }, + ], + [ + { x: 2, y: 0 }, + { x: 2, y: 1 }, + ], + [ + { x: 3, y: 0 }, + { x: 3, y: 1 }, + ], + ]); + expect(se.group_list.map((group) => group.color)).toEqual([0, 1, 2, 0]); + }); +}); From c5c7eea9a5dbb913b9ac5cc119979267aaec679a Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 15:57:52 -0700 Subject: [PATCH 16/19] Add test for amount and winner --- src/__tests__/ScoreEstimator.test.ts | 37 +++++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/__tests__/ScoreEstimator.test.ts b/src/__tests__/ScoreEstimator.test.ts index ee556cb5..ccb55f17 100644 --- a/src/__tests__/ScoreEstimator.test.ts +++ b/src/__tests__/ScoreEstimator.test.ts @@ -46,10 +46,22 @@ describe("ScoreEstimator", () => { [1, 1, -1, -1], ]; const KOMI = 0.5; + const engine = new GoEngine({ komi: KOMI, width: 4, height: 2 }); + engine.place(1, 0); + engine.place(2, 0); + engine.place(1, 1); + engine.place(2, 1); + + // It might seem weird to set prefer_remote = true just to test + // resetGroups, but is a necessary hack to bypass initialization of + // the OGSScoreEstimation library + const prefer_remote = true; + const trials = 10; + const tolerance = 0.25; beforeEach(() => { set_remote_scorer(async () => { - return { ownership: OWNERSHIP }; + return { ownership: OWNERSHIP, score: -7.5 }; }); }); @@ -58,17 +70,7 @@ describe("ScoreEstimator", () => { }); test("resetGroups", async () => { - const engine = new GoEngine({ komi: KOMI, width: 4, height: 2 }); - engine.place(1, 0); - engine.place(2, 0); - engine.place(1, 1); - engine.place(2, 1); - - // It might seem weird to set prefer_remote = true just to test - // resetGroups, but is a necessary hack to bypass initialization of - // the OGSScoreEstimation library - const prefer_remote = true; - const se = new ScoreEstimator(undefined, engine, 10, 0.5, prefer_remote); + const se = new ScoreEstimator(undefined, engine, trials, tolerance, prefer_remote); await se.when_ready; @@ -93,4 +95,15 @@ describe("ScoreEstimator", () => { ]); expect(se.group_list.map((group) => group.color)).toEqual([0, 1, 2, 0]); }); + + test("amount and winner", async () => { + const se = new ScoreEstimator(undefined, engine, 10, 0.5, prefer_remote); + + await se.when_ready; + + // Though these properties are not used within Goban, + // They are used in the UI as of this writing. + expect(se.winner).toBe("White"); + expect(se.amount).toBe(0.5); + }); }); From 8149a84f77ba8d7a579f3f81ec3dee3c715089ae Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 16:06:34 -0700 Subject: [PATCH 17/19] Remove amount_frational (unused) --- src/ScoreEstimator.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 0905be08..63c463c1 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -256,7 +256,6 @@ export class ScoreEstimator { tolerance: number; group_list: Array; amount: number = NaN; - amount_fractional: string = "[unset]"; ownership: Array>; territory: Array>; trials: number; @@ -442,11 +441,9 @@ export class ScoreEstimator { if (typeof score === "undefined") { this.winner = this.estimated_hard_score > 0 ? _("Black") : _("White"); this.amount = Math.abs(this.estimated_hard_score); - this.amount_fractional = Math.abs(this.estimated_hard_score).toFixed(1); } else { this.winner = score > 0 ? _("Black") : _("White"); this.amount = Math.abs(score); - this.amount_fractional = Math.abs(score).toFixed(1); } if (this.goban_callback && this.goban_callback.updateScoreEstimation) { From 7ad19958549d88dc8bfdf6d316883dd913e5682e Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Wed, 25 Oct 2023 20:38:16 -0700 Subject: [PATCH 18/19] Remove currentMarker, since IDs are now generated by GoStoneGroups --- src/ScoreEstimator.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 63c463c1..78c09eca 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -250,7 +250,6 @@ export class ScoreEstimator { engine: GoEngine; groups: Array>; - currentMarker: number; removal: Array>; goban_callback?: GobanCore; tolerance: number; @@ -274,7 +273,6 @@ export class ScoreEstimator { ) { this.goban_callback = goban_callback; - this.currentMarker = 1; this.engine = engine; this.width = engine.width; this.height = engine.height; @@ -595,9 +593,6 @@ export class ScoreEstimator { getGroup(x: number, y: number): SEGroup { return this.groups[y][x]; } - incrementCurrentMarker(): void { - ++this.currentMarker; - } /** * This gets run after we've instructed the estimator how/when to fill dame, From 0d30b3092dd4f14a1af678030edb87ef359b4412 Mon Sep 17 00:00:00 2001 From: "Benjamin P. Jones" Date: Thu, 26 Oct 2023 18:31:19 -0700 Subject: [PATCH 19/19] Re-add prisoners to the ScoreEstimateRequest These properties are still referenced on the backend. --- src/ScoreEstimator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ScoreEstimator.ts b/src/ScoreEstimator.ts index 78c09eca..507042be 100644 --- a/src/ScoreEstimator.ts +++ b/src/ScoreEstimator.ts @@ -47,6 +47,8 @@ export interface ScoreEstimateRequest { height: number; board_state: Array>; rules: GoEngineRules; + black_prisoners?: number; + white_prisoners?: number; komi?: number; jwt: string; }