IPVGO: Provide API for getting game stats per opponent (#1255)

Give users access to wins, losses, stat bonuses, and favor gained
This commit is contained in:
Michael Ficocelli 2024-05-10 04:57:03 -04:00 committed by GitHub
parent 35c32e2871
commit b53c35126e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 100 additions and 3 deletions

@ -17,5 +17,7 @@ analysis: {
getLiberties(): number[][]; getLiberties(): number[][];
getControlledEmptyNodes(): string[]; getControlledEmptyNodes(): string[];
getStats(): Partial<Record<GoOpponent, SimpleOpponentStats>>;
}; };
``` ```

@ -16,7 +16,7 @@ export interface Go
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [analysis](./bitburner.go.analysis.md) | | { getValidMoves(): boolean\[\]\[\]; getChains(): (number \| null)\[\]\[\]; getLiberties(): number\[\]\[\]; getControlledEmptyNodes(): string\[\]; } | Tools to analyze the IPvGO subnet. | | [analysis](./bitburner.go.analysis.md) | | { getValidMoves(): boolean\[\]\[\]; getChains(): (number \| null)\[\]\[\]; getLiberties(): number\[\]\[\]; getControlledEmptyNodes(): string\[\]; getStats(): Partial&lt;Record&lt;[GoOpponent](./bitburner.goopponent.md)<!-- -->, [SimpleOpponentStats](./bitburner.simpleopponentstats.md)<!-- -->&gt;&gt;; } | Tools to analyze the IPvGO subnet. |
| [cheat](./bitburner.go.cheat.md) | | { getCheatSuccessChance(): number; removeRouter( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; playTwoMoves( x1: number, y1: number, x2: number, y2: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; repairOfflineNode( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; destroyNode( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; } | Illicit and dangerous IPvGO tools. Not for the faint of heart. Requires Bitnode 14.2 to use. | | [cheat](./bitburner.go.cheat.md) | | { getCheatSuccessChance(): number; removeRouter( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; playTwoMoves( x1: number, y1: number, x2: number, y2: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; repairOfflineNode( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; destroyNode( x: number, y: number, ): Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;; } | Illicit and dangerous IPvGO tools. Not for the faint of heart. Requires Bitnode 14.2 to use. |
## Methods ## Methods

@ -162,6 +162,7 @@
| [PlayerRequirement](./bitburner.playerrequirement.md) | Structured interface to requirements for joining a faction or company. For fields with numerical value &gt; 0, the player must have at least this value. For fields with numerical value &lt;<!-- -->= 0, the player must have at most this value. For "not", the sub-condition must be failed instead of passed. For "someCondition", at least one sub-condition must be passed. | | [PlayerRequirement](./bitburner.playerrequirement.md) | Structured interface to requirements for joining a faction or company. For fields with numerical value &gt; 0, the player must have at least this value. For fields with numerical value &lt;<!-- -->= 0, the player must have at most this value. For "not", the sub-condition must be failed instead of passed. For "someCondition", at least one sub-condition must be passed. |
| [ReactNode](./bitburner.reactnode.md) | <p>A stand-in for the real React.ReactNode. A [ReactElement](./bitburner.reactelement.md) is rendered dynamically with React. number and string are displayed directly. boolean, null, and undefined are ignored and not rendered. An array of ReactNodes will display all members of that array sequentially.</p><p>Use React.createElement to make the ReactElement type, see [creating an element without jsx](https://react.dev/reference/react/createElement#creating-an-element-without-jsx) from the official React documentation.</p> | | [ReactNode](./bitburner.reactnode.md) | <p>A stand-in for the real React.ReactNode. A [ReactElement](./bitburner.reactelement.md) is rendered dynamically with React. number and string are displayed directly. boolean, null, and undefined are ignored and not rendered. An array of ReactNodes will display all members of that array sequentially.</p><p>Use React.createElement to make the ReactElement type, see [creating an element without jsx](https://react.dev/reference/react/createElement#creating-an-element-without-jsx) from the official React documentation.</p> |
| [ScriptArg](./bitburner.scriptarg.md) | | | [ScriptArg](./bitburner.scriptarg.md) | |
| [SimpleOpponentStats](./bitburner.simpleopponentstats.md) | |
| [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) | | | [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) | |
| [SleeveClassTask](./bitburner.sleeveclasstask.md) | | | [SleeveClassTask](./bitburner.sleeveclasstask.md) | |
| [SleeveCompanyTask](./bitburner.sleevecompanytask.md) | | | [SleeveCompanyTask](./bitburner.sleevecompanytask.md) | |

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SimpleOpponentStats](./bitburner.simpleopponentstats.md)
## SimpleOpponentStats type
**Signature:**
```typescript
type SimpleOpponentStats = {
wins: number;
losses: number;
winStreak: number;
highestWinStreak: number;
favor: number;
bonusPercent: number;
bonusDescription: string;
};
```

@ -79,3 +79,13 @@ export type OpponentStats = {
highestWinStreak: number; highestWinStreak: number;
favor: number; favor: number;
}; };
export type SimpleOpponentStats = {
wins: number;
losses: number;
winStreak: number;
highestWinStreak: number;
favor: number;
bonusPercent: number;
bonusDescription: string;
};

@ -1,4 +1,4 @@
import type { BoardState, Play } from "../Types"; import { BoardState, Play, SimpleOpponentStats } from "../Types";
import { Player } from "@player"; import { Player } from "@player";
import { AugmentationName, GoColor, GoOpponent, GoPlayType, GoValidity } from "@enums"; import { AugmentationName, GoColor, GoOpponent, GoPlayType, GoValidity } from "@enums";
@ -11,8 +11,10 @@ import {
getControlledSpace, getControlledSpace,
simpleBoardFromBoard, simpleBoardFromBoard,
} from "../boardAnalysis/boardAnalysis"; } from "../boardAnalysis/boardAnalysis";
import { getScore, resetWinstreak } from "../boardAnalysis/scoring"; import { getOpponentStats, getScore, resetWinstreak } from "../boardAnalysis/scoring";
import { WHRNG } from "../../Casino/RNG"; import { WHRNG } from "../../Casino/RNG";
import { getRecordKeys } from "../../Types/Record";
import { CalculateEffect, getEffectTypeForFaction } from "./effect";
/** /**
* Check the move based on the current settings * Check the move based on the current settings
@ -362,6 +364,30 @@ export function resetBoardState(
return simpleBoardFromBoard(Go.currentGame.board); return simpleBoardFromBoard(Go.currentGame.board);
} }
/**
* Retrieve and clean up stats for each opponent played against
*/
export function getStats() {
const statDetails: Partial<Record<GoOpponent, SimpleOpponentStats>> = {};
for (const opponent of getRecordKeys(Go.stats)) {
const details = getOpponentStats(opponent);
const nodePower = getOpponentStats(opponent).nodePower;
const effectPercent = (CalculateEffect(nodePower, opponent) - 1) * 100;
const effectDescription = getEffectTypeForFaction(opponent);
statDetails[opponent] = {
wins: details.wins,
losses: details.losses,
winStreak: details.winStreak,
highestWinStreak: details.highestWinStreak,
favor: details.favor,
bonusPercent: effectPercent,
bonusDescription: effectDescription,
};
}
return statDetails;
}
/** Validate singularity access by throwing an error if the player does not have access. */ /** Validate singularity access by throwing an error if the player does not have access. */
export function checkCheatApiAccess(error: (s: string) => void): void { export function checkCheatApiAccess(error: (s: string) => void): void {
const hasSourceFile = Player.sourceFileLvl(14) > 1; const hasSourceFile = Player.sourceFileLvl(14) > 1;

@ -260,6 +260,7 @@ const go = {
getChains: 16, getChains: 16,
getLiberties: 16, getLiberties: 16,
getControlledEmptyNodes: 16, getControlledEmptyNodes: 16,
getStats: 0,
}, },
cheat: { cheat: {
getCheatSuccessChance: 1, getCheatSuccessChance: 1,

@ -18,6 +18,7 @@ import {
getGameState, getGameState,
getLiberties, getLiberties,
getOpponentNextMove, getOpponentNextMove,
getStats,
getValidMoves, getValidMoves,
handlePassTurn, handlePassTurn,
makePlayerMove, makePlayerMove,
@ -85,6 +86,9 @@ export function NetscriptGo(): InternalAPI<NSGo> {
getControlledEmptyNodes: () => () => { getControlledEmptyNodes: () => () => {
return getControlledEmptyNodes(); return getControlledEmptyNodes();
}, },
getStats: () => () => {
return getStats();
},
}, },
cheat: { cheat: {
getCheatSuccessChance: (ctx: NetscriptContext) => () => { getCheatSuccessChance: (ctx: NetscriptContext) => () => {

@ -3949,6 +3949,17 @@ type GoOpponent =
| "Illuminati" | "Illuminati"
| "????????????"; | "????????????";
/** @public */
type SimpleOpponentStats = {
wins: number;
losses: number;
winStreak: number;
highestWinStreak: number;
favor: number;
bonusPercent: number;
bonusDescription: string;
};
/** /**
* IPvGO api * IPvGO api
* @public * @public
@ -4167,6 +4178,28 @@ export interface Go {
* (This is intentionally expensive; you can derive this info from just getBoardState() ) * (This is intentionally expensive; you can derive this info from just getBoardState() )
*/ */
getControlledEmptyNodes(): string[]; getControlledEmptyNodes(): string[];
/**
* Displays the game history, captured nodes, and gained bonuses for each opponent you have played against.
*
* The details are keyed by opponent name, in this structure:
*
* <pre lang="javascript">
* {
* <OpponentName>: {
* wins: number,
* losses: number,
* winStreak: number,
* highestWinStreak: number,
* favor: number,
* bonusPercent: number,
* bonusDescription: string,
* }
* }
* </pre>
*
*/
getStats(): Partial<Record<GoOpponent, SimpleOpponentStats>>;
}; };
/** /**