Commit 5a8b3a4f authored by sbl1996@126.com's avatar sbl1996@126.com

Add autoSelect

parent bde80f6a
Pipeline #28166 failed with stages
in 29 seconds
...@@ -369,7 +369,7 @@ export function convertDeckCard(meta: CardMeta): Card { ...@@ -369,7 +369,7 @@ export function convertDeckCard(meta: CardMeta): Card {
} }
export function convertCard(card: CardType, player: number): Card { export function convertCard(card: CardType, player: number): Card {
// TODO (ygo-agent): unseen cards // TODO (ygo-agent): opponent's visible facedown cards (confirm_cards)
return { return {
code: card.code, code: card.code,
location: cardZoneToLocation(card.location.zone), location: cardZoneToLocation(card.location.zone),
...@@ -478,6 +478,8 @@ interface MsgSelectCard { ...@@ -478,6 +478,8 @@ interface MsgSelectCard {
selected: number[]; selected: number[];
} }
export type MultiSelectMsg = MsgSelectCard | MsgSelectSum | MsgSelectTribute;
function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard { function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard {
// response is -1 for finish // response is -1 for finish
return { return {
...@@ -489,7 +491,6 @@ function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard { ...@@ -489,7 +491,6 @@ function convertMsgSelectCard(msg: GM.MsgSelectCard): MsgSelectCard {
location: convertCardLocation(c.location, msg.player), location: convertCardLocation(c.location, msg.player),
response: c.response response: c.response
})), })),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [], selected: [],
}; };
} }
...@@ -520,7 +521,6 @@ function convertMsgSelectTribute(msg: GM.MsgSelectTribute): MsgSelectTribute { ...@@ -520,7 +521,6 @@ function convertMsgSelectTribute(msg: GM.MsgSelectTribute): MsgSelectTribute {
level: c.level, level: c.level,
response: c.response, response: c.response,
})), })),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [], selected: [],
}; };
} }
...@@ -532,7 +532,7 @@ interface SelectSumCard { ...@@ -532,7 +532,7 @@ interface SelectSumCard {
response: number; response: number;
} }
interface MsgSelectSum { export interface MsgSelectSum {
msg_type: "select_sum"; msg_type: "select_sum";
overflow: boolean; overflow: boolean;
level_sum: number; level_sum: number;
...@@ -562,7 +562,6 @@ function convertMsgSelectSum(msg: GM.MsgSelectSum): MsgSelectSum { ...@@ -562,7 +562,6 @@ function convertMsgSelectSum(msg: GM.MsgSelectSum): MsgSelectSum {
level2: c.level2, level2: c.level2,
response: c.response, response: c.response,
})), })),
// TODO (ygo-agent): the indices of the selected `cards`
selected: [], selected: [],
} }
} }
...@@ -665,7 +664,8 @@ function convertMsgSelectIdleCmd(msg: GM.MsgSelectIdleCmd): MsgSelectIdleCmd { ...@@ -665,7 +664,8 @@ function convertMsgSelectIdleCmd(msg: GM.MsgSelectIdleCmd): MsgSelectIdleCmd {
// response will be 6 // response will be 6
idle_cmds.push({ cmd_type: IdleCmdType.ToBp }); idle_cmds.push({ cmd_type: IdleCmdType.ToBp });
} }
if (msg.enable_ep) { // TODO (ygo-agent): new models will support it
if (msg.enable_ep && !msg.enable_bp) {
// response will be 7 // response will be 7
idle_cmds.push({ cmd_type: IdleCmdType.ToEp }); idle_cmds.push({ cmd_type: IdleCmdType.ToEp });
} }
...@@ -724,20 +724,20 @@ function convertMsgSelectPosition(msg: GM.MsgSelectPosition): MsgSelectPosition ...@@ -724,20 +724,20 @@ function convertMsgSelectPosition(msg: GM.MsgSelectPosition): MsgSelectPosition
} }
interface MsgSelectYesNo { interface MsgSelectYesNo {
msg_type: "select_yes_no"; msg_type: "select_yesno";
effect_description: number; effect_description: number;
} }
function convertMsgSelectYesNo(msg: GM.MsgSelectYesNo): MsgSelectYesNo { function convertMsgSelectYesNo(msg: GM.MsgSelectYesNo): MsgSelectYesNo {
// response is 1 for yes and 0 for no // response is 1 for yes and 0 for no
return { return {
msg_type: "select_yes_no", msg_type: "select_yesno",
effect_description: msg.effect_description, effect_description: msg.effect_description,
}; };
} }
interface MsgSelectEffectYn { interface MsgSelectEffectYn {
msg_type: "select_effect_yn"; msg_type: "select_effectyn";
code: number; code: number;
location: CardLocation; location: CardLocation;
effect_description: number; effect_description: number;
...@@ -746,7 +746,7 @@ interface MsgSelectEffectYn { ...@@ -746,7 +746,7 @@ interface MsgSelectEffectYn {
function convertMsgSelectEffectYn(msg: GM.MsgSelectEffectYn): MsgSelectEffectYn { function convertMsgSelectEffectYn(msg: GM.MsgSelectEffectYn): MsgSelectEffectYn {
// response is 1 for yes and 0 for no // response is 1 for yes and 0 for no
return { return {
msg_type: "select_effect_yn", msg_type: "select_effectyn",
code: msg.code, code: msg.code,
location: convertCardLocation(msg.location, msg.player), location: convertCardLocation(msg.location, msg.player),
effect_description: msg.effect_description, effect_description: msg.effect_description,
...@@ -807,7 +807,8 @@ function convertMsgSelectBattleCmd(msg: GM.MsgSelectBattleCmd): MsgSelectBattleC ...@@ -807,7 +807,8 @@ function convertMsgSelectBattleCmd(msg: GM.MsgSelectBattleCmd): MsgSelectBattleC
// response will be 2 // response will be 2
battle_cmds.push({ cmd_type: BattleCmdType.ToM2 }); battle_cmds.push({ cmd_type: BattleCmdType.ToM2 });
} }
if (msg.enable_ep) { // TODO (ygo-agent): new models will support it
if (msg.enable_ep && !msg.enable_m2) {
// response will be 3 // response will be 3
battle_cmds.push({ cmd_type: BattleCmdType.ToEp }); battle_cmds.push({ cmd_type: BattleCmdType.ToEp });
} }
...@@ -886,6 +887,7 @@ interface MsgSelectPlace { ...@@ -886,6 +887,7 @@ interface MsgSelectPlace {
places: Place[]; places: Place[];
} }
// TODO (ygo-agent): SelectDisfield is different from SelectPlace
function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace { function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace {
return { return {
msg_type: "select_place", msg_type: "select_place",
...@@ -899,8 +901,6 @@ function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace { ...@@ -899,8 +901,6 @@ function convertMsgSelectPlace(msg: GM.MsgSelectPlace): MsgSelectPlace {
}; };
} }
// TODO (ygo-agent): MsgSelectDisfield
interface AnnounceAttrib { interface AnnounceAttrib {
attribute: Attribute; attribute: Attribute;
response: number; response: number;
...@@ -962,23 +962,6 @@ type ActionMsgData = ...@@ -962,23 +962,6 @@ type ActionMsgData =
MsgAnnounceAttrib | MsgAnnounceAttrib |
MsgAnnounceNumber; MsgAnnounceNumber;
// export enum ActionMsgName {
// AnnounceAttrib = "announce_attrib",
// AnnounceNumber = "announce_number",
// SelectBattlecmd = "select_battlecmd",
// SelectCard = "select_card",
// SelectChain = "select_chain",
// SelectDisfield = "select_disfield",
// SelectEffectyn = "select_effectyn",
// SelectIdlecmd = "select_idlecmd",
// SelectOption = "select_option",
// SelectPlace = "select_place",
// SelectPosition = "select_position",
// SelectSum = "select_sum",
// SelectTribute = "select_tribute",
// SelectUnselectCard = "select_unselect_card",
// SelectYesno = "select_yesno",
// }
interface ActionMsg { interface ActionMsg {
data: ActionMsgData; data: ActionMsgData;
...@@ -1059,6 +1042,7 @@ export interface Input { ...@@ -1059,6 +1042,7 @@ export interface Input {
interface ActionPredict { interface ActionPredict {
prob: number; prob: number;
response: number; response: number;
can_finish: boolean;
} }
export interface MsgResponse { export interface MsgResponse {
......
...@@ -12,7 +12,10 @@ import { ...@@ -12,7 +12,10 @@ import {
sendSortCardResponse, sendSortCardResponse,
} from "@/api"; } from "@/api";
import { cardStore, matStore } from "@/stores"; import { cardStore, matStore } from "@/stores";
import { Global, convertPhase, convertCard, convertDeckCard, parsePlayerFromMsg, convertActionMsg, Input } from "@/api/ygoAgent/schema"; import {
Global, convertPhase, convertCard, convertDeckCard,
parsePlayerFromMsg, convertActionMsg, Input, MultiSelectMsg, MsgSelectSum
} from "@/api/ygoAgent/schema";
import { predictDuel } from "@/api/ygoAgent/predict"; import { predictDuel } from "@/api/ygoAgent/predict";
function computeSetDifference(a1: number[], a2: number[]): number[] { function computeSetDifference(a1: number[], a2: number[]): number[] {
...@@ -20,10 +23,10 @@ function computeSetDifference(a1: number[], a2: number[]): number[] { ...@@ -20,10 +23,10 @@ function computeSetDifference(a1: number[], a2: number[]): number[] {
const freq2 = new Map<number, number>(); const freq2 = new Map<number, number>();
for (const num of a1) { for (const num of a1) {
freq1.set(num, (freq1.get(num) || 0) + 1); freq1.set(num, (freq1.get(num) || 0) + 1);
} }
for (const num of a2) { for (const num of a2) {
freq2.set(num, (freq2.get(num) || 0) + 1); freq2.set(num, (freq2.get(num) || 0) + 1);
} }
for (const [num, count] of freq2) { for (const [num, count] of freq2) {
...@@ -42,7 +45,7 @@ function computeSetDifference(a1: number[], a2: number[]): number[] { ...@@ -42,7 +45,7 @@ function computeSetDifference(a1: number[], a2: number[]): number[] {
} }
export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq { export function genInput(msg: ygopro.StocGameMessage): Input {
// 全局信息可以从 `matStore` 里面拿 // 全局信息可以从 `matStore` 里面拿
const mat = matStore; const mat = matStore;
// 卡片信息可以从 `cardStore` 里面拿 // 卡片信息可以从 `cardStore` 里面拿
...@@ -60,7 +63,6 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq { ...@@ -60,7 +63,6 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
// 这里已经保证 `msg` 是众多 `select_xxx` msg 中的一个 // 这里已经保证 `msg` 是众多 `select_xxx` msg 中的一个
const player = parsePlayerFromMsg(msg); const player = parsePlayerFromMsg(msg);
// TODO (ygo-agent): check if this is correct
const opponent = 1 - player; const opponent = 1 - player;
const cards = cardStore.inner const cards = cardStore.inner
...@@ -72,15 +74,15 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq { ...@@ -72,15 +74,15 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
) )
.map((card) => convertCard(card, player)); .map((card) => convertCard(card, player));
const card_codes_me = cardStore.inner const cardCodesMe = cardStore.inner
.filter((card) => .filter((card) =>
zones.includes(card.location.zone) && card.location.controller === player zones.includes(card.location.zone) && card.location.controller === player
) )
.map((card) => card.code); .map((card) => card.code);
const card_codes_me_deck = computeSetDifference(mat.main_deck, card_codes_me); const cardCodesMeDeck = computeSetDifference(mat.mainDeck, cardCodesMe);
const main_deck_card_meta = mat.main_deck_card_meta; const mainDeckCardMeta = mat.mainDeckCardMeta;
// TODO (ygo-agent): 临时方案,有很多边界情况未考虑 // TODO (ygo-agent): 临时方案,有很多边界情况未考虑
const deck_cards_me = card_codes_me_deck.map((code) => convertDeckCard(main_deck_card_meta.get(code)!)); const deckCardsMe = cardCodesMeDeck.map((code) => convertDeckCard(mainDeckCardMeta.get(code)!));
const turnPlayer = mat.currentPlayer; const turnPlayer = mat.currentPlayer;
const global: Global = { const global: Global = {
...@@ -89,28 +91,20 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq { ...@@ -89,28 +91,20 @@ export function genPredictReq(msg: ygopro.StocGameMessage): PredictReq {
my_lp: mat.initInfo.of(player).life, my_lp: mat.initInfo.of(player).life,
op_lp: mat.initInfo.of(opponent).life, op_lp: mat.initInfo.of(opponent).life,
phase: convertPhase(mat.phase.currentPhase), phase: convertPhase(mat.phase.currentPhase),
turn: mat.turn_count, turn: mat.turnCount,
} }
const actionMsg = convertActionMsg(msg); const actionMsg = convertActionMsg(msg);
const input: Input = { return {
global: global, global: global,
cards: deck_cards_me.concat(cards), cards: deckCardsMe.concat(cards),
action_msg: actionMsg, action_msg: actionMsg,
} }
return {
index: mat.agentIndex,
input: input,
prev_action_idx: mat.prevActionIndex,
}
} }
export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) { async function sendRequest(req: PredictReq) {
// const msg = matStore.actionMsg;
const req = genPredictReq(msg);
console.log("Sending predict request:", req); console.log("Sending predict request:", req);
const duelId = matStore.duelId; const duelId = matStore.duelId;
const resp = await predictDuel(duelId, req); const resp = await predictDuel(duelId, req);
...@@ -119,59 +113,121 @@ export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) { ...@@ -119,59 +113,121 @@ export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) {
matStore.agentIndex = resp.index; matStore.agentIndex = resp.index;
} }
else { else {
console.error("Failed to get predict response"); throw new Error("Failed to get predict response");
return;
} }
const preds = resp.predict_results.action_preds; const preds = resp.predict_results.action_preds;
const action_idx = argmax(preds, (r) => r.prob); const actionIdx = argmax(preds, (r) => r.prob);
matStore.prevActionIndex = action_idx; matStore.prevActionIndex = actionIdx;
const response = preds[action_idx].response; const pred = preds[actionIdx];
const msg_name = req.input.action_msg.data.msg_type; return pred;
switch (msg_name) { }
case "announce_attrib":
case "announce_number": export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) {
sendSelectOptionResponse(response); const input = genInput(msg);
break; const msgName = input.action_msg.data.msg_type;
case "select_battle_cmd": const multiSelectMsgs = ["select_card", "select_tribute", "select_sum"];
sendSelectBattleCmdResponse(response);
break; if (multiSelectMsgs.includes(msgName)) {
case "select_chain": switch (msgName) {
sendSelectSingleResponse(response); case "select_tribute":
break; case "select_card": {
case "select_yes_no": const msg_ = input.action_msg.data as MultiSelectMsg;
case "select_effect_yn": const selected = [];
sendSelectEffectYnResponse(response === 1); const responses = [];
break; while (true) {
case "select_idle_cmd": msg_.selected = selected;
sendSelectIdleCmdResponse(response); const req = {
break; index: matStore.agentIndex,
case "select_option": input: input,
sendSelectOptionResponse(response); prev_action_idx: matStore.prevActionIndex,
break; };
case "select_position": const response = (await sendRequest(req)).response;
sendSelectPositionResponse(convertPositionResponse(response)); if (response != -1) {
break; selected.push(matStore.prevActionIndex);
case "select_place": responses.push(response);
const place = (msg as unknown as ygopro.StocGameMessage.MsgSelectPlace).places[response]; }
sendSelectPlaceResponse({ if (response == -1 || selected.length == msg_.max) {
controller: place.controller, sendSelectMultiResponse(responses);
zone: place.zone, break;
sequence: place.sequence, }
}); }
break; break;
case "select_unselect_card":
case "select_card":
const msg_ = msg as unknown as ygopro.StocGameMessage.MsgSelectCard;
if (msg_.min === 1 && msg_.max === 1) {
sendSelectMultiResponse([response]);
} else {
throw new Error(`Unsupported select_card for min=${msg_.min}, max=${msg_.max}`);
} }
break; case "select_sum":
case "select_sum": const msg_ = input.action_msg.data as MsgSelectSum;
case "select_tribute": const selected = [];
throw new Error(`Unsupported msg_name: ${msg_name}`); const responses = [];
for (const c of msg_.must_cards) {
responses.push(c.response);
}
while (true) {
msg_.selected = selected;
const req = {
index: matStore.agentIndex,
input: input,
prev_action_idx: matStore.prevActionIndex,
};
const pred = await sendRequest(req);
const idx = matStore.prevActionIndex
selected.push(idx);
responses.push(pred.response);
if (pred.can_finish) {
sendSelectMultiResponse(responses);
break;
}
}
break;
}
} else {
const req = {
index: matStore.agentIndex,
input: input,
prev_action_idx: matStore.prevActionIndex,
};
const response = (await sendRequest(req)).response;
switch (msgName) {
case "announce_attrib":
case "announce_number":
sendSelectOptionResponse(response);
break;
case "select_battlecmd":
sendSelectBattleCmdResponse(response);
break;
case "select_chain":
sendSelectSingleResponse(response);
break;
case "select_yesno":
case "select_effectyn":
sendSelectEffectYnResponse(response === 1);
break;
case "select_idlecmd":
sendSelectIdleCmdResponse(response);
break;
case "select_option":
sendSelectOptionResponse(response);
break;
case "select_position":
sendSelectPositionResponse(convertPositionResponse(response));
break;
case "select_place": {
const place = (msg as unknown as ygopro.StocGameMessage.MsgSelectPlace).places[response];
sendSelectPlaceResponse({
controller: place.controller,
zone: place.zone,
sequence: place.sequence,
});
break;
}
case "select_unselect_card": {
if (response == -1) {
sendSelectSingleResponse(-1);
} else {
sendSelectMultiResponse([response]);
}
break;
}
}
} }
} }
......
...@@ -3,11 +3,14 @@ import { displayOptionModal } from "@/ui/Duel/Message"; ...@@ -3,11 +3,14 @@ import { displayOptionModal } from "@/ui/Duel/Message";
import MsgAnnounce = ygopro.StocGameMessage.MsgAnnounce; import MsgAnnounce = ygopro.StocGameMessage.MsgAnnounce;
import { displayAnnounceModal } from "@/ui/Duel/Message/AnnounceModal"; import { displayAnnounceModal } from "@/ui/Duel/Message/AnnounceModal";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
export default async (announce: MsgAnnounce) => { export default async (announce: MsgAnnounce) => {
console.log("intercept announce"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(announce as unknown as ygopro.StocGameMessage); console.log("intercept announce");
return; await sendAIPredictAsResponse(announce as unknown as ygopro.StocGameMessage);
return;
}
const type_ = announce.announce_type; const type_ = announce.announce_type;
let min = announce.min; let min = announce.min;
......
...@@ -100,6 +100,8 @@ export default async function handleGameMsg( ...@@ -100,6 +100,8 @@ export default async function handleGameMsg(
if (replayStore.isReplay && ReplayIgnoreMsg.includes(msg.gameMsg)) return; if (replayStore.isReplay && ReplayIgnoreMsg.includes(msg.gameMsg)) return;
console.log(msg.gameMsg);
switch (msg.gameMsg) { switch (msg.gameMsg) {
case "start": { case "start": {
await onMsgStart(msg.start); await onMsgStart(msg.start);
......
...@@ -6,5 +6,5 @@ export default (newTurn: ygopro.StocGameMessage.MsgNewTurn) => { ...@@ -6,5 +6,5 @@ export default (newTurn: ygopro.StocGameMessage.MsgNewTurn) => {
playEffect(AudioActionType.SOUND_NEXT_TURN); playEffect(AudioActionType.SOUND_NEXT_TURN);
const player = newTurn.player; const player = newTurn.player;
matStore.currentPlayer = player; matStore.currentPlayer = player;
matStore.turn_count = matStore.turn_count + 1; matStore.turnCount = matStore.turnCount + 1;
}; };
...@@ -18,9 +18,11 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => { ...@@ -18,9 +18,11 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => {
card.idleInteractivities = []; card.idleInteractivities = [];
}); });
console.log("intercept selectBattleCmd"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectBattleCmd as unknown as ygopro.StocGameMessage); console.log("intercept selectBattleCmd");
return; await sendAIPredictAsResponse(selectBattleCmd as unknown as ygopro.StocGameMessage);
return;
}
cmds.forEach((cmd) => { cmds.forEach((cmd) => {
const interactType = battleTypeToInteracType(cmd.battle_type); const interactType = battleTypeToInteracType(cmd.battle_type);
......
...@@ -6,23 +6,26 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal" ...@@ -6,23 +6,26 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
export default async (selectCard: MsgSelectCard) => { export default async (selectCard: MsgSelectCard) => {
const { cancelable, min, max, cards } = selectCard; const { cancelable, min, max, cards } = selectCard;
// TODO: handle release_param // TODO: handle release_param
if (matStore.autoSelect) {
console.log("intercept selectCard");
await sendAIPredictAsResponse(selectCard as unknown as ygopro.StocGameMessage);
return;
}
if (!cancelable && cards.length === 1) { if (!cancelable && cards.length === 1) {
// auto send // auto send
sendSelectMultiResponse([cards[0].response]); sendSelectMultiResponse([cards[0].response]);
return; return;
} }
if (min === 1 && max === 1) {
console.log("intercept selectCard");
await sendAIPredictAsResponse(selectCard as unknown as ygopro.StocGameMessage);
return;
}
const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta( const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta(
cards, cards,
......
...@@ -67,9 +67,11 @@ export default async (selectChain: MsgSelectChain) => { ...@@ -67,9 +67,11 @@ export default async (selectChain: MsgSelectChain) => {
} }
case 2: // 处理多张 case 2: // 处理多张
case 3: { case 3: {
console.log("intercept selectChain"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectChain as unknown as ygopro.StocGameMessage); console.log("intercept selectChain");
return; await sendAIPredictAsResponse(selectChain as unknown as ygopro.StocGameMessage);
return;
}
// 处理强制发动的卡 // 处理强制发动的卡
fetchSelectHintMeta({ fetchSelectHintMeta({
......
...@@ -2,14 +2,17 @@ import { fetchStrings, Region, type ygopro } from "@/api"; ...@@ -2,14 +2,17 @@ import { fetchStrings, Region, type ygopro } from "@/api";
import { CardMeta, fetchCard } from "@/api/cards"; import { CardMeta, fetchCard } from "@/api/cards";
import { displayYesNoModal } from "@/ui/Duel/Message"; import { displayYesNoModal } from "@/ui/Duel/Message";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
type MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn; type MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn;
// 这里改成了 async 不知道有没有影响 // 这里改成了 async 不知道有没有影响
export default async (selectEffectYn: MsgSelectEffectYn) => { export default async (selectEffectYn: MsgSelectEffectYn) => {
console.log("intercept announce"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectEffectYn as unknown as ygopro.StocGameMessage); console.log("intercept selectEffectYn");
return; await sendAIPredictAsResponse(selectEffectYn as unknown as ygopro.StocGameMessage);
return;
}
const code = selectEffectYn.code; const code = selectEffectYn.code;
const location = selectEffectYn.location; const location = selectEffectYn.location;
......
...@@ -18,9 +18,11 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => { ...@@ -18,9 +18,11 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => {
card.idleInteractivities = []; card.idleInteractivities = [];
}); });
console.log("intercept selectIdleCmd"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectIdleCmd as unknown as ygopro.StocGameMessage); console.log("intercept selectIdleCmd");
return; await sendAIPredictAsResponse(selectIdleCmd as unknown as ygopro.StocGameMessage);
return;
}
cmds.forEach((cmd) => { cmds.forEach((cmd) => {
const interactType = idleTypeToInteractType(cmd.idle_type); const interactType = idleTypeToInteractType(cmd.idle_type);
......
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
} from "@/api"; } from "@/api";
import { displayOptionModal } from "@/ui/Duel/Message"; import { displayOptionModal } from "@/ui/Duel/Message";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
export default async (selectOption: ygopro.StocGameMessage.MsgSelectOption) => { export default async (selectOption: ygopro.StocGameMessage.MsgSelectOption) => {
const options = selectOption.options; const options = selectOption.options;
...@@ -14,15 +15,18 @@ export default async (selectOption: ygopro.StocGameMessage.MsgSelectOption) => { ...@@ -14,15 +15,18 @@ export default async (selectOption: ygopro.StocGameMessage.MsgSelectOption) => {
sendSelectOptionResponse(0); sendSelectOptionResponse(0);
return; return;
} }
if (matStore.autoSelect) {
console.log("intercept selectOption");
await sendAIPredictAsResponse(selectOption as unknown as ygopro.StocGameMessage);
return;
}
if (options.length === 1) { if (options.length === 1) {
sendSelectOptionResponse(options[0].response); sendSelectOptionResponse(options[0].response);
return; return;
} }
console.log("intercept selectOption");
await sendAIPredictAsResponse(selectOption as unknown as ygopro.StocGameMessage);
return;
await displayOptionModal( await displayOptionModal(
fetchStrings(Region.System, 556), fetchStrings(Region.System, 556),
options.map(({ code, response }) => ({ options.map(({ code, response }) => ({
......
import { sendSelectPlaceResponse, ygopro } from "@/api"; import { sendSelectPlaceResponse, ygopro } from "@/api";
import { InteractType, placeStore } from "@/stores"; import { InteractType, placeStore, matStore } from "@/stores";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
type MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace; type MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
...@@ -10,6 +10,12 @@ export default async (selectPlace: MsgSelectPlace) => { ...@@ -10,6 +10,12 @@ export default async (selectPlace: MsgSelectPlace) => {
return; return;
} }
if (matStore.autoSelect) {
console.log("intercept selectPlace");
await sendAIPredictAsResponse(selectPlace as unknown as ygopro.StocGameMessage);
return;
}
if (selectPlace.places.length === 1) { if (selectPlace.places.length === 1) {
const place = selectPlace.places[0]; const place = selectPlace.places[0];
sendSelectPlaceResponse({ sendSelectPlaceResponse({
...@@ -21,26 +27,22 @@ export default async (selectPlace: MsgSelectPlace) => { ...@@ -21,26 +27,22 @@ export default async (selectPlace: MsgSelectPlace) => {
return; return;
} }
console.log("intercept announce"); for (const place of selectPlace.places) {
await sendAIPredictAsResponse(selectPlace as unknown as ygopro.StocGameMessage); switch (place.zone) {
return; case ygopro.CardZone.MZONE:
case ygopro.CardZone.SZONE:
// for (const place of selectPlace.places) { const block = placeStore.of(place);
// switch (place.zone) { if (block) {
// case ygopro.CardZone.MZONE: block.interactivity = {
// case ygopro.CardZone.SZONE: interactType: InteractType.PLACE_SELECTABLE,
// const block = placeStore.of(place); response: {
// if (block) { controller: place.controller,
// block.interactivity = { zone: place.zone,
// interactType: InteractType.PLACE_SELECTABLE, sequence: place.sequence,
// response: { },
// controller: place.controller, };
// zone: place.zone, }
// sequence: place.sequence, break;
// }, }
// }; }
// }
// break;
// }
// }
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { displayPositionModal } from "@/ui/Duel/Message"; import { displayPositionModal } from "@/ui/Duel/Message";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
type MsgSelectPosition = ygopro.StocGameMessage.MsgSelectPosition; type MsgSelectPosition = ygopro.StocGameMessage.MsgSelectPosition;
export default async (selectPosition: MsgSelectPosition) => { export default async (selectPosition: MsgSelectPosition) => {
console.log("intercept announce"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectPosition as unknown as ygopro.StocGameMessage); console.log("intercept selectPosition");
return; await sendAIPredictAsResponse(selectPosition as unknown as ygopro.StocGameMessage);
return;
}
const _player = selectPosition.player; const _player = selectPosition.player;
const positions = selectPosition.positions.map( const positions = selectPosition.positions.map(
......
...@@ -4,7 +4,16 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal" ...@@ -4,7 +4,16 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum; type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum;
import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
export default async (selectSum: MsgSelectSum) => { export default async (selectSum: MsgSelectSum) => {
if (matStore.autoSelect) {
console.log("intercept selectSum");
await sendAIPredictAsResponse(selectSum as unknown as ygopro.StocGameMessage);
return;
}
const { const {
selecteds: selecteds1, selecteds: selecteds1,
mustSelects: mustSelect1, mustSelects: mustSelect1,
......
...@@ -4,11 +4,20 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal" ...@@ -4,11 +4,20 @@ import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute; type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute;
import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
export default async (selectTribute: MsgSelectTribute) => { export default async (selectTribute: MsgSelectTribute) => {
// TODO: 当玩家选择卡数大于`max`时,是否也合法? if (matStore.autoSelect) {
console.log("intercept selectTribute");
await sendAIPredictAsResponse(selectTribute as unknown as ygopro.StocGameMessage);
return;
}
const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta( const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta(
selectTribute.selectable_cards, selectTribute.selectable_cards,
); );
// TODO: 当玩家选择卡数大于`max`时,是否也合法?
await displaySelectActionsModal({ await displaySelectActionsModal({
overflow: true, overflow: true,
totalLevels: 0, totalLevels: 0,
......
...@@ -6,14 +6,23 @@ import { fetchCheckCardMeta } from "../utils"; ...@@ -6,14 +6,23 @@ import { fetchCheckCardMeta } from "../utils";
import { isAllOnField } from "./util"; import { isAllOnField } from "./util";
type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard; type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
export default async ({ import { sendAIPredictAsResponse } from "@/service/duel/agent";
finishable,
cancelable, export default async (selectUnselectCards: MsgSelectUnselectCard) => {
min, if (matStore.autoSelect) {
max, console.log("intercept selectUnselectCards");
selectable_cards: selectableCards, await sendAIPredictAsResponse(selectUnselectCards as unknown as ygopro.StocGameMessage);
selected_cards: selectedCards, return;
}: MsgSelectUnselectCard) => { }
const {
finishable,
cancelable,
min,
max,
selectable_cards: selectableCards,
selected_cards: selectedCards,
} = selectUnselectCards;
if ( if (
isAllOnField( isAllOnField(
selectableCards.concat(selectedCards).map((info) => info.location), selectableCards.concat(selectedCards).map((info) => info.location),
......
import { getStrings, ygopro } from "@/api"; import { getStrings, ygopro } from "@/api";
import { displayYesNoModal } from "@/ui/Duel/Message"; import { displayYesNoModal } from "@/ui/Duel/Message";
import { sendAIPredictAsResponse } from "@/service/duel/agent"; import { sendAIPredictAsResponse } from "@/service/duel/agent";
import { matStore } from "@/stores";
type MsgSelectYesNo = ygopro.StocGameMessage.MsgSelectYesNo; type MsgSelectYesNo = ygopro.StocGameMessage.MsgSelectYesNo;
export default async (selectYesNo: MsgSelectYesNo) => { export default async (selectYesNo: MsgSelectYesNo) => {
console.log("intercept selectYesNo"); if (matStore.autoSelect) {
await sendAIPredictAsResponse(selectYesNo as unknown as ygopro.StocGameMessage); console.log("intercept selectYesNo");
return; await sendAIPredictAsResponse(selectYesNo as unknown as ygopro.StocGameMessage);
return;
}
const _player = selectYesNo.player; const _player = selectYesNo.player;
const effect_description = selectYesNo.effect_description; const effect_description = selectYesNo.effect_description;
......
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { displaySortCardModal } from "@/ui/Duel/Message"; import { displaySortCardModal } from "@/ui/Duel/Message";
import { matStore } from "@/stores";
type MsgSortCard = ygopro.StocGameMessage.MsgSortCard; type MsgSortCard = ygopro.StocGameMessage.MsgSortCard;
export default async (sortCard: MsgSortCard) => { export default async (sortCard: MsgSortCard) => {
if (matStore.autoSelect) {
console.log("intercept selectTribute");
// TODO (ygo-agent): don't sort, should response 255
}
const options = await Promise.all( const options = await Promise.all(
sortCard.options.map(async ({ code, response }) => { sortCard.options.map(async ({ code, response }) => {
const meta = fetchCard(code!); const meta = fetchCard(code!);
......
...@@ -92,7 +92,7 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => { ...@@ -92,7 +92,7 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
const { duelId, index } = (await createDuel())!; const { duelId, index } = (await createDuel())!;
matStore.duelId = duelId; matStore.duelId = duelId;
matStore.agentIndex = index; matStore.agentIndex = index;
matStore.main_deck_card_meta = matStore.main_deck matStore.mainDeckCardMeta = matStore.mainDeck
.reduce((map, item) => { .reduce((map, item) => {
if (!map.has(item)) { if (!map.has(item)) {
map.set(item, fetchCard(item)); map.set(item, fetchCard(item));
......
...@@ -93,13 +93,13 @@ const initialState: Omit<MatState, "reset"> = { ...@@ -93,13 +93,13 @@ const initialState: Omit<MatState, "reset"> = {
duelEnd: false, duelEnd: false,
// methods // methods
isMe, isMe,
turn_count: 0, turnCount: 0,
duelId: "", duelId: "",
agentIndex: 0, agentIndex: 0,
prevActionIndex: 0, prevActionIndex: 0,
actionMsg: undefined, mainDeck: [],
main_deck: [], mainDeckCardMeta: new Map(),
main_deck_card_meta: new Map(), autoSelect: false,
}; };
class MatStore implements MatState, NeosStore { class MatStore implements MatState, NeosStore {
...@@ -116,13 +116,13 @@ class MatStore implements MatState, NeosStore { ...@@ -116,13 +116,13 @@ class MatStore implements MatState, NeosStore {
tossResult = initialState.tossResult; tossResult = initialState.tossResult;
selectUnselectInfo = initialState.selectUnselectInfo; selectUnselectInfo = initialState.selectUnselectInfo;
duelEnd = initialState.duelEnd; duelEnd = initialState.duelEnd;
turn_count = initialState.turn_count; turnCount = initialState.turnCount;
duelId = initialState.duelId; duelId = initialState.duelId;
agentIndex = initialState.agentIndex; agentIndex = initialState.agentIndex;
prevActionIndex = initialState.prevActionIndex; prevActionIndex = initialState.prevActionIndex;
actionMsg = initialState.actionMsg; mainDeck = initialState.mainDeck;
main_deck = initialState.main_deck; mainDeckCardMeta = initialState.mainDeckCardMeta;
main_deck_card_meta = initialState.main_deck_card_meta; autoSelect = initialState.autoSelect;
// methods // methods
isMe = initialState.isMe; isMe = initialState.isMe;
...@@ -152,13 +152,13 @@ class MatStore implements MatState, NeosStore { ...@@ -152,13 +152,13 @@ class MatStore implements MatState, NeosStore {
selectedList: [], selectedList: [],
}; };
this.duelEnd = false; this.duelEnd = false;
this.turn_count = 0; this.turnCount = 0;
this.duelId = ""; this.duelId = "";
this.agentIndex = 0; this.agentIndex = 0;
this.prevActionIndex = 0; this.prevActionIndex = 0;
this.actionMsg = undefined; this.mainDeck = [];
this.main_deck = []; this.mainDeckCardMeta = new Map();
this.main_deck_card_meta = new Map(); this.autoSelect = false;
} }
} }
......
...@@ -50,14 +50,13 @@ export interface MatState { ...@@ -50,14 +50,13 @@ export interface MatState {
/** 根据自己的先后手判断是否是自己 */ /** 根据自己的先后手判断是否是自己 */
isMe: (player: number) => boolean; isMe: (player: number) => boolean;
// TODO (ygo-agent): 检查实现是否正确 turnCount: number,
turn_count: number,
duelId: string; duelId: string;
agentIndex: number; agentIndex: number;
prevActionIndex: number; prevActionIndex: number;
actionMsg?: any; mainDeck: number[];
main_deck: number[]; mainDeckCardMeta: Map<number, CardMeta>;
main_deck_card_meta: Map<number, CardMeta>; autoSelect: boolean;
} }
export interface InitInfo { export interface InitInfo {
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
MessageFilled, MessageFilled,
StepForwardFilled, StepForwardFilled,
RobotFilled, RobotFilled,
RobotOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
Button, Button,
...@@ -29,7 +30,6 @@ import { ...@@ -29,7 +30,6 @@ 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 { sendAIPredictAsResponse } 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;
...@@ -215,6 +215,7 @@ export const Menu = () => { ...@@ -215,6 +215,7 @@ export const Menu = () => {
const [phaseSwitchItems, setPhaseSwitchItems] = useState<MenuProps["items"]>( const [phaseSwitchItems, setPhaseSwitchItems] = useState<MenuProps["items"]>(
[], [],
); );
const [autoSelect, setAutoSelect] = useState(false);
useEffect(() => { useEffect(() => {
const endResponse = [ const endResponse = [
...@@ -299,8 +300,10 @@ export const Menu = () => { ...@@ -299,8 +300,10 @@ export const Menu = () => {
const globalDisable = !matStore.isMe(currentPlayer); const globalDisable = !matStore.isMe(currentPlayer);
const aiPredict = async () => { const switchAutoSelect = () => {
await sendAIPredictAsResponse(); const newAutoSelect = !autoSelect;
matStore.autoSelect = newAutoSelect;
setAutoSelect(newAutoSelect);
}; };
return ( return (
...@@ -331,8 +334,8 @@ export const Menu = () => { ...@@ -331,8 +334,8 @@ export const Menu = () => {
</DropdownWithTitle> </DropdownWithTitle>
<Tooltip title="AI"> <Tooltip title="AI">
<Button <Button
icon={<RobotFilled />} icon={autoSelect ? <RobotFilled /> : <RobotOutlined />}
onClick={aiPredict} onClick={switchAutoSelect}
type="text" type="text"
></Button> ></Button>
</Tooltip> </Tooltip>
......
...@@ -65,7 +65,7 @@ export const Component: React.FC = () => { ...@@ -65,7 +65,7 @@ export const Component: React.FC = () => {
const updateDeck = (deck: IDeck) => { const updateDeck = (deck: IDeck) => {
sendUpdateDeck(deck); sendUpdateDeck(deck);
matStore.main_deck = deck.main; matStore.mainDeck = deck.main;
// 设置side里面的卡组 // 设置side里面的卡组
sideStore.setSideDeck(deck); 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