Skip to content

Commit

Permalink
IPVGO: Add support for highlighting nodes and adding small text
Browse files Browse the repository at this point in the history
  • Loading branch information
ficocelliguy committed Mar 5, 2025
1 parent 6530b43 commit bee59eb
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 25 deletions.
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
49 changes: 39 additions & 10 deletions src/Go/ui/GoPoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { columnIndexes } from "../Constants";
import { findNeighbors } from "../boardState/boardState";
import { boardStyles, pointStyle } from "../boardState/goStyles";
import { findAdjacentLibertiesAndAlliesForPoint, getColorOnBoardString } from "../boardAnalysis/boardAnalysis";
import { PointHighlight } from "../Go";

interface GoPointProps {
state: BoardState;
Expand All @@ -15,11 +16,29 @@ interface GoPointProps {
traditional: boolean;
hover: boolean;
valid: boolean;
emptyPointOwner: GoColor;
emptyPointOwner?: GoColor;
pointHighlight?: PointHighlight | null;
}

export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwner }: GoPointProps): React.ReactElement {
export function GoPoint({
state,
x,
y,
traditional,
hover,
valid,
emptyPointOwner,
pointHighlight,
}: GoPointProps): React.ReactElement {
const { classes } = pointStyle({});
const colorClasses = {
hack: classes.hack,
hp: classes.hp,
money: classes.money,
int: classes.int,
cha: classes.cha,
none: "none",
};

const currentPoint = state.board[x]?.[y];
const player = currentPoint?.color;
Expand Down Expand Up @@ -51,9 +70,15 @@ export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwne
? classes.libertyBlack
: "";

const mainClassName = `${classes.point} ${sizeClass} ${traditional ? classes.traditional : ""} ${
const highlightClass: string = pointHighlight?.color
? colorClasses[pointHighlight.color as keyof typeof colorClasses] ?? ""
: "";
const rawColorStyle = !highlightClass && pointHighlight?.color ? `${pointHighlight.color}` : "";
const highlightText = pointHighlight?.text ?? "";

const mainClassName = `${classes.point} ${sizeClass} ${traditional ? classes.traditional : classes.cyberStyle} ${
hover ? classes.hover : ""
} ${valid ? classes.valid : ""} ${isPriorMove ? classes.priorPoint : ""}
} ${valid ? classes.valid : ""} ${isPriorMove ? classes.priorPoint : ""} ${highlightClass}
${isInAtari ? classes.fadeLoopAnimation : ""}`;

return (
Expand All @@ -64,18 +89,22 @@ export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwne
<div className={hasEastLiberty ? `${classes.eastLiberty} ${colorLiberty}` : classes.liberty}></div>
<div className={hasSouthLiberty ? `${classes.southLiberty} ${colorLiberty}` : classes.liberty}></div>
<div className={hasWestLiberty ? `${classes.westLiberty} ${colorLiberty}` : classes.liberty}></div>
<div className={`${classes.innerPoint} `}>
<div className={`${classes.innerPoint} `} style={{ outlineColor: rawColorStyle }}>
<div
className={`${pointClass} ${player !== GoColor.empty ? classes.filledPoint : emptyPointColorClass}`}
></div>
</div>
<div className={`${pointClass} ${classes.tradStone}`} />
{traditional ? <div className={`${pointClass} ${classes.priorStoneTrad}`}></div> : ""}
<div className={classes.coordinates}>
{columnIndexes[x]}
{traditional ? "" : "."}
{y + 1}
</div>
{highlightText ? (
<div className={`${classes.highlightText} ${classes.coordinates}`}>{highlightText}</div>
) : (
<div className={classes.coordinates}>
{columnIndexes[x]}
{traditional ? "" : "."}
{y + 1}
</div>
)}
</>
) : (
<>
Expand Down
3 changes: 3 additions & 0 deletions src/Netscript/RamCostGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ const go = {
getControlledEmptyNodes: 16,
getStats: 0,
resetStats: 0,
highlightPoint: 0,
clearPointHighlight: 0,
clearAllPointHighlights: 0,
},
cheat: {
getCheatSuccessChance: 1,
Expand Down
16 changes: 16 additions & 0 deletions src/NetscriptFunctions/Go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import { Go } from "../Go/Go";
import { helpers } from "../Netscript/NetscriptHelpers";
import { simpleBoardFromBoard } from "../Go/boardAnalysis/boardAnalysis";
import {
addPointHighlight,
cheatDestroyNode,
cheatPlayTwoMoves,
cheatRemoveRouter,
cheatRepairOfflineNode,
cheatSuccessChance,
checkCheatApiAccess,
clearAllPointHighlights,
clearPointHighlight,
getChains,
getControlledEmptyNodes,
getCurrentPlayer,
Expand Down Expand Up @@ -104,6 +107,19 @@ export function NetscriptGo(): InternalAPI<NSGo> {
(resetAll = false) => {
resetStats(!!resetAll);
},
highlightPoint: (ctx) => (_x, _y, _color, _text) => {
const x = helpers.number(ctx, "x", _x);
const y = helpers.number(ctx, "y", _y);
const color = helpers.string(ctx, "color", _color ?? "");
const text = helpers.string(ctx, "text", _text ?? "");
addPointHighlight(x, y, color, text);
},
clearPointHighlight: (ctx) => (_x, _y) => {
const x = helpers.number(ctx, "x", _x);
const y = helpers.number(ctx, "y", _y);
clearPointHighlight(x, y);
},
clearAllPointHighlights: () => () => clearAllPointHighlights(),
},
cheat: {
getCheatSuccessChance: (ctx: NetscriptContext) => (_cheatCount, playAsWhite) => {
Expand Down
Loading

0 comments on commit bee59eb

Please sign in to comment.