IPVGO: Support bonus cycles from offline time (#1345)

This commit is contained in:
Michael Ficocelli 2024-06-04 21:43:29 -04:00 committed by GitHub
parent 5f6a5c8785
commit bd5c502f53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 42 additions and 4 deletions

@ -11,6 +11,7 @@ export class GoObject {
currentGame: BoardState = getNewBoardState(7); currentGame: BoardState = getNewBoardState(7);
stats: PartialRecord<GoOpponent, OpponentStats> = {}; stats: PartialRecord<GoOpponent, OpponentStats> = {};
nextTurn: Promise<Play> = Promise.resolve({ type: GoPlayType.gameOver, x: null, y: null }); nextTurn: Promise<Play> = Promise.resolve({ type: GoPlayType.gameOver, x: null, y: null });
storedCycles: number = 0;
prestigeAugmentation() { prestigeAugmentation() {
for (const stats of getRecordValues(this.stats)) { for (const stats of getRecordValues(this.stats)) {
@ -24,6 +25,16 @@ export class GoObject {
this.currentGame = getNewBoardState(7); this.currentGame = getNewBoardState(7);
this.stats = {}; this.stats = {};
} }
/**
* Stores offline time that is consumed to speed up the AI.
* Only stores offline time if the player has actually been using the mechanic.
*/
storeCycles(offlineCycles: number) {
if (this.previousGame) {
this.storedCycles += offlineCycles ?? 0;
}
}
} }
export const Go = new GoObject(); export const Go = new GoObject();

@ -21,6 +21,7 @@ type SaveFormat = {
previousGame: PreviousGameSaveData; previousGame: PreviousGameSaveData;
currentGame: CurrentGameSaveData; currentGame: CurrentGameSaveData;
stats: PartialRecord<GoOpponent, OpponentStats>; stats: PartialRecord<GoOpponent, OpponentStats>;
storedCycles: number;
}; };
export function getGoSave(): SaveFormat { export function getGoSave(): SaveFormat {
@ -40,6 +41,7 @@ export function getGoSave(): SaveFormat {
passCount: Go.currentGame.passCount, passCount: Go.currentGame.passCount,
}, },
stats: Go.stats, stats: Go.stats,
storedCycles: Go.storedCycles,
}; };
} }
@ -78,6 +80,7 @@ export function loadGo(data: unknown): boolean {
Go.currentGame = currentGame; Go.currentGame = currentGame;
Go.previousGame = previousGame; Go.previousGame = previousGame;
Go.stats = stats; Go.stats = stats;
Go.storeCycles(loadStoredCycles(parsedData.storedCycles));
// If it's the AI's turn, initiate their turn, which will populate nextTurn // If it's the AI's turn, initiate their turn, which will populate nextTurn
if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) makeAIMove(currentGame); if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) makeAIMove(currentGame);
@ -178,3 +181,11 @@ function loadSimpleBoard(simpleBoard: unknown, requiredSize?: number): SimpleBoa
} }
return simpleBoard; return simpleBoard;
} }
function loadStoredCycles(storedCycles: unknown): number {
if (!storedCycles || isNaN(+storedCycles)) {
return 0;
}
return +storedCycles;
}

@ -57,7 +57,7 @@ export function makeAIMove(boardState: BoardState): Promise<Play> {
} }
// Handle AI making a move // Handle AI making a move
await sleep(500); await waitCycle();
const aiUpdatedBoard = makeMove(boardState, play.x, play.y, GoColor.white); const aiUpdatedBoard = makeMove(boardState, play.x, play.y, GoColor.white);
// Handle the AI breaking. This shouldn't ever happen. // Handle the AI breaking. This shouldn't ever happen.
@ -114,7 +114,7 @@ export async function getMove(
opponent: GoOpponent, opponent: GoOpponent,
rngOverride?: number, rngOverride?: number,
): Promise<Play & { type: GoPlayType.move | GoPlayType.pass }> { ): Promise<Play & { type: GoPlayType.move | GoPlayType.pass }> {
await sleep(300); await waitCycle();
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,9 +142,9 @@ 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();
if (chosenMove) { if (chosenMove) {
await sleep(200);
//console.debug(`Non-priority move chosen: ${chosenMove.x} ${chosenMove.y}`); //console.debug(`Non-priority move chosen: ${chosenMove.x} ${chosenMove.y}`);
return { type: GoPlayType.move, x: chosenMove.x, y: chosenMove.y }; return { type: GoPlayType.move, x: chosenMove.x, y: chosenMove.y };
} }
@ -786,7 +786,7 @@ function getMoveOptions(
}; };
async function retrieveMoveOption(id: keyof typeof moveOptions): Promise<Move | null> { async function retrieveMoveOption(id: keyof typeof moveOptions): Promise<Move | null> {
await sleep(100); await waitCycle();
if (moveOptions[id] !== undefined) { if (moveOptions[id] !== undefined) {
return moveOptions[id] ?? null; return moveOptions[id] ?? null;
} }
@ -813,6 +813,18 @@ export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }
/**
* 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;
return sleep(40);
}
return sleep(200);
}
export function showWorldDemon() { export function showWorldDemon() {
return Player.hasAugmentation(AugmentationName.TheRedPill, true) && Player.sourceFileLvl(1); return Player.hasAugmentation(AugmentationName.TheRedPill, true) && Player.sourceFileLvl(1);
} }

@ -44,6 +44,7 @@ import { setupUncaughtPromiseHandler } from "./UncaughtPromiseHandler";
import { Button, Typography } from "@mui/material"; import { Button, Typography } from "@mui/material";
import { SnackbarEvents } from "./ui/React/Snackbar"; import { SnackbarEvents } from "./ui/React/Snackbar";
import { SaveData } from "./types"; import { SaveData } from "./types";
import { Go } from "./Go/Go";
/** Game engine. Handles the main game loop. */ /** Game engine. Handles the main game loop. */
const Engine: { const Engine: {
@ -330,6 +331,8 @@ const Engine: {
// Bladeburner offline progress // Bladeburner offline progress
if (Player.bladeburner) Player.bladeburner.storeCycles(numCyclesOffline); if (Player.bladeburner) Player.bladeburner.storeCycles(numCyclesOffline);
Go.storeCycles(numCyclesOffline);
staneksGift.process(numCyclesOffline); staneksGift.process(numCyclesOffline);
// Sleeves offline progress // Sleeves offline progress

@ -45,6 +45,7 @@ exports[`Check Save File Continuity GoSave continuity 1`] = `
}, },
"previousGame": null, "previousGame": null,
"stats": {}, "stats": {},
"storedCycles": 0,
} }
`; `;