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
*/
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 (isAiThinking) {
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.
else {
Go.nextTurn = getMove(boardState, GoColor.white, Go.currentGame.ai).then(async (play): Promise<Play> => {
if (boardState !== Go.currentGame) return play; //Stale game
Go.nextTurn = getMove(boardState, GoColor.white, Go.currentGame.ai, useOfflineCycles).then(
async (play): Promise<Play> => {
if (boardState !== Go.currentGame) return play; //Stale game
// Handle AI passing
if (play.type === GoPlayType.pass) {
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 (boardState.previousPlayer === null || !getAllValidMoves(boardState, GoColor.black).length) {
return { type: GoPlayType.gameOver, x: null, y: null };
// Handle AI passing
if (play.type === GoPlayType.pass) {
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 (boardState.previousPlayer === null || !getAllValidMoves(boardState, GoColor.black).length) {
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;
}
// 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),
@ -112,9 +114,10 @@ export async function getMove(
boardState: BoardState,
player: GoColor,
opponent: GoOpponent,
useOfflineCycles = true,
rngOverride?: number,
): Promise<Play & { type: GoPlayType.move | GoPlayType.pass }> {
await waitCycle();
await waitCycle(useOfflineCycles);
const rng = new WHRNG(rngOverride || Player.totalPlaytime);
const smart = isSmart(opponent, rng.random());
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));
const chosenMove = moveOptions[Math.floor(rng.random() * moveOptions.length)];
await waitCycle();
await waitCycle(useOfflineCycles);
if (chosenMove) {
//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
* If bonus time is available, significantly decrease the length of the wait
*/
function waitCycle(): Promise<void> {
if (Go.storedCycles > 0) {
Go.storedCycles -= 1;
function waitCycle(useOfflineCycles = true): Promise<void> {
if (useOfflineCycles && Go.storedCycles > 0) {
Go.storedCycles -= 2;
return sleep(40);
}
return sleep(200);

@ -115,7 +115,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
return;
}
const move = await makeAIMove(boardState);
const move = await makeAIMove(boardState, false);
if (move.type === GoPlayType.pass) {
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 () => {
const board = ["...O...", "OOOO...", ".......", ".......", ".......", ".......", "......."];
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]);
});