Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate SEGroup #139

Merged
merged 1 commit into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -58,10 +58,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 @@ -77,33 +73,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
Loading