Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPVGO: Add support for highlighting nodes and adding small text #1996

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions markdown/bitburner.goanalysis.clearallpointhighlights.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GoAnalysis](./bitburner.goanalysis.md) &gt; [clearAllPointHighlights](./bitburner.goanalysis.clearallpointhighlights.md)

## GoAnalysis.clearAllPointHighlights() method

Removes all highlights from the board.

**Signature:**

```typescript
clearAllPointHighlights(): void;
```
**Returns:**

void

25 changes: 25 additions & 0 deletions markdown/bitburner.goanalysis.clearpointhighlight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GoAnalysis](./bitburner.goanalysis.md) &gt; [clearPointHighlight](./bitburner.goanalysis.clearpointhighlight.md)

## GoAnalysis.clearPointHighlight() method

Removes the highlight color and text from the specified node.

**Signature:**

```typescript
clearPointHighlight(x, y): void;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| x | (not declared) | the x coordinate to remove highlight from |
| y | (not declared) | the y coordinate to remove highlight from |

**Returns:**

void

27 changes: 27 additions & 0 deletions markdown/bitburner.goanalysis.highlightpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GoAnalysis](./bitburner.goanalysis.md) &gt; [highlightPoint](./bitburner.goanalysis.highlightpoint.md)

## GoAnalysis.highlightPoint() method

Adds a colored circle indicator to the specified point. These indicators are removed once a move is played.

**Signature:**

```typescript
highlightPoint(x, y, color = "", text = ""): void;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| x | (not declared) | the x coordinate to highlight |
| y | (not declared) | the y coordinate to highlight |
| color | (not declared) | _(Optional)_ optional: the color to use for the circle. Can be given an RGB string like "\#FFF000", or "none" to clear it, or one of these color names from the selected theme: "hack" (green), "hp" (red), "money" (yellow), "int" (blue), "cha" (purple) |
| text | (not declared) | _(Optional)_ optional: text to add to the node (replaces the default A.1 or B5 seen on hover). Should be kept short to fit well. |

**Returns:**

void

3 changes: 3 additions & 0 deletions markdown/bitburner.goanalysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ export interface GoAnalysis

| Method | Description |
| --- | --- |
| [clearAllPointHighlights()](./bitburner.goanalysis.clearallpointhighlights.md) | Removes all highlights from the board. |
| [clearPointHighlight(x, y)](./bitburner.goanalysis.clearpointhighlight.md) | Removes the highlight color and text from the specified node. |
| [getChains(boardState)](./bitburner.goanalysis.getchains.md) | <p>Returns an ID for each point. All points that share an ID are part of the same network (or "chain"). Empty points are also given chain IDs to represent continuous empty space. Dead nodes are given the value <code>null.</code></p><p>Takes an optional boardState argument; by default uses the current board state.</p><p>The data from getChains() can be used with the data from getBoardState() to see which player (or empty) each chain is</p><p>For example, a 5x5 board might look like this. There is a large chain \#1 on the left side, smaller chains 2 and 3 on the right, and a large chain 0 taking up the center of the board. <pre lang="javascript"> \[ \[ 0,0,0,3,4\], \[ 1,0,0,3,3\], \[ 1,1,0,0,0\], \[null,1,0,2,2\], \[null,1,0,2,5\], \] </pre></p> |
| [getControlledEmptyNodes(boardState)](./bitburner.goanalysis.getcontrolledemptynodes.md) | <p>Returns 'X' for black, 'O' for white, or '?' for each empty point to indicate which player controls that empty point. If no single player fully encircles the empty space, it is shown as contested with '?'. "\#" are dead nodes that are not part of the subnet.</p><p>Takes an optional boardState argument; by default uses the current board state.</p><p>Filled points of any color are indicated with '.'</p><p>In this example, white encircles some space in the top-left, black encircles some in the top-right, and between their routers is contested space in the center: <pre lang="javascript"> \[ "OO..?", "OO.?.", "O.?.X", ".?.XX", "?..X\#", \] </pre></p> |
| [getLiberties(boardState)](./bitburner.goanalysis.getliberties.md) | <p>Returns a number for each point, representing how many open nodes its network/chain is connected to. Empty nodes and dead nodes are shown as -1 liberties.</p><p>Takes an optional boardState argument; by default uses the current board state.</p><p>For example, a 5x5 board might look like this. The chain in the top-left touches 5 total empty nodes, and the one in the center touches four. The group in the bottom-right only has one liberty; it is in danger of being captured! <pre lang="javascript"> \[ \[-1, 5,-1,-1, 2\], \[ 5, 5,-1,-1,-1\], \[-1,-1, 4,-1,-1\], \[ 3,-1,-1, 3, 1\], \[ 3,-1,-1, 3, 1\], \] </pre></p> |
| [getStats()](./bitburner.goanalysis.getstats.md) | <p>Displays the game history, captured nodes, and gained bonuses for each opponent you have played against.</p><p>The details are keyed by opponent name, in this structure:</p><p><pre lang="javascript"> { <OpponentName>: { wins: number, losses: number, winStreak: number, highestWinStreak: number, favor: number, bonusPercent: number, bonusDescription: string, } } </pre></p> |
| [getValidMoves(boardState, priorBoardState, playAsWhite)](./bitburner.goanalysis.getvalidmoves.md) | <p>Shows if each point on the board is a valid move for the player. By default, analyzes the current board state. Takes an optional boardState (and an optional prior-move boardState, if desired) to analyze a custom board.</p><p>The true/false validity of each move can be retrieved via the X and Y coordinates of the move. <code>const validMoves = ns.go.analysis.getValidMoves();</code></p><p><code>const moveIsValid = validMoves[x][y];</code></p><p>Note that the \[0\]\[0\] point is shown on the bottom-left on the visual board (as is traditional), and each string represents a vertical column on the board. In other words, the printed example above can be understood to be rotated 90 degrees clockwise compared to the board UI as shown in the IPvGO subnet tab.</p><p>Also note that, when given a custom board state, only one prior move can be analyzed. This means that the superko rules (no duplicate board states in the full game history) is not supported; you will have to implement your own analysis for that.</p><p>playAsWhite is optional, and gets the current valid moves for the white player. Intended to be used when playing as white when the opponent is set to "No AI"</p> |
| [highlightPoint(x, y, color, text)](./bitburner.goanalysis.highlightpoint.md) | Adds a colored circle indicator to the specified point. These indicators are removed once a move is played. |
| [resetStats(resetAll)](./bitburner.goanalysis.resetstats.md) | Reset all win/loss and winstreak records for the No AI opponent. |

10 changes: 10 additions & 0 deletions src/Go/Go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import { getNewBoardState } from "./boardState/boardState";
import { EventEmitter } from "../utils/EventEmitter";
import { newOpponentStats } from "./Constants";

export const getEmptyHighlightedPoints = (size: number) => {
return Array.from({ length: size }, () => Array.from({ length: size }, () => null));
};

export class GoObject {
// Todo: Make previous game a slimmer interface
previousGame: BoardState | null = null;
currentGame: BoardState = getNewBoardState(7);
stats: PartialRecord<GoOpponent, OpponentStats> = {};
storedCycles: number = 0;
highlightedPoints: (PointHighlight | null)[][] = getEmptyHighlightedPoints(7);

prestigeAugmentation() {
for (const opponent of getRecordKeys(Go.stats)) {
Expand Down Expand Up @@ -41,3 +46,8 @@ export const Go = new GoObject();

/** Event emitter to allow the UI to subscribe to Go gameplay updates in order to trigger rerenders properly */
export const GoEvents = new EventEmitter();

export type PointHighlight = {
color: string;
text: string;
};
4 changes: 4 additions & 0 deletions src/Go/boardState/boardState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "../boardAnalysis/boardAnalysis";
import { endGoGame } from "../boardAnalysis/scoring";
import { addObstacles, resetCoordinates, rotate90Degrees } from "./offlineNodes";
import { clearAllPointHighlights } from "../effects/netscriptGoImplementation";

/** Generates a new BoardState object with the given opponent and size. Optionally use an existing board. */
export function getNewBoardState(
Expand Down Expand Up @@ -122,6 +123,7 @@ export function makeMove(boardState: BoardState, x: number, y: number, player: G

// Add move to board history
boardState.previousBoards.unshift(boardStringFromBoard(boardState.board));
clearAllPointHighlights();

point.color = player;
boardState.previousPlayer = player;
Expand All @@ -139,6 +141,8 @@ export function passTurn(boardState: BoardState, player: GoColor, allowEndGame =
if (boardState.previousPlayer === null || boardState.previousPlayer === player) {
return;
}
clearAllPointHighlights();

boardState.previousPlayer = boardState.previousPlayer === GoColor.black ? GoColor.white : GoColor.black;
boardState.passCount++;

Expand Down
65 changes: 52 additions & 13 deletions src/Go/boardState/goStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ type Point =
| "filledPoint"
| "emptyPoint"
| "broken"
| "cyberStyle"
| "tradStone"
| "priorStoneTrad";
type Structure = "coordinates" | "liberty" | "northLiberty" | "eastLiberty" | "westLiberty" | "southLiberty";
type Highlight = "hover" | "valid" | "priorPoint";
type Structure =
| "coordinates"
| "highlightText"
| "liberty"
| "northLiberty"
| "eastLiberty"
| "westLiberty"
| "southLiberty";
type Highlight = "hover" | "valid" | "priorPoint" | "hack" | "hp" | "money" | "int" | "cha";

const fadeLoop = keyframes`
0% {
Expand All @@ -29,31 +37,56 @@ export const pointStyle = makeStyles<unknown, Size | Point | Structure | Highlig
hover: {},
valid: {},
priorPoint: {},
cyberStyle: {},
highlightText: {},
hack: {},
hp: {},
money: {},
int: {},
cha: {},
point: {
position: "relative",
height: "100%",
width: "100%",

[`&.${classes.hover}.${classes.valid}:hover .${classes.innerPoint}`]: {
[`&.${classes.hover}.${classes.valid}.${classes.cyberStyle}:hover .${classes.innerPoint}`]: {
outlineColor: theme.colors.white,
},
[`&.${classes.hover}.${classes.priorPoint} .${classes.innerPoint}`]: {
[`&.${classes.hover}.${classes.cyberStyle}.${classes.priorPoint} .${classes.innerPoint}`]: {
outlineColor: theme.colors.white,
},
[`&.${classes.hover}.${classes.priorPoint} .${classes.priorStoneTrad}.${classes.blackPoint}`]: {
outlineColor: theme.colors.white,
display: "block",
},
[`&.${classes.hover}.${classes.priorPoint} .${classes.priorStoneTrad}.${classes.whitePoint}`]: {
outlineColor: theme.colors.black,
display: "block",
},
[`&.${classes.hover}.${classes.cyberStyle}.${classes.priorPoint} .${classes.priorStoneTrad}.${classes.blackPoint}`]:
{
outlineColor: theme.colors.white,
display: "block",
},
[`&.${classes.hover}.${classes.cyberStyle}.${classes.priorPoint} .${classes.priorStoneTrad}.${classes.whitePoint}`]:
{
outlineColor: theme.colors.black,
display: "block",
},
[`&.${classes.hover}:hover .${classes.coordinates}`]: {
display: "block",
},
[`&:hover .${classes.broken}`]: {
opacity: "0.4",
},

[`&.${classes.hack} .${classes.innerPoint}`]: {
outlineColor: theme.colors.hack,
},
[`&.${classes.hp} .${classes.innerPoint}`]: {
outlineColor: theme.colors.hp,
},
[`&.${classes.money} .${classes.innerPoint}`]: {
outlineColor: theme.colors.money,
},
[`&.${classes.int} .${classes.innerPoint}`]: {
outlineColor: theme.colors.int,
},
[`&.${classes.cha} .${classes.innerPoint}`]: {
outlineColor: theme.colors.cha,
},
},
broken: {
backgroundImage: `repeating-radial-gradient(circle at 17% 32%, ${theme.colors.white}, black 0.00085px)`,
Expand Down Expand Up @@ -82,7 +115,10 @@ export const pointStyle = makeStyles<unknown, Size | Point | Structure | Highlig
},
traditional: {
[`& .${classes.innerPoint}`]: {
display: "none",
outlineColor: "transparent",
[`& .${classes.emptyPoint}`]: {
display: "none",
},
},
[`& .${classes.broken}`]: {
backgroundImage: "none",
Expand Down Expand Up @@ -379,6 +415,9 @@ export const pointStyle = makeStyles<unknown, Size | Point | Structure | Highlig
left: "8%",
zIndex: "10",
userSelect: "none",
[`&.${classes.highlightText}`]: {
display: "block",
},
},
priorStoneTrad: {
display: "none",
Expand Down
18 changes: 17 additions & 1 deletion src/Go/effects/netscriptGoImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Board, BoardState, OpponentStats, Play, SimpleBoard, SimpleOpponentStat

import { Player } from "@player";
import { AugmentationName, GoColor, GoOpponent, GoPlayType, GoValidity } from "@enums";
import { Go } from "../Go";
import { getEmptyHighlightedPoints, Go, GoEvents } from "../Go";
import {
getNewBoardState,
getNewBoardStateFromSimpleBoard,
Expand Down Expand Up @@ -344,6 +344,7 @@ export function resetBoardState(

resetAI();
Go.currentGame = getNewBoardState(boardSize, opponent, true);
clearAllPointHighlights();
handleNextTurn(Go.currentGame).catch((error) => exceptionAlert(error));
logger(`New game started: ${opponent}, ${boardSize}x${boardSize}`);
return simpleBoardFromBoard(Go.currentGame.board);
Expand Down Expand Up @@ -426,6 +427,21 @@ export function validateBoardState(
}
}

export function addPointHighlight(x: number, y: number, color: string, text: string) {
Go.highlightedPoints[x][y] = { color, text };
GoEvents.emit();
}

export function clearPointHighlight(x: number, y: number) {
Go.highlightedPoints[x][y] = { color: "", text: "" };
GoEvents.emit();
}

export function clearAllPointHighlights() {
Go.highlightedPoints = getEmptyHighlightedPoints(Go.currentGame.board.length);
GoEvents.emit();
}

/**
* Check that the given boardState is a valid SimpleBoard, and return it if it is.
*/
Expand Down
11 changes: 10 additions & 1 deletion src/Go/ui/GoGameboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import { GoOpponent, GoColor } from "@enums";
import { getSizeClass, GoPoint } from "./GoPoint";
import { boardStyles } from "../boardState/goStyles";
import { getAllValidMoves, getControlledSpace } from "../boardAnalysis/boardAnalysis";
import { PointHighlight } from "../Go";

interface GoGameboardProps {
boardState: BoardState;
traditional: boolean;
clickHandler: (x: number, y: number) => unknown;
hover: boolean;
highlightedPoints?: (PointHighlight | null)[][];
}

export function GoGameboard({ boardState, traditional, clickHandler, hover }: GoGameboardProps): React.ReactElement {
export function GoGameboard({
boardState,
traditional,
clickHandler,
hover,
highlightedPoints,
}: GoGameboardProps): React.ReactElement {
const currentPlayer =
boardState.ai !== GoOpponent.none || boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white;

Expand Down Expand Up @@ -52,6 +60,7 @@ export function GoGameboard({ boardState, traditional, clickHandler, hover }: Go
hover={hover}
valid={pointIsValid(xIndex, yIndex)}
emptyPointOwner={ownedEmptyNodes[xIndex]?.[yIndex]}
pointHighlight={highlightedPoints?.[xIndex]?.[yIndex]}
/>
</Grid>
);
Expand Down
3 changes: 3 additions & 0 deletions src/Go/ui/GoGameboardWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { CorruptableText } from "../../ui/React/CorruptableText";
import { handleNextTurn, resetAI } from "../boardAnalysis/goAI";
import { GoScoreExplanation } from "./GoScoreExplanation";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { clearAllPointHighlights } from "../effects/netscriptGoImplementation";

interface GoGameboardWrapperProps {
showInstructions: () => void;
Expand Down Expand Up @@ -135,6 +136,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps

resetAI();
Go.currentGame = getNewBoardState(newBoardSize, newOpponent, true);
clearAllPointHighlights();
handleNextTurn(Go.currentGame).catch((error) => exceptionAlert(error));
}

Expand Down Expand Up @@ -223,6 +225,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
traditional={traditional}
clickHandler={clickHandler}
hover={!showPriorMove}
highlightedPoints={Go.highlightedPoints}
/>
</div>
<Box className={classes.inlineFlexBox}>
Expand Down
Loading