IPVGO: Balance and improvements for offline bonus time cycles (#1356)

This commit is contained in:
Michael Ficocelli 2024-06-05 22:39:22 -04:00 committed by GitHub
parent 463d4cdb1d
commit 481938a2fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 30 deletions

@ -28,7 +28,7 @@ let currentTurnResolver: (() => void) | null = null;
/** /**
* Retrieves a move from the current faction in response to the player's move * Retrieves a move from the current faction in response to the player's move
*/ */
export function makeAIMove(boardState: BoardState): Promise<Play> { export function makeAIMove(boardState: BoardState, useOfflineCycles = true): Promise<Play> {
// If AI is already taking their turn, return the existing turn. // If AI is already taking their turn, return the existing turn.
if (isAiThinking) { if (isAiThinking) {
return Go.nextTurn; return Go.nextTurn;
@ -43,31 +43,33 @@ export function makeAIMove(boardState: BoardState): Promise<Play> {
} }
// If an AI is in use, find the faction's move in response, and resolve the Go.nextTurn promise once it is found and played. // If an AI is in use, find the faction's move in response, and resolve the Go.nextTurn promise once it is found and played.
else { else {
Go.nextTurn = getMove(boardState, GoColor.white, Go.currentGame.ai).then(async (play): Promise<Play> => { Go.nextTurn = getMove(boardState, GoColor.white, Go.currentGame.ai, useOfflineCycles).then(
if (boardState !== Go.currentGame) return play; //Stale game async (play): Promise<Play> => {
if (boardState !== Go.currentGame) return play; //Stale game
// Handle AI passing // Handle AI passing
if (play.type === GoPlayType.pass) { if (play.type === GoPlayType.pass) {
passTurn(boardState, GoColor.white); passTurn(boardState, GoColor.white);
// if passTurn called endGoGame, or the player has no valid moves left, the move should be shown as a game over // if passTurn called endGoGame, or the player has no valid moves left, the move should be shown as a game over
if (boardState.previousPlayer === null || !getAllValidMoves(boardState, GoColor.black).length) { if (boardState.previousPlayer === null || !getAllValidMoves(boardState, GoColor.black).length) {
return { type: GoPlayType.gameOver, x: null, y: null }; return { type: GoPlayType.gameOver, x: null, y: null };
}
return play;
} }
// Handle AI making a move
await waitCycle(useOfflineCycles);
const aiUpdatedBoard = makeMove(boardState, play.x, play.y, GoColor.white);
// Handle the AI breaking. This shouldn't ever happen.
if (!aiUpdatedBoard) {
boardState.previousPlayer = GoColor.white;
console.error(`Invalid AI move attempted: ${play.x}, ${play.y}. This should not happen.`);
}
return play; return play;
} },
);
// Handle AI making a move
await waitCycle();
const aiUpdatedBoard = makeMove(boardState, play.x, play.y, GoColor.white);
// Handle the AI breaking. This shouldn't ever happen.
if (!aiUpdatedBoard) {
boardState.previousPlayer = GoColor.white;
console.error(`Invalid AI move attempted: ${play.x}, ${play.y}. This should not happen.`);
}
return play;
});
} }
// Once the AI moves (or the player playing as white with No AI moves), // Once the AI moves (or the player playing as white with No AI moves),
@ -112,9 +114,10 @@ export async function getMove(
boardState: BoardState, boardState: BoardState,
player: GoColor, player: GoColor,
opponent: GoOpponent, opponent: GoOpponent,
useOfflineCycles = true,
rngOverride?: number, rngOverride?: number,
): Promise<Play & { type: GoPlayType.move | GoPlayType.pass }> { ): Promise<Play & { type: GoPlayType.move | GoPlayType.pass }> {
await waitCycle(); await waitCycle(useOfflineCycles);
const rng = new WHRNG(rngOverride || Player.totalPlaytime); const rng = new WHRNG(rngOverride || Player.totalPlaytime);
const smart = isSmart(opponent, rng.random()); const smart = isSmart(opponent, rng.random());
const moves = getMoveOptions(boardState, player, rng.random(), smart); const moves = getMoveOptions(boardState, player, rng.random(), smart);
@ -142,7 +145,7 @@ export async function getMove(
.filter((point) => evaluateIfMoveIsValid(boardState, point.x, point.y, player, false)); .filter((point) => evaluateIfMoveIsValid(boardState, point.x, point.y, player, false));
const chosenMove = moveOptions[Math.floor(rng.random() * moveOptions.length)]; const chosenMove = moveOptions[Math.floor(rng.random() * moveOptions.length)];
await waitCycle(); await waitCycle(useOfflineCycles);
if (chosenMove) { if (chosenMove) {
//console.debug(`Non-priority move chosen: ${chosenMove.x} ${chosenMove.y}`); //console.debug(`Non-priority move chosen: ${chosenMove.x} ${chosenMove.y}`);
@ -817,9 +820,9 @@ export function sleep(ms: number): Promise<void> {
* Spend some time waiting to allow the UI & CSS to render smoothly * Spend some time waiting to allow the UI & CSS to render smoothly
* If bonus time is available, significantly decrease the length of the wait * If bonus time is available, significantly decrease the length of the wait
*/ */
function waitCycle(): Promise<void> { function waitCycle(useOfflineCycles = true): Promise<void> {
if (Go.storedCycles > 0) { if (useOfflineCycles && Go.storedCycles > 0) {
Go.storedCycles -= 1; Go.storedCycles -= 2;
return sleep(40); return sleep(40);
} }
return sleep(200); return sleep(200);

@ -115,7 +115,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
return; return;
} }
const move = await makeAIMove(boardState); const move = await makeAIMove(boardState, false);
if (move.type === GoPlayType.pass) { if (move.type === GoPlayType.pass) {
SnackbarEvents.emit(`The opponent passes their turn; It is now your turn to move.`, ToastVariant.WARNING, 4000); SnackbarEvents.emit(`The opponent passes their turn; It is now your turn to move.`, ToastVariant.WARNING, 4000);

@ -31,7 +31,7 @@ describe("Go AI tests", () => {
it("prioritizes eye creation moves for Illuminati", async () => { it("prioritizes eye creation moves for Illuminati", async () => {
const board = ["...O...", "OOOO...", ".......", ".......", ".......", ".......", "......."]; const board = ["...O...", "OOOO...", ".......", ".......", ".......", ".......", "......."];
const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus); const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus);
const move = await getMove(boardState, GoColor.white, GoOpponent.Daedalus, 0); const move = await getMove(boardState, GoColor.white, GoOpponent.Daedalus, false, 0);
expect([move.x, move.y]).toEqual([0, 1]); expect([move.x, move.y]).toEqual([0, 1]);
}); });