Commit 2d1ca98c authored by biluo.shen's avatar biluo.shen

Add AI predict

parent d27fb2f1
Pipeline #28077 failed with stages
in 32 seconds
...@@ -3,9 +3,13 @@ ...@@ -3,9 +3,13 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7",
"@ant-design/pro-components": "^2.6.12", "@ant-design/pro-components": "^2.6.12",
"@ant-design/pro-provider": "^2.14.7",
"@dnd-kit/core": "^6.0.8", "@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2", "@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.2",
"@react-spring/shared": "^9.7.3",
"@react-spring/web": "^9.7.3", "@react-spring/web": "^9.7.3",
"antd": "^5.8.3", "antd": "^5.8.3",
"classnames": "^2.3.2", "classnames": "^2.3.2",
...@@ -16,6 +20,7 @@ ...@@ -16,6 +20,7 @@
"i18next": "^23.11.4", "i18next": "^23.11.4",
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"overlayscrollbars": "^2.9.1",
"overlayscrollbars-react": "^0.5.1", "overlayscrollbars-react": "^0.5.1",
"rdndmb-html5-to-touch": "^8.0.3", "rdndmb-html5-to-touch": "^8.0.3",
"react": "^18.2.0", "react": "^18.2.0",
......
...@@ -5,7 +5,7 @@ import { Input, MsgResponse } from "./schema"; ...@@ -5,7 +5,7 @@ import { Input, MsgResponse } from "./schema";
import { agentHeader } from "./util"; import { agentHeader } from "./util";
const { agentServer } = useConfig(); const { agentServer } = useConfig();
const apiPath = (duelId: string) => `${agentServer}/v0/duels/${duelId}/predict`; const apiPath = (duelId: string) => `v0/duels/${duelId}/predict`;
export interface PredictReq { export interface PredictReq {
/** /**
...@@ -13,6 +13,7 @@ export interface PredictReq { ...@@ -13,6 +13,7 @@ export interface PredictReq {
*/ */
index: number; index: number;
input: Input; input: Input;
prev_action_idx: number;
} }
interface PredictResp { interface PredictResp {
...@@ -27,7 +28,10 @@ export async function predictDuel( ...@@ -27,7 +28,10 @@ export async function predictDuel(
duelId: string, duelId: string,
req: PredictReq, req: PredictReq,
): Promise<PredictResp | undefined> { ): Promise<PredictResp | undefined> {
const headers = agentHeader(); const headers = {
...agentHeader(),
'Content-Type': 'application/json',
};
const resp = await fetch(`${agentServer}/${apiPath(duelId)}`, { const resp = await fetch(`${agentServer}/${apiPath(duelId)}`, {
method: "POST", method: "POST",
......
This diff is collapsed.
import { PredictReq, ygopro } from "@/api"; import { PredictReq, ygopro } from "@/api";
import { cardStore, matStore, placeStore } from "@/stores"; import { cardStore, matStore } from "@/stores";
import { Global, convertPhase, convertCard, parsePlayerFromMsg, convertActionMsg, Input } from "@/api/ygoAgent/schema";
export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq { export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
// 全局信息可以从 `matStore` 里面拿 // 全局信息可以从 `matStore` 里面拿
const mat = matStore; const mat = matStore;
// 卡片信息可以从 `cardStore` 里面拿 // 卡片信息可以从 `cardStore` 里面拿
const cards = cardStore.inner; const zones = [
// 选择场上位置的可选项可以从 `cardStore` 里面拿 ygopro.CardZone.DECK,
// 也可以从 `selectPlace` msg 里面获取 ygopro.CardZone.HAND,
const place = placeStore; ygopro.CardZone.MZONE,
ygopro.CardZone.SZONE,
ygopro.CardZone.GRAVE,
ygopro.CardZone.REMOVED,
ygopro.CardZone.EXTRA,
];
// select_xxx msg 从参数 `msg` 里获取 // select_xxx msg 从参数 `msg` 里获取
// 这里已经保证 `msg` 是众多 `select_xxx` msg 中的一个 // 这里已经保证 `msg` 是众多 `select_xxx` msg 中的一个
const duelId = mat.duelId; const player = parsePlayerFromMsg(msg);
const index = mat.agentIndex; // TODO (ygo-agent): check if this is correct
const opponent = 1 - player;
const cards = cardStore.inner
.filter((card) => zones.includes(card.location.zone))
.map((card) => convertCard(card, player));
const turnPlayer = mat.currentPlayer;
const global: Global = {
is_first: player === 0,
is_my_turn: turnPlayer === player,
my_lp: mat.initInfo.of(player).life,
op_lp: mat.initInfo.of(opponent).life,
phase: convertPhase(mat.phase.currentPhase),
// TODO (ygo-agent): use real turn
turn: 1,
}
const actionMsg = convertActionMsg(msg);
const input: Input = {
global: global,
cards: cards,
action_msg: actionMsg,
}
// TODO: impl return {
index: mat.agentIndex,
input: input,
// TODO (ygo-agent): use real value
prev_action_idx: 0,
}
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { replayStore } from "@/stores"; import { matStore, replayStore } from "@/stores";
import { showWaiting } from "@/ui/Duel/Message"; import { showWaiting } from "@/ui/Duel/Message";
import onAnnounce from "./announce"; import onAnnounce from "./announce";
...@@ -100,6 +100,63 @@ export default async function handleGameMsg( ...@@ -100,6 +100,63 @@ export default async function handleGameMsg(
if (replayStore.isReplay && ReplayIgnoreMsg.includes(msg.gameMsg)) return; if (replayStore.isReplay && ReplayIgnoreMsg.includes(msg.gameMsg)) return;
switch (msg.gameMsg) {
case "select_card": {
matStore.actionMsg = msg.select_card;
break;
}
case "select_tribute": {
matStore.actionMsg = msg.select_tribute;
break;
}
case "select_sum": {
matStore.actionMsg = msg.select_sum;
break;
}
case "select_idle_cmd": {
matStore.actionMsg = msg.select_idle_cmd;
break;
}
case "select_chain": {
matStore.actionMsg = msg.select_chain;
break;
}
case "select_position": {
matStore.actionMsg = msg.select_position;
break;
}
case "select_effect_yn": {
matStore.actionMsg = msg.select_effect_yn;
break;
}
case "select_yes_no": {
matStore.actionMsg = msg.select_yes_no;
break;
}
case "select_battle_cmd": {
matStore.actionMsg = msg.select_battle_cmd;
break;
}
case "select_unselect_card": {
matStore.actionMsg = msg.select_unselect_card;
break;
}
case "select_option": {
matStore.actionMsg = msg.select_option;
break;
}
case "select_place": {
matStore.actionMsg = msg.select_place;
break;
}
case "announce":
matStore.actionMsg = msg.announce;
break;
default: {
break;
}
}
switch (msg.gameMsg) { switch (msg.gameMsg) {
case "start": { case "start": {
await onMsgStart(msg.start); await onMsgStart(msg.start);
......
...@@ -94,7 +94,8 @@ const initialState: Omit<MatState, "reset"> = { ...@@ -94,7 +94,8 @@ const initialState: Omit<MatState, "reset"> = {
// methods // methods
isMe, isMe,
duelId: "", duelId: "",
agentIndex: 0 agentIndex: 0,
actionMsg: undefined
}; };
class MatStore implements MatState, NeosStore { class MatStore implements MatState, NeosStore {
...@@ -113,6 +114,7 @@ class MatStore implements MatState, NeosStore { ...@@ -113,6 +114,7 @@ class MatStore implements MatState, NeosStore {
duelEnd = initialState.duelEnd; duelEnd = initialState.duelEnd;
duelId = initialState.duelId; duelId = initialState.duelId;
agentIndex = initialState.agentIndex; agentIndex = initialState.agentIndex;
actionMsg = initialState.actionMsg;
// methods // methods
isMe = initialState.isMe; isMe = initialState.isMe;
......
...@@ -52,6 +52,7 @@ export interface MatState { ...@@ -52,6 +52,7 @@ export interface MatState {
duelId: string; duelId: string;
agentIndex: number; agentIndex: number;
actionMsg?: any;
} }
export interface InitInfo { export interface InitInfo {
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
CloseCircleFilled, CloseCircleFilled,
MessageFilled, MessageFilled,
StepForwardFilled, StepForwardFilled,
RobotFilled,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
Button, Button,
...@@ -28,6 +29,8 @@ import { ...@@ -28,6 +29,8 @@ import {
} from "@/api"; } from "@/api";
import { ChainSetting, matStore } from "@/stores"; import { ChainSetting, matStore } from "@/stores";
import { IconFont } from "@/ui/Shared"; import { IconFont } from "@/ui/Shared";
import { predictDuel } from "@/api/ygoAgent/predict";
import { genPredictReq } from "@/service/duel/agent";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType; import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType;
...@@ -297,6 +300,15 @@ export const Menu = () => { ...@@ -297,6 +300,15 @@ export const Menu = () => {
const globalDisable = !matStore.isMe(currentPlayer); const globalDisable = !matStore.isMe(currentPlayer);
const aiPredict = async () => {
console.log("AI Predict");
const req = genPredictReq(matStore.actionMsg);
console.log(req);
const duelId = matStore.duelId;
const predictRes = await predictDuel(duelId, req);
console.log(predictRes);
};
return ( return (
<div className={styles["menu-container"]}> <div className={styles["menu-container"]}>
<SelectManager /> <SelectManager />
...@@ -323,6 +335,13 @@ export const Menu = () => { ...@@ -323,6 +335,13 @@ export const Menu = () => {
type="text" type="text"
></Button> ></Button>
</DropdownWithTitle> </DropdownWithTitle>
<Tooltip title="AI">
<Button
icon={<RobotFilled />}
onClick={aiPredict}
type="text"
></Button>
</Tooltip>
<Tooltip title={i18n("ChatRoom")}> <Tooltip title={i18n("ChatRoom")}>
<Button <Button
icon={<MessageFilled />} icon={<MessageFilled />}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment