Commit 3a8c96b5 authored by biluo.shen's avatar biluo.shen

Add response

parent 2d1ca98c
Pipeline #28097 failed with stages
in 21 seconds
import { ygopro } from "@/api";
import { ygopro, CardMeta } from "@/api";
import { CardType } from "@/stores/cardStore";
import { extraCardTypes } from "@/common";
......@@ -349,13 +349,31 @@ function getCounter(counters: { [type: number]: number }) {
return 0;
}
export function convertDeckCard(meta: CardMeta): Card {
return {
code: meta.id,
location: Location.Deck,
sequence: 0,
controller: Controller.Me,
position: Position.Facedown,
overlay_sequence: -1,
attribute: numberToAttribute(meta.data.attribute ?? 0),
race: numberToRace(meta.data.race ?? 0),
level: meta.data.level ?? 0,
counter: 0,
negated: false,
attack: meta.data.atk ?? 0,
defense: meta.data.def ?? 0,
types: extraCardTypes(meta.data.type ?? 0).map(numberToType),
}
}
export function convertCard(card: CardType, player: number): Card {
// TODO (ygo-agent): unseen cards
return {
code: card.code,
location: cardZoneToLocation(card.location.zone),
sequence: card.location.sequence + 1,
sequence: card.location.sequence,
controller: convertController(card.location.controller, player),
position: convertPosition(card.location.position),
overlay_sequence: convertOverlaySequence(card.location),
......@@ -446,20 +464,29 @@ export function parsePlayerFromMsg(msg: GM): number {
throw new Error(`Unsupported message type: ${msg}`);
}
interface SelectAbleCard {
location: CardLocation;
response: number;
}
interface MsgSelectCard {
cancelable: boolean;
min: number;
max: number;
cards: CardLocation[];
cards: SelectAbleCard[];
selected: number[];
}
function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard {
return {
// response is -1 for finish
return {
cancelable: msg.cancelable,
min: msg.min,
max: msg.max,
cards: msg.cards.map(c => convertCardLocation(c.location, msg.player)),
cards: msg.cards.map(c => ({
location: convertCardLocation(c.location, msg.player),
response: c.response
})),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [],
};
......@@ -468,6 +495,7 @@ function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard {
interface SelectTributeCard {
location: CardLocation;
level: number;
response: number;
}
interface MsgSelectTribute {
......@@ -486,6 +514,7 @@ function convertMsgSelectTribute(msg: GM.MsgSelectTribute): MsgSelectTribute {
cards: msg.selectable_cards.map(c => ({
location: convertCardLocation(c.location, msg.player),
level: c.level,
response: c.response,
})),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [],
......@@ -496,6 +525,7 @@ interface SelectSumCard {
location: CardLocation;
level1: number;
level2: number;
response: number;
}
interface MsgSelectSum {
......@@ -518,11 +548,13 @@ function convertMsgSelectSum(msg: GM.MsgSelectSum): MsgSelectSum {
location: convertCardLocation(c.location, msg.player),
level1: c.level1,
level2: c.level2,
response: c.response,
})),
must_cards: msg.must_select_cards.map(c => ({
location: convertCardLocation(c.location, msg.player),
level1: c.level1,
level2: c.level2,
response: c.response,
})),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [],
......@@ -584,6 +616,7 @@ function convertIdleCmdType(cmdType: _IdleType): IdleCmdType {
interface IdleCmdData {
card_info: CardInfo;
effect_description: number;
response: number;
}
interface IdleCmd {
......@@ -613,6 +646,7 @@ function convertMsgSelectIdleCmd(msg: GM.MsgSelectIdleCmd): MsgSelectIdleCmd {
data: {
card_info: convertCardInfo(data.card_info, msg.player),
effect_description: cmd_type == IdleCmdType.Activate ? data.effect_description : 0,
response: data.response,
},
});
} else {
......@@ -621,9 +655,11 @@ function convertMsgSelectIdleCmd(msg: GM.MsgSelectIdleCmd): MsgSelectIdleCmd {
}
}
if (msg.enable_bp) {
// response will be 6
idle_cmds.push({ cmd_type: IdleCmdType.ToBp });
}
if (msg.enable_ep) {
// response will be 7
idle_cmds.push({ cmd_type: IdleCmdType.ToEp });
}
return { idle_cmds };
......@@ -633,6 +669,7 @@ interface Chain {
code: number;
location: CardLocation;
effect_description: number;
response: number;
}
interface MsgSelectChain {
......@@ -645,10 +682,12 @@ function convertChain(chain: GM.MsgSelectChain.Chain, player: number): Chain {
code: chain.code,
location: convertCardLocation(chain.location, player),
effect_description: chain.effect_description,
response: chain.response,
};
}
function convertMsgSelectChain(msg: GM.MsgSelectChain): MsgSelectChain {
// response is -1 for cancel
return {
forced: msg.forced,
chains: msg.chains.map(c => convertChain(c, msg.player)),
......@@ -663,6 +702,9 @@ interface MsgSelectPosition {
function convertMsgSelectPosition(msg: GM.MsgSelectPosition): MsgSelectPosition {
return {
code: msg.code,
// response will be equal to POS_* from ocgcore
// POS_FACEUP_ATTACK: 0x1, POS_FACEDOWN_ATTACK: 0x2,
// POS_FACEUP_DEFENSE: 0x4, POS_FACEDOWN_DEFENSE: 0x8
positions: msg.positions.map(p => convertPosition(p.position)),
};
}
......@@ -672,6 +714,7 @@ interface MsgSelectYesNo {
}
function convertMsgSelectYesNo(msg: GM.MsgSelectYesNo): MsgSelectYesNo {
// response is 1 for yes and 0 for no
return {
effect_description: msg.effect_description,
};
......@@ -684,6 +727,7 @@ interface MsgSelectEffectYn {
}
function convertMsgSelectEffectYn(msg: GM.MsgSelectEffectYn): MsgSelectEffectYn {
// response is 1 for yes and 0 for no
return {
code: msg.code,
location: convertCardLocation(msg.location, msg.player),
......@@ -714,6 +758,7 @@ interface BattleCmdData {
card_info: CardInfo;
effect_description: number;
direct_attackable: boolean;
response: number;
}
interface BattleCmd {
......@@ -734,41 +779,59 @@ function convertMsgSelectBattleCmd(msg: GM.MsgSelectBattleCmd): MsgSelectBattleC
card_info: convertCardInfo(data.card_info, msg.player),
effect_description: data.effect_description,
direct_attackable: data.direct_attackable,
response: data.response,
};
battle_cmds.push({ cmd_type, data: battle_data });
}
}
if (msg.enable_m2) {
// response will be 2
battle_cmds.push({ cmd_type: BattleCmdType.ToM2 });
}
if (msg.enable_ep) {
// response will be 3
battle_cmds.push({ cmd_type: BattleCmdType.ToEp });
}
return { battle_cmds };
}
interface SelectUnselectCard {
location: CardLocation;
response: number;
}
interface MsgSelectUnselectCard {
finishable: boolean;
cancelable: boolean;
min: number;
max: number;
selected_cards: CardLocation[];
selectable_cards: CardLocation[];
selected_cards: SelectUnselectCard[];
selectable_cards: SelectUnselectCard[];
}
function convertMsgSelectUnselectCard(msg: GM.MsgSelectUnselectCard): MsgSelectUnselectCard {
return {
// response is -1 for finish
finishable: msg.finishable,
cancelable: msg.cancelable,
min: msg.min,
max: msg.max,
selected_cards: msg.selected_cards.map(c => convertCardLocation(c.location, msg.player)),
selectable_cards: msg.selectable_cards.map(c => convertCardLocation(c.location, msg.player)),
selected_cards: msg.selected_cards.map(c => ({
location: convertCardLocation(c.location, msg.player),
response: c.response,
})),
selectable_cards: msg.selectable_cards.map(c => ({
location: convertCardLocation(c.location, msg.player),
response: c.response,
})),
};
}
interface Option {
code: number;
response: number;
}
interface MsgSelectOption {
......@@ -777,7 +840,10 @@ interface MsgSelectOption {
function convertMsgSelectOption(msg: GM.MsgSelectOption): MsgSelectOption {
return {
options: msg.options.map(o => ({ code: o.code })),
options: msg.options.map(o => ({
code: o.code,
response: o.response,
})),
};
}
......@@ -797,6 +863,7 @@ function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace {
return {
count: msg.count,
places: msg.places.map(p => ({
// NOTICE: the response is all -1
controller: convertController(p.controller, msg.player),
location: cardZoneToLocation(p.zone),
sequence: p.sequence,
......@@ -806,28 +873,44 @@ function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace {
// TODO (ygo-agent): MsgSelectDisfield
interface AnnounceAttrib {
attribute: Attribute;
response: number;
}
interface MsgAnnounceAttrib {
count: number;
attributes: Attribute[];
attributes: AnnounceAttrib[];
}
function convertMsgAnnounceAttrib(msg: GM.MsgAnnounce): MsgAnnounceAttrib {
return {
count: msg.min,
// from api/ocgcore/ocgAdapter/stoc/stocGameMsg/announceAttrib.ts
attributes: msg.options.map(a => numberToAttribute(1 << a.code)),
attributes: msg.options.map(a => ({
attribute: numberToAttribute(1 << a.code),
response: a.response,
})),
};
}
interface AnnounceNumber {
number: number;
response: number;
}
interface MsgAnnounceNumber {
count: number;
numbers: number[];
numbers: AnnounceNumber[];
}
function convertMsgAnnounceNumber(msg: GM.MsgAnnounce): MsgAnnounceNumber {
return {
count: msg.min,
numbers: msg.options.map(o => o.code),
numbers: msg.options.map(o => ({
number: o.code,
response: o.response,
})),
};
}
......@@ -847,7 +930,7 @@ type ActionMsgData =
MsgAnnounceAttrib |
MsgAnnounceNumber;
enum ActionMsgName {
export enum ActionMsgName {
AnnounceAttrib = "announce_attrib",
AnnounceNumber = "announce_number",
SelectBattlecmd = "select_battlecmd",
......@@ -956,10 +1039,12 @@ export interface Input {
global: Global;
}
/**
* MsgResponse
*/
interface ActionPredict {
prob: number;
response: number;
}
export interface MsgResponse {
action_probs: number[];
action_preds: ActionPredict[];
win_rate: number;
}
import { PredictReq, ygopro } from "@/api";
import {
PredictReq,
ygopro,
sendSelectIdleCmdResponse,
sendSelectPlaceResponse,
sendSelectMultiResponse,
sendSelectSingleResponse,
sendSelectEffectYnResponse,
sendSelectPositionResponse,
sendSelectOptionResponse,
sendSelectBattleCmdResponse,
sendSortCardResponse,
} from "@/api";
import { cardStore, matStore } from "@/stores";
import { Global, convertPhase, convertCard, parsePlayerFromMsg, convertActionMsg, Input } from "@/api/ygoAgent/schema";
import { ActionMsgName, Global, convertPhase, convertCard, convertDeckCard, parsePlayerFromMsg, convertActionMsg, Input } from "@/api/ygoAgent/schema";
import { predictDuel } from "@/api/ygoAgent/predict";
export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
......@@ -24,8 +37,24 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
const opponent = 1 - player;
const cards = cardStore.inner
.filter((card) => zones.includes(card.location.zone))
.map((card) => convertCard(card, player));
.filter((card) =>
zones.includes(card.location.zone) &&
!(card.location.zone === ygopro.CardZone.DECK
&& card.location.controller === player
)
)
.map((card) => convertCard(card, player));
const card_codes_me = new Set(
cardStore.inner
.filter((card) =>
zones.includes(card.location.zone) && card.location.controller === player
)
.map((card) => card.code));
const deck_cards_me = matStore.main_deck_cards
.filter((meta) => card_codes_me.has(meta.id))
.map(convertDeckCard);
const turnPlayer = mat.currentPlayer;
const global: Global = {
......@@ -42,7 +71,7 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
const input: Input = {
global: global,
cards: cards,
cards: deck_cards_me.concat(cards),
action_msg: actionMsg,
}
......@@ -53,3 +82,85 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
prev_action_idx: 0,
}
}
export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) {
const req = genPredictReq(msg);
console.log("Sending predict request:", req);
const duelId = matStore.duelId;
const resp = await predictDuel(duelId, req);
if (!resp) {
console.error("Failed to get predict response");
return;
}
const preds = resp.predict_results.action_preds;
const action_idx = argmax(preds, (r) => r.prob);
const response = preds[action_idx].response;
const msg_name = req.input.action_msg.name;
switch (msg_name) {
case ActionMsgName.AnnounceAttrib:
case ActionMsgName.AnnounceNumber:
sendSelectOptionResponse(response);
break;
case ActionMsgName.SelectBattlecmd:
sendSelectBattleCmdResponse(response);
break;
case ActionMsgName.SelectChain:
sendSelectSingleResponse(response);
break;
case ActionMsgName.SelectYesno:
case ActionMsgName.SelectEffectyn:
sendSelectEffectYnResponse(response === 1);
break;
case ActionMsgName.SelectIdlecmd:
sendSelectIdleCmdResponse(response);
break;
case ActionMsgName.SelectOption:
sendSelectOptionResponse(response);
break;
case ActionMsgName.SelectPosition:
sendSelectPositionResponse(convertPositionResponse(response));
break;
case ActionMsgName.SelectUnselectCard:
case ActionMsgName.SelectDisfield:
case ActionMsgName.SelectPlace:
case ActionMsgName.SelectCard:
case ActionMsgName.SelectSum:
case ActionMsgName.SelectTribute:
default:
throw new Error(`Unsupported msg_name: ${msg_name}`);
}
}
function argmax<T>(arr: T[], getValue: (item: T) => number): number {
if (arr.length === 0) {
throw new Error("Array is empty");
}
let maxIndex = 0;
let maxValue = getValue(arr[0]);
for (let i = 1; i < arr.length; i++) {
const currentValue = getValue(arr[i]);
if (currentValue > maxValue) {
maxValue = currentValue;
maxIndex = i;
}
}
return maxIndex;
}
function convertPositionResponse(response: number): ygopro.CardPosition {
switch (response) {
case 0x1: return ygopro.CardPosition.FACEUP_ATTACK;
case 0x2: return ygopro.CardPosition.FACEDOWN_ATTACK;
case 0x4: return ygopro.CardPosition.FACEUP_DEFENSE;
case 0x8: return ygopro.CardPosition.FACEDOWN_DEFENSE;
default:
throw new Error(`Invalid position response: ${response}`);
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import {
InteractType,
matStore,
} from "@/stores";
// import { sendAIPredictAsResponse } from "@/service/duel/agent";
import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd;
......@@ -17,6 +18,9 @@ export default (selectIdleCmd: MsgSelectIdleCmd) => {
card.idleInteractivities = [];
});
// sendAIPredictAsResponse(selectIdleCmd as unknown as ygopro.StocGameMessage);
// return;
cmds.forEach((cmd) => {
const interactType = idleTypeToInteractType(cmd.idle_type);
......
import { flatten } from "lodash-es";
import { v4 as v4uuid } from "uuid";
import { createDuel, ygopro } from "@/api";
import { createDuel, ygopro, fetchCard } from "@/api";
import { useConfig } from "@/config";
import { sleep } from "@/infra";
import {
......@@ -92,6 +92,10 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
const { duelId, index } = (await createDuel())!;
matStore.duelId = duelId;
matStore.agentIndex = index;
const main_deck_cards = matStore.main_deck.map((code) => {
return fetchCard(code);
});
matStore.main_deck_cards = main_deck_cards;
if (replayStore.isReplay) {
replayStart();
......
......@@ -95,7 +95,9 @@ const initialState: Omit<MatState, "reset"> = {
isMe,
duelId: "",
agentIndex: 0,
actionMsg: undefined
actionMsg: undefined,
main_deck: [],
main_deck_cards: [],
};
class MatStore implements MatState, NeosStore {
......@@ -115,6 +117,8 @@ class MatStore implements MatState, NeosStore {
duelId = initialState.duelId;
agentIndex = initialState.agentIndex;
actionMsg = initialState.actionMsg;
main_deck = initialState.main_deck;
main_deck_cards = initialState.main_deck_cards;
// methods
isMe = initialState.isMe;
......@@ -144,6 +148,11 @@ class MatStore implements MatState, NeosStore {
selectedList: [],
};
this.duelEnd = false;
this.duelId = "";
this.agentIndex = 0;
this.actionMsg = undefined;
this.main_deck = [];
this.main_deck_cards = [];
}
}
......
import type { ygopro } from "@/api";
import type { CardMeta, ygopro } from "@/api";
// >>> play mat state >>>
......@@ -53,6 +53,8 @@ export interface MatState {
duelId: string;
agentIndex: number;
actionMsg?: any;
main_deck: number[];
main_deck_cards: CardMeta[];
}
export interface InitInfo {
......
......@@ -32,6 +32,7 @@ import {
RoomStage,
roomStore,
sideStore,
matStore,
} from "@/stores";
import { Background, IconFont, Select, SpecialButton } from "@/ui/Shared";
......@@ -64,6 +65,7 @@ export const Component: React.FC = () => {
const updateDeck = (deck: IDeck) => {
sendUpdateDeck(deck);
matStore.main_deck = deck.main;
// 设置side里面的卡组
sideStore.setSideDeck(deck);
};
......
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