IPVGO: Nerf overly-difficult handicap, [issue #1169] misc bugfixes (#1188)

* IPVGO: Nerf overly-difficult handicap on 5x5 board

* IPVGO: Tweak 5x5 handicap
This commit is contained in:
Michael Ficocelli 2024-03-25 19:12:35 -04:00 committed by GitHub
parent 714c1cc9d6
commit d8de22a273
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 1 deletions

@ -24,6 +24,7 @@ export function getNewBoardState(
if (ai === GoOpponent.w0r1d_d43m0n) { if (ai === GoOpponent.w0r1d_d43m0n) {
boardToCopy = resetCoordinates(rotate90Degrees(boardFromSimpleBoard(bitverseBoardShape))); boardToCopy = resetCoordinates(rotate90Degrees(boardFromSimpleBoard(bitverseBoardShape)));
boardSize = 19; boardSize = 19;
applyObstacles = false;
} }
const newBoardState: BoardState = { const newBoardState: BoardState = {
@ -64,7 +65,13 @@ export function getNewBoardState(
export function getHandicap(boardSize: number, opponent: GoOpponent) { export function getHandicap(boardSize: number, opponent: GoOpponent) {
// Illuminati and WD get a few starting routers // Illuminati and WD get a few starting routers
if (opponent === GoOpponent.Illuminati || opponent === GoOpponent.w0r1d_d43m0n) { if (opponent === GoOpponent.Illuminati || opponent === GoOpponent.w0r1d_d43m0n) {
return ceil(boardSize * 0.35); return {
[5]: 1,
[7]: 3,
[9]: 4,
[13]: 5,
[19]: 7,
}[boardSize];
} }
return 0; return 0;
} }
@ -124,6 +131,13 @@ export function applyHandicap(board: Board, handicap: number): void {
const handicapMoveOptions = getExpansionMoveArray(board, availableMoves); const handicapMoveOptions = getExpansionMoveArray(board, availableMoves);
const handicapMoves: Move[] = []; const handicapMoves: Move[] = [];
// Special handling for 5x5: extra weight on handicap piece in the center of the board
if (availableMoves.length < 26 && board[2][2] && Math.random() < 0.2) {
board[2][2].color = GoColor.white;
updateChains(board);
return;
}
// select random distinct moves from the move options list up to the specified handicap amount // select random distinct moves from the move options list up to the specified handicap amount
for (let i = 0; i < handicap && i < handicapMoveOptions.length; i++) { for (let i = 0; i < handicap && i < handicapMoveOptions.length; i++) {
const index = floor(Math.random() * handicapMoveOptions.length); const index = floor(Math.random() * handicapMoveOptions.length);

@ -0,0 +1,77 @@
import { getNewBoardState, makeMove, passTurn, updateCaptures } from "../../../src/Go/boardState/boardState";
import { GoColor, GoOpponent } from "@enums";
import { boardFromSimpleBoard, simpleBoardFromBoard } from "../../../src/Go/boardAnalysis/boardAnalysis";
import { resetCoordinates, rotate90Degrees } from "../../../src/Go/boardState/offlineNodes";
import { bitverseBoardShape } from "../../../src/Go/Constants";
describe("Board analysis utility tests", () => {
it("Correctly applies the board size and handicap for 5x5 board", () => {
const result = getNewBoardState(5, GoOpponent.Illuminati, false);
const whitePieceCount = simpleBoardFromBoard(result.board)
.join("")
.split("")
.filter((p) => p === "O").length;
expect(whitePieceCount).toEqual(1);
expect(result).toEqual({
board: expect.any(Object),
previousPlayer: GoColor.white,
previousBoards: [],
ai: GoOpponent.Illuminati,
passCount: 0,
cheatCount: 0,
});
});
it("Correctly applies the board size and handicap for the special opponent", () => {
const result = getNewBoardState(5, GoOpponent.w0r1d_d43m0n, true);
const simpleBoard = simpleBoardFromBoard(result.board);
const whitePieceCount = simpleBoard
.join("")
.split("")
.filter((p) => p === "O").length;
// Handicap of 7
expect(whitePieceCount).toEqual(7);
// Special board: 19x19 (even if other board size is passed in)
expect(result.board[0]?.length).toEqual(19);
// Special board shape
const boardWithNoRouters = simpleBoard.map((row) =>
row
.split("")
.map((p) => (p === "O" ? "." : p))
.join(""),
);
const specialBoardTemplate = simpleBoardFromBoard(
resetCoordinates(rotate90Degrees(boardFromSimpleBoard(bitverseBoardShape))),
);
expect(boardWithNoRouters).toEqual(specialBoardTemplate);
});
it("Correctly applies moves made", () => {
const result = getNewBoardState(5, GoOpponent.Daedalus, false);
makeMove(result, 1, 1, GoColor.black);
expect(simpleBoardFromBoard(result.board)).toEqual([".....", ".X...", ".....", ".....", "....."]);
});
it("Correctly applies passes made", () => {
const result = getNewBoardState(5, GoOpponent.Daedalus, false);
expect(result.previousPlayer).toEqual(GoColor.white);
passTurn(result, GoColor.black);
expect(result.passCount).toEqual(1);
expect(result.previousPlayer).toEqual(GoColor.black);
passTurn(result, GoColor.white);
expect(result.passCount).toEqual(2);
expect(result.previousPlayer).toEqual(null);
});
it("Correctly updates captures and chains", () => {
const boardState = boardFromSimpleBoard(["OX...", "XX...", ".....", ".....", "....."]);
expect(boardState[0][0]?.color).toEqual(GoColor.white);
updateCaptures(boardState, GoColor.black, true);
expect(boardState[0][0]?.color).toEqual(GoColor.empty);
expect(boardState[0][1]?.chain).toEqual("0,1");
});
});