mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 12:45:45 +01:00
IPVGO: Support playing manually as white against your scripts using the No AI type board (#1296)
This commit is contained in:
parent
f40d4f8e92
commit
a28bb4bd99
@ -30,5 +30,5 @@ export interface Go
|
|||||||
| [makeMove(x, y)](./bitburner.go.makemove.md) | Make a move on the IPvGO subnet gameboard, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI. |
|
| [makeMove(x, y)](./bitburner.go.makemove.md) | Make a move on the IPvGO subnet gameboard, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI. |
|
||||||
| [opponentNextTurn(logOpponentMove)](./bitburner.go.opponentnextturn.md) | Returns a promise that resolves with the success or failure state of your last move, and the AI's response, if applicable. x:0 y:0 represents the bottom-left corner of the board in the UI. |
|
| [opponentNextTurn(logOpponentMove)](./bitburner.go.opponentnextturn.md) | Returns a promise that resolves with the success or failure state of your last move, and the AI's response, if applicable. x:0 y:0 represents the bottom-left corner of the board in the UI. |
|
||||||
| [passTurn()](./bitburner.go.passturn.md) | <p>Pass the player's turn rather than making a move, and await the opponent's response. This ends the game if the opponent passed on the previous turn, or if the opponent passes on their following turn.</p><p>This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.</p> |
|
| [passTurn()](./bitburner.go.passturn.md) | <p>Pass the player's turn rather than making a move, and await the opponent's response. This ends the game if the opponent passed on the previous turn, or if the opponent passes on their following turn.</p><p>This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.</p> |
|
||||||
| [resetBoardState(opponent, boardSize)](./bitburner.go.resetboardstate.md) | <p>Gets new IPvGO subnet with the specified size owned by the listed faction, ready for the player to make a move. This will reset your win streak if the current game is not complete and you have already made moves.</p><p>Note that some factions will have a few routers on the subnet at this state.</p><p>opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",</p> |
|
| [resetBoardState(opponent, boardSize)](./bitburner.go.resetboardstate.md) | <p>Gets new IPvGO subnet with the specified size owned by the listed faction, ready for the player to make a move. This will reset your win streak if the current game is not complete and you have already made moves.</p><p>Note that some factions will have a few routers on the subnet at this state.</p><p>opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",</p> |
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ Gets new IPvGO subnet with the specified size owned by the listed faction, ready
|
|||||||
|
|
||||||
Note that some factions will have a few routers on the subnet at this state.
|
Note that some factions will have a few routers on the subnet at this state.
|
||||||
|
|
||||||
opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",
|
opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",
|
||||||
|
|
||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
|
@ -6,7 +6,8 @@ export const opponentDetails = {
|
|||||||
[GoOpponent.none]: {
|
[GoOpponent.none]: {
|
||||||
komi: 5.5,
|
komi: 5.5,
|
||||||
description: "Practice Board",
|
description: "Practice Board",
|
||||||
flavorText: "Practice on a subnet where you place both colors of routers.",
|
flavorText:
|
||||||
|
"Practice on a subnet where you place both colors of routers, or play as white against your IPvGO script.",
|
||||||
bonusDescription: "",
|
bonusDescription: "",
|
||||||
bonusPower: 0,
|
bonusPower: 0,
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { Board, BoardState, Neighbor, PointState, SimpleBoard } from "../Types";
|
import type { Board, BoardState, Neighbor, Play, PointState, SimpleBoard } from "../Types";
|
||||||
|
|
||||||
import { GoValidity, GoOpponent, GoColor } from "@enums";
|
import { GoValidity, GoOpponent, GoColor, GoPlayType } from "@enums";
|
||||||
import { Go } from "../Go";
|
import { Go } from "../Go";
|
||||||
import {
|
import {
|
||||||
findAdjacentPointsInChain,
|
findAdjacentPointsInChain,
|
||||||
@ -655,3 +655,23 @@ export function getPreviousMove(): [number, number] | null {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last move, if it was made by the specified color and is present
|
||||||
|
*/
|
||||||
|
export function getPreviousMoveDetails(): Play {
|
||||||
|
const priorMove = getPreviousMove();
|
||||||
|
if (priorMove) {
|
||||||
|
return {
|
||||||
|
type: GoPlayType.move,
|
||||||
|
x: priorMove[0],
|
||||||
|
y: priorMove[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: !priorMove && Go.currentGame?.passCount ? GoPlayType.pass : GoPlayType.gameOver,
|
||||||
|
x: null,
|
||||||
|
y: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { Board, BoardState, EyeMove, Move, MoveOptions, Play, PointState } from "../Types";
|
import type { Board, BoardState, EyeMove, Move, MoveOptions, Play, PointState } from "../Types";
|
||||||
|
|
||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
import { AugmentationName, GoOpponent, GoColor, GoPlayType } from "@enums";
|
import { AugmentationName, GoColor, GoOpponent, GoPlayType } from "@enums";
|
||||||
import { opponentDetails } from "../Constants";
|
import { opponentDetails } from "../Constants";
|
||||||
import { findNeighbors, isNotNullish, makeMove, passTurn } from "../boardState/boardState";
|
import { findNeighbors, isNotNullish, makeMove, passTurn } from "../boardState/boardState";
|
||||||
import {
|
import {
|
||||||
@ -15,22 +15,35 @@ import {
|
|||||||
getAllEyesByChainId,
|
getAllEyesByChainId,
|
||||||
getAllNeighboringChains,
|
getAllNeighboringChains,
|
||||||
getAllValidMoves,
|
getAllValidMoves,
|
||||||
|
getPreviousMoveDetails,
|
||||||
} from "./boardAnalysis";
|
} from "./boardAnalysis";
|
||||||
import { findDisputedTerritory } from "./controlledTerritory";
|
import { findDisputedTerritory } from "./controlledTerritory";
|
||||||
import { findAnyMatchedPatterns } from "./patternMatching";
|
import { findAnyMatchedPatterns } from "./patternMatching";
|
||||||
import { WHRNG } from "../../Casino/RNG";
|
import { WHRNG } from "../../Casino/RNG";
|
||||||
import { Go, GoEvents } from "../Go";
|
import { Go, GoEvents } from "../Go";
|
||||||
|
|
||||||
let currentAITurn: Promise<Play> | null = null;
|
let isAiThinking: boolean = false;
|
||||||
|
let currentTurnResolver: (() => void) | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a move from the current faction in response to the player's move
|
* 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): Promise<Play> {
|
||||||
// If AI is already taking their turn, return the existing turn.
|
// If AI is already taking their turn, return the existing turn.
|
||||||
if (currentAITurn) return currentAITurn;
|
if (isAiThinking) {
|
||||||
currentAITurn = Go.nextTurn = getMove(boardState, GoColor.white, Go.currentGame.ai)
|
return Go.nextTurn;
|
||||||
.then(async (play): Promise<Play> => {
|
}
|
||||||
|
isAiThinking = true;
|
||||||
|
|
||||||
|
// If the AI is disabled, simply make a promise to be resolved once the player makes a move as white
|
||||||
|
if (boardState.ai === GoOpponent.none) {
|
||||||
|
GoEvents.emit();
|
||||||
|
// Update currentTurnResolver to call Go.nextTurn's resolve function with the last played move's details
|
||||||
|
Go.nextTurn = new Promise((resolve) => (currentTurnResolver = () => resolve(getPreviousMoveDetails())));
|
||||||
|
}
|
||||||
|
// 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
|
if (boardState !== Go.currentGame) return play; //Stale game
|
||||||
|
|
||||||
// Handle AI passing
|
// Handle AI passing
|
||||||
@ -54,15 +67,29 @@ export function makeAIMove(boardState: BoardState): Promise<Play> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return play;
|
return play;
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
currentAITurn = null;
|
|
||||||
GoEvents.emit();
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the AI moves (or the player playing as white with No AI moves),
|
||||||
|
// clear the isAiThinking semaphore and update the board UI.
|
||||||
|
Go.nextTurn = Go.nextTurn.finally(() => {
|
||||||
|
isAiThinking = false;
|
||||||
|
GoEvents.emit();
|
||||||
|
});
|
||||||
|
|
||||||
return Go.nextTurn;
|
return Go.nextTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the current turn.
|
||||||
|
* This is used for players manually playing against their script on the no-ai board.
|
||||||
|
*/
|
||||||
|
export function resolveCurrentTurn() {
|
||||||
|
// Call the resolve function on Go.nextTurn, if it exists
|
||||||
|
currentTurnResolver?.();
|
||||||
|
currentTurnResolver = null;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Basic GO AIs, each with some personality and weaknesses
|
Basic GO AIs, each with some personality and weaknesses
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import { GoScoreModal } from "./GoScoreModal";
|
|||||||
import { GoGameboard } from "./GoGameboard";
|
import { GoGameboard } from "./GoGameboard";
|
||||||
import { GoSubnetSearch } from "./GoSubnetSearch";
|
import { GoSubnetSearch } from "./GoSubnetSearch";
|
||||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||||
import { makeAIMove } from "../boardAnalysis/goAI";
|
import { makeAIMove, resolveCurrentTurn } from "../boardAnalysis/goAI";
|
||||||
|
|
||||||
interface GoGameboardWrapperProps {
|
interface GoGameboardWrapperProps {
|
||||||
showInstructions: () => void;
|
showInstructions: () => void;
|
||||||
@ -85,7 +85,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
|||||||
const didUpdateBoard = makeMove(boardState, x, y, currentPlayer);
|
const didUpdateBoard = makeMove(boardState, x, y, currentPlayer);
|
||||||
if (didUpdateBoard) {
|
if (didUpdateBoard) {
|
||||||
rerender();
|
rerender();
|
||||||
Go.currentGame.ai !== GoOpponent.none && takeAiTurn(boardState);
|
takeAiTurn(boardState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +104,17 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Go.currentGame.ai !== GoOpponent.none && takeAiTurn(boardState);
|
takeAiTurn(boardState);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function takeAiTurn(boardState: BoardState) {
|
async function takeAiTurn(boardState: BoardState) {
|
||||||
|
// If white is being played manually, halt and notify any scripts playing as black if present, instead of making an AI move
|
||||||
|
if (Go.currentGame.ai === GoOpponent.none) {
|
||||||
|
Go.currentGame.previousPlayer && resolveCurrentTurn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const move = await makeAIMove(boardState);
|
const move = await makeAIMove(boardState);
|
||||||
|
|
||||||
if (move.type === GoPlayType.pass) {
|
if (move.type === GoPlayType.pass) {
|
||||||
@ -137,6 +143,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
|||||||
|
|
||||||
Go.currentGame = getNewBoardState(newBoardSize, newOpponent, true);
|
Go.currentGame = getNewBoardState(newBoardSize, newOpponent, true);
|
||||||
rerender();
|
rerender();
|
||||||
|
resolveCurrentTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPriorMove() {
|
function getPriorMove() {
|
||||||
@ -159,17 +166,19 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
|||||||
rerender();
|
rerender();
|
||||||
}
|
}
|
||||||
|
|
||||||
const endGameAvailable = boardState.previousPlayer === GoColor.white && boardState.passCount;
|
const ongoingNoAiGame = boardState.ai === GoOpponent.none && boardState.previousPlayer;
|
||||||
const noLegalMoves =
|
const manualTurnAvailable = ongoingNoAiGame || boardState.previousPlayer === GoColor.white;
|
||||||
boardState.previousPlayer === GoColor.white && !getAllValidMoves(boardState, GoColor.black).length;
|
const endGameAvailable = manualTurnAvailable && boardState.passCount;
|
||||||
|
const noLegalMoves = manualTurnAvailable && !getAllValidMoves(boardState, currentPlayer).length;
|
||||||
|
|
||||||
const scoreBoxText = boardState.previousBoards.length
|
const scoreBoxText = boardState.previousBoards.length
|
||||||
? `Score: Black: ${score[GoColor.black].sum} White: ${score[GoColor.white].sum}`
|
? `Score: Black: ${score[GoColor.black].sum} White: ${score[GoColor.white].sum}`
|
||||||
: "Place a router to begin!";
|
: "Place a router to begin!";
|
||||||
|
|
||||||
const getPassButtonLabel = () => {
|
const getPassButtonLabel = () => {
|
||||||
|
const playerString = boardState.ai === GoOpponent.none ? ` (${currentPlayer})` : "";
|
||||||
if (endGameAvailable) {
|
if (endGameAvailable) {
|
||||||
return "End Game";
|
return `End Game${playerString}`;
|
||||||
}
|
}
|
||||||
if (boardState.previousPlayer === null) {
|
if (boardState.previousPlayer === null) {
|
||||||
return "View Final Score";
|
return "View Final Score";
|
||||||
@ -177,8 +186,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
|||||||
if (waitingOnAI) {
|
if (waitingOnAI) {
|
||||||
return "Waiting for opponent";
|
return "Waiting for opponent";
|
||||||
}
|
}
|
||||||
const currentPlayer = boardState.previousPlayer === GoColor.black ? GoColor.white : GoColor.black;
|
return `Pass Turn${playerString}`;
|
||||||
return `Pass Turn${boardState.ai === GoOpponent.none ? ` (${currentPlayer})` : ""}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -4079,7 +4079,7 @@ export interface Go {
|
|||||||
*
|
*
|
||||||
* Note that some factions will have a few routers on the subnet at this state.
|
* Note that some factions will have a few routers on the subnet at this state.
|
||||||
*
|
*
|
||||||
* opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",
|
* opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",
|
||||||
*
|
*
|
||||||
* @returns a simplified version of the board state as an array of strings representing the board columns. See ns.Go.getBoardState() for full details
|
* @returns a simplified version of the board state as an array of strings representing the board columns. See ns.Go.getBoardState() for full details
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user