Skip to content

Commit

Permalink
Merge pull request #139 from benjaminpjones/kill-segroup-2
Browse files Browse the repository at this point in the history
Deprecate SEGroup
  • Loading branch information
anoek authored Nov 12, 2023
2 parents fa4589e + 7b0e587 commit 4dd04e9
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 181 deletions.
22 changes: 22 additions & 0 deletions src/GoStoneGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ export class GoStoneGroup {
is_territory_in_seki: boolean = false;

private __added_neighbors: { [group_id: number]: boolean };
private neighboring_space: GoStoneGroup[];
private neighboring_enemy: GoStoneGroup[];

constructor(board_state: BoardState, id: number, color: JGOFNumericPlayerColor) {
this.board_state = board_state;
this.points = [];
this.neighbors = [];
this.neighboring_space = [];
this.neighboring_enemy = [];
this.id = id;
this.color = color;
this.is_strong_eye = false;
Expand All @@ -60,6 +64,14 @@ export class GoStoneGroup {
if (!(group.id in this.__added_neighbors)) {
this.neighbors.push(group);
this.__added_neighbors[group.id] = true;

if (group.color !== this.color) {
if (group.color === JGOFNumericPlayerColor.EMPTY) {
this.neighboring_space.push(group);
} else {
this.neighboring_enemy.push(group);
}
}
}
}
addCornerGroup(x: number, y: number, group: GoStoneGroup): void {
Expand All @@ -78,6 +90,16 @@ export class GoStoneGroup {
fn(this.neighbors[i]);
}
}
foreachNeighborSpaceGroup(fn: (group: GoStoneGroup) => void): void {
for (let i = 0; i < this.neighboring_space.length; ++i) {
fn(this.neighboring_space[i]);
}
}
foreachNeighborEnemyGroup(fn: (group: GoStoneGroup) => void): void {
for (let i = 0; i < this.neighbors.length; ++i) {
fn(this.neighboring_enemy[i]);
}
}
computeIsEye(): void {
this.is_eye = false;

Expand Down
164 changes: 14 additions & 150 deletions src/ScoreEstimator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,111 +80,6 @@ export function set_local_scorer(scorer: LocalEstimator) {
local_scorer = scorer;
}

interface SEPoint {
x: number;
y: number;
}

class SEGroup {
points: Array<SEPoint>;
neighboring_enemy: Array<SEGroup>;
neighboring_space: Array<SEGroup>;
se: ScoreEstimator;
id: number;
color: JGOFNumericPlayerColor;
removed: boolean;
neighbors: Array<SEGroup>;
neighbor_map: { [group_id: string]: boolean };

constructor(se: ScoreEstimator, color: JGOFNumericPlayerColor, id: number) {
this.points = [];
this.se = se;
this.id = id;
this.color = color;
this.neighbors = [];
this.neighboring_space = [];
this.neighboring_enemy = [];
this.neighbor_map = {};
this.removed = false;
}
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) {
fn(this.points[i]);
}
}
foreachNeighboringPoint(fn: (pt: SEPoint) => void) {
const self = this;
const points = this.points;
const done_array = new Array(this.se.height * this.se.width);
for (let i = 0; i < points.length; ++i) {
done_array[points[i].x + points[i].y * this.se.width] = true;
}

function checkAndDo(x: number, y: number): void {
const idx = x + y * self.se.width;
if (done_array[idx]) {
return;
}
done_array[idx] = true;

fn({ x: x, y: y });
}

for (let i = 0; i < points.length; ++i) {
const pt = points[i];
if (pt.x - 1 >= 0) {
checkAndDo(pt.x - 1, pt.y);
}
if (pt.x + 1 !== this.se.width) {
checkAndDo(pt.x + 1, pt.y);
}
if (pt.y - 1 >= 0) {
checkAndDo(pt.x, pt.y - 1);
}
if (pt.y + 1 !== this.se.height) {
checkAndDo(pt.x, pt.y + 1);
}
}
}
addNeighbor(group: SEGroup): void {
if (!(group.id in this.neighbor_map)) {
this.neighbors.push(group);
this.neighbor_map[group.id] = true;

if (group.color === 0) {
this.neighboring_space.push(group);
} else {
this.neighboring_enemy.push(group);
}
}
}
foreachNeighborGroup(fn: (group: SEGroup) => void): void {
for (let i = 0; i < this.neighbors.length; ++i) {
fn(this.neighbors[i]);
}
}
foreachNeighborSpaceGroup(fn: (group: SEGroup) => void): void {
for (let i = 0; i < this.neighboring_space.length; ++i) {
fn(this.neighboring_space[i]);
}
}
foreachNeighborEnemyGroup(fn: (group: SEGroup) => void): void {
for (let i = 0; i < this.neighboring_enemy.length; ++i) {
fn(this.neighboring_enemy[i]);
}
}
setRemoved(removed: boolean): void {
this.removed = removed;
for (let i = 0; i < this.points.length; ++i) {
const pt = this.points[i];
this.se.setRemoved(pt.x, pt.y, removed ? 1 : 0);
}
}
}

export class ScoreEstimator {
width: number;
height: number;
Expand All @@ -209,11 +104,10 @@ export class ScoreEstimator {
};

engine: GoEngine;
groups: Array<Array<SEGroup>>;
private groups: GoStoneGroups;
removal: Array<Array<number>>;
goban_callback?: GobanCore;
tolerance: number;
group_list: Array<SEGroup>;
amount: number = NaN;
ownership: Array<Array<number>>;
territory: Array<Array<number>>;
Expand All @@ -240,15 +134,15 @@ export class ScoreEstimator {
this.board = dup(engine.board);
this.removal = 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);
this.estimated_hard_score = 0.0;
this.group_list = [];
this.trials = trials;
this.tolerance = tolerance;
this.prefer_remote = prefer_remote;

this.resetGroups();
this.territory = GoMath.makeMatrix(this.width, this.height, 0);
this.groups = new GoStoneGroups(this);

this.when_ready = this.estimateScore(this.trials, this.tolerance);
}

Expand Down Expand Up @@ -390,32 +284,6 @@ export class ScoreEstimator {
}
return ret;
}
resetGroups(): void {
this.territory = GoMath.makeMatrix(this.width, this.height, 0);
this.groups = GoMath.makeEmptyObjectMatrix(this.width, this.height);
this.group_list = [];

const go_stone_groups = new GoStoneGroups(this);

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);
});

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]);
}
}
}
foreachGroup(fn: (group: SEGroup) => void): void {
for (let i = 0; i < this.group_list.length; ++i) {
fn(this.group_list[i]);
}
}
handleClick(i: number, j: number, modkey: boolean) {
if (modkey) {
this.setRemoved(i, j, !this.removal[j][i] ? 1 : 0);
Expand All @@ -427,16 +295,21 @@ export class ScoreEstimator {
/* empty */
});
}

private removeGroup(g: GoStoneGroup, removing: boolean) {
g.foreachStone(({ x, y }) => this.setRemoved(x, y, removing ? 1 : 0));
}

toggleMetaGroupRemoval(x: number, y: number): void {
const already_done: { [k: string]: boolean } = {};
const space_groups: Array<SEGroup> = [];
const space_groups: Array<GoStoneGroup> = [];
let group_color: JGOFNumericPlayerColor;

try {
if (x >= 0 && y >= 0) {
const removing = !this.removal[y][x];
const group = this.getGroup(x, y);
group.setRemoved(removing);
this.removeGroup(group, removing);

group_color = this.board[y][x];
if (group_color === 0) {
Expand All @@ -458,7 +331,7 @@ export class ScoreEstimator {
if (!already_done[g.id]) {
already_done[g.id] = true;
if (g.color === group_color) {
g.setRemoved(removing);
this.removeGroup(g, removing);
g.foreachNeighborSpaceGroup((gspace) => {
if (!already_done[gspace.id]) {
space_groups.push(gspace);
Expand Down Expand Up @@ -506,8 +379,8 @@ export class ScoreEstimator {
}
return ret;
}
getGroup(x: number, y: number): SEGroup {
return this.groups[y][x];
getGroup(x: number, y: number): GoStoneGroup {
return this.groups.groups[this.groups.group_id_map[y][x]];
}

/**
Expand Down Expand Up @@ -683,12 +556,3 @@ function sum_board(board: GoMath.NumberMatrix) {
}
return sum;
}

/**
* 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;
}
31 changes: 0 additions & 31 deletions src/__tests__/ScoreEstimator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ describe("ScoreEstimator", () => {
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;

Expand All @@ -78,33 +74,6 @@ describe("ScoreEstimator", () => {
set_remote_scorer(undefined as any);
});

test("resetGroups", async () => {
const se = new ScoreEstimator(undefined, engine, trials, tolerance, 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]);
});

test("amount and winner", async () => {
const se = new ScoreEstimator(undefined, engine, trials, tolerance, false);

Expand Down

0 comments on commit 4dd04e9

Please sign in to comment.