Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
1
Issues
1
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Neos
Commits
5e3a7019
"...svn:/svn.code.sf.net/p/irrlicht/code/trunk@5271" did not exist on "49b00b631a33f98b12576f1e6c9bbf55476a9fad"
Commit
5e3a7019
authored
Jul 11, 2024
by
Chunchi Che
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor ai predict
parent
e0e5c7a6
Pipeline
#28402
passed with stages
in 9 minutes and 56 seconds
Changes
8
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
940 additions
and
843 deletions
+940
-843
src/api/ygoAgent/index.ts
src/api/ygoAgent/index.ts
+1
-0
src/api/ygoAgent/schema.ts
src/api/ygoAgent/schema.ts
+32
-701
src/api/ygoAgent/transaction.ts
src/api/ygoAgent/transaction.ts
+778
-0
src/common.ts
src/common.ts
+62
-55
src/service/duel/agent.ts
src/service/duel/agent.ts
+20
-86
src/service/duel/util.ts
src/service/duel/util.ts
+45
-0
src/stores/matStore/types.ts
src/stores/matStore/types.ts
+1
-0
src/ui/Duel/Message/NeosModal/index.tsx
src/ui/Duel/Message/NeosModal/index.tsx
+1
-1
No files found.
src/api/ygoAgent/index.ts
View file @
5e3a7019
export
*
from
"
./create
"
;
export
*
from
"
./delete
"
;
export
*
from
"
./predict
"
;
export
*
from
"
./transaction
"
;
src/api/ygoAgent/schema.ts
View file @
5e3a7019
import
{
CardMeta
,
ygopro
}
from
"
@/api
"
;
import
{
extraCardTypes
}
from
"
@/common
"
;
import
{
CardType
}
from
"
@/stores/cardStore
"
;
import
GM
=
ygopro
.
StocGameMessage
;
// Data schema for YgoAgent Service
/**
* none for N/A or unknown or token.
*/
enum
Attribute
{
e
xport
e
num
Attribute
{
None
=
"
none
"
,
Earth
=
"
earth
"
,
Water
=
"
water
"
,
...
...
@@ -18,38 +14,13 @@ enum Attribute {
Divine
=
"
divine
"
,
}
// from common.ts ATTRIBUTE_*
// TODO (ygo-agent): replace literal numbers with constants
function
numberToAttribute
(
attributeNumber
:
number
):
Attribute
{
switch
(
attributeNumber
)
{
case
0x00
:
return
Attribute
.
None
;
case
0x01
:
return
Attribute
.
Earth
;
case
0x02
:
return
Attribute
.
Water
;
case
0x04
:
return
Attribute
.
Fire
;
case
0x08
:
return
Attribute
.
Wind
;
case
0x10
:
return
Attribute
.
Light
;
case
0x20
:
return
Attribute
.
Dark
;
case
0x40
:
return
Attribute
.
Divine
;
default
:
throw
new
Error
(
`Unknown attribute number:
${
attributeNumber
}
`
);
}
}
enum
Controller
{
export
enum
Controller
{
Me
=
"
me
"
,
Opponent
=
"
opponent
"
,
}
//
enum
Location
{
e
xport
e
num
Location
{
Deck
=
"
deck
"
,
Extra
=
"
extra
"
,
Grave
=
"
grave
"
,
...
...
@@ -59,27 +30,6 @@ enum Location {
SZone
=
"
szone
"
,
}
function
cardZoneToLocation
(
zone
:
ygopro
.
CardZone
):
Location
{
switch
(
zone
)
{
case
ygopro
.
CardZone
.
DECK
:
return
Location
.
Deck
;
case
ygopro
.
CardZone
.
HAND
:
return
Location
.
Hand
;
case
ygopro
.
CardZone
.
MZONE
:
return
Location
.
MZone
;
case
ygopro
.
CardZone
.
SZONE
:
return
Location
.
SZone
;
case
ygopro
.
CardZone
.
GRAVE
:
return
Location
.
Grave
;
case
ygopro
.
CardZone
.
REMOVED
:
return
Location
.
Removed
;
case
ygopro
.
CardZone
.
EXTRA
:
return
Location
.
Extra
;
default
:
throw
new
Error
(
`Unknown card zone:
${
zone
}
`
);
}
}
interface
Place
{
controller
:
Controller
;
location
:
Location
;
...
...
@@ -93,7 +43,7 @@ interface Option {
code
:
number
;
}
interface
CardLocation
{
export
interface
CardLocation
{
controller
:
Controller
;
location
:
Location
;
/**
...
...
@@ -106,30 +56,10 @@ interface CardLocation {
sequence
:
number
;
}
function
convertController
(
controller
:
number
,
player
:
number
):
Controller
{
return
controller
===
player
?
Controller
.
Me
:
Controller
.
Opponent
;
}
function
convertOverlaySequence
(
cl
:
ygopro
.
CardLocation
):
number
{
return
cl
.
is_overlay
?
cl
.
overlay_sequence
:
-
1
;
}
function
convertCardLocation
(
cl
:
ygopro
.
CardLocation
,
player
:
number
,
):
CardLocation
{
return
{
controller
:
convertController
(
cl
.
controller
,
player
),
location
:
cardZoneToLocation
(
cl
.
zone
),
overlay_sequence
:
convertOverlaySequence
(
cl
),
sequence
:
cl
.
sequence
,
};
}
/**
* If the monster is xyz material (overlay_sequence != -1), the position is faceup.
*/
enum
Position
{
e
xport
e
num
Position
{
None
=
"
none
"
,
FaceupAttack
=
"
faceup_attack
"
,
FacedownAttack
=
"
facedown_attack
"
,
...
...
@@ -141,33 +71,10 @@ enum Position {
Defense
=
"
defense
"
,
}
function
convertPosition
(
position
:
ygopro
.
CardPosition
):
Position
{
switch
(
position
)
{
case
ygopro
.
CardPosition
.
FACEUP_ATTACK
:
return
Position
.
FaceupAttack
;
case
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
:
return
Position
.
FacedownAttack
;
case
ygopro
.
CardPosition
.
FACEUP_DEFENSE
:
return
Position
.
FaceupDefense
;
case
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
:
return
Position
.
FacedownDefense
;
case
ygopro
.
CardPosition
.
FACEUP
:
return
Position
.
Faceup
;
case
ygopro
.
CardPosition
.
FACEDOWN
:
return
Position
.
Facedown
;
case
ygopro
.
CardPosition
.
ATTACK
:
return
Position
.
Attack
;
case
ygopro
.
CardPosition
.
DEFENSE
:
return
Position
.
Defense
;
default
:
throw
new
Error
(
`Unknown card position:
${
position
}
`
);
}
}
/**
* none for N/A or unknown or token.
*/
enum
Race
{
e
xport
e
num
Race
{
Aqua
=
"
aqua
"
,
Beast
=
"
beast
"
,
BeastWarrior
=
"
beast_warrior
"
,
...
...
@@ -197,68 +104,7 @@ enum Race {
Zombie
=
"
zombie
"
,
}
// from common.ts RACE_*
// TODO (ygo-agent): replace literal numbers with constants
function
numberToRace
(
raceNumber
:
number
):
Race
{
switch
(
raceNumber
)
{
case
0x0
:
return
Race
.
None
;
case
0x1
:
return
Race
.
Warrior
;
case
0x2
:
return
Race
.
Spellcaster
;
case
0x4
:
return
Race
.
Fairy
;
case
0x8
:
return
Race
.
Fiend
;
case
0x10
:
return
Race
.
Zombie
;
case
0x20
:
return
Race
.
Machine
;
case
0x40
:
return
Race
.
Aqua
;
case
0x80
:
return
Race
.
Pyro
;
case
0x100
:
return
Race
.
Rock
;
case
0x200
:
return
Race
.
Windbeast
;
case
0x400
:
return
Race
.
Plant
;
case
0x800
:
return
Race
.
Insect
;
case
0x1000
:
return
Race
.
Thunder
;
case
0x2000
:
return
Race
.
Dragon
;
case
0x4000
:
return
Race
.
Beast
;
case
0x8000
:
return
Race
.
BeastWarrior
;
case
0x10000
:
return
Race
.
Dinosaur
;
case
0x20000
:
return
Race
.
Fish
;
case
0x40000
:
return
Race
.
SeaSerpent
;
case
0x80000
:
return
Race
.
Reptile
;
case
0x100000
:
return
Race
.
Psycho
;
case
0x200000
:
return
Race
.
Devine
;
case
0x400000
:
return
Race
.
CreatorGod
;
case
0x800000
:
return
Race
.
Wyrm
;
case
0x1000000
:
return
Race
.
Cyberse
;
default
:
throw
new
Error
(
`Unknown race number:
${
raceNumber
}
`
);
}
}
enum
Type
{
export
enum
Type
{
Continuous
=
"
continuous
"
,
Counter
=
"
counter
"
,
Dual
=
"
dual
"
,
...
...
@@ -286,66 +132,7 @@ enum Type {
Xyz
=
"
xyz
"
,
}
// from common.ts TYPE_*
// TODO (ygo-agent): replace literal numbers with constants
function
numberToType
(
typeNumber
:
number
):
Type
{
switch
(
typeNumber
)
{
case
0x1
:
return
Type
.
Monster
;
case
0x2
:
return
Type
.
Spell
;
case
0x4
:
return
Type
.
Trap
;
case
0x10
:
return
Type
.
Normal
;
case
0x20
:
return
Type
.
Effect
;
case
0x40
:
return
Type
.
Fusion
;
case
0x80
:
return
Type
.
Ritual
;
case
0x100
:
return
Type
.
TrapMonster
;
case
0x200
:
return
Type
.
Spirit
;
case
0x400
:
return
Type
.
Union
;
case
0x800
:
return
Type
.
Dual
;
case
0x1000
:
return
Type
.
Tuner
;
case
0x2000
:
return
Type
.
Synchro
;
case
0x4000
:
return
Type
.
Token
;
case
0x10000
:
return
Type
.
QuickPlay
;
case
0x20000
:
return
Type
.
Continuous
;
case
0x40000
:
return
Type
.
Equip
;
case
0x80000
:
return
Type
.
Field
;
case
0x100000
:
return
Type
.
Counter
;
case
0x200000
:
return
Type
.
Flip
;
case
0x400000
:
return
Type
.
Toon
;
case
0x800000
:
return
Type
.
Xyz
;
case
0x1000000
:
return
Type
.
Pendulum
;
case
0x2000000
:
return
Type
.
Special
;
case
0x4000000
:
return
Type
.
Link
;
default
:
throw
new
Error
(
`Unknown type number:
${
typeNumber
}
`
);
}
}
interface
Card
{
export
interface
Card
{
/**
* Card code from cards.cdb
*/
...
...
@@ -392,56 +179,7 @@ interface Card {
types
:
Type
[];
}
function
getCounter
(
counters
:
{
[
type
:
number
]:
number
})
{
if
(
counters
)
{
for
(
const
type
in
counters
)
{
return
counters
[
type
];
}
}
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): opponent's visible facedown cards (confirm_cards)
return
{
code
:
card
.
code
,
location
:
cardZoneToLocation
(
card
.
location
.
zone
),
sequence
:
card
.
location
.
sequence
,
controller
:
convertController
(
card
.
location
.
controller
,
player
),
position
:
convertPosition
(
card
.
location
.
position
),
overlay_sequence
:
convertOverlaySequence
(
card
.
location
),
attribute
:
numberToAttribute
(
card
.
meta
.
data
.
attribute
??
0
),
race
:
numberToRace
(
card
.
meta
.
data
.
race
??
0
),
level
:
card
.
meta
.
data
.
level
??
0
,
counter
:
getCounter
(
card
.
counters
),
// TODO (ygo-agent): add negated
negated
:
false
,
attack
:
card
.
meta
.
data
.
atk
??
0
,
defense
:
card
.
meta
.
data
.
def
??
0
,
types
:
extraCardTypes
(
card
.
meta
.
data
.
type
??
0
).
map
(
numberToType
),
};
}
enum
Phase
{
export
enum
Phase
{
Battle
=
"
battle
"
,
BattleStart
=
"
battle_start
"
,
BattleStep
=
"
battle_step
"
,
...
...
@@ -454,35 +192,6 @@ enum Phase {
Standby
=
"
standby
"
,
}
import
_Phase
=
GM
.
MsgNewPhase
.
PhaseType
;
export
function
convertPhase
(
phase
:
_Phase
):
Phase
{
switch
(
phase
)
{
case
_Phase
.
DRAW
:
return
Phase
.
Draw
;
case
_Phase
.
STANDBY
:
return
Phase
.
Standby
;
case
_Phase
.
MAIN1
:
return
Phase
.
Main1
;
case
_Phase
.
BATTLE_START
:
return
Phase
.
BattleStart
;
case
_Phase
.
BATTLE_STEP
:
return
Phase
.
BattleStep
;
case
_Phase
.
DAMAGE_GAL
:
return
Phase
.
DamageCalculation
;
case
_Phase
.
DAMAGE
:
return
Phase
.
Damage
;
case
_Phase
.
BATTLE
:
return
Phase
.
Battle
;
case
_Phase
.
MAIN2
:
return
Phase
.
Main2
;
case
_Phase
.
END
:
return
Phase
.
End
;
default
:
throw
new
Error
(
`Unknown phase:
${
phase
}
`
);
}
}
export
interface
Global
{
/**
* Whether me is the first player
...
...
@@ -495,33 +204,12 @@ export interface Global {
turn
:
number
;
}
export
function
parsePlayerFromMsg
(
msg
:
GM
):
number
{
if
(
msg
instanceof
GM
.
MsgSelectCard
||
msg
instanceof
GM
.
MsgSelectTribute
||
msg
instanceof
GM
.
MsgSelectSum
||
msg
instanceof
GM
.
MsgSelectIdleCmd
||
msg
instanceof
GM
.
MsgSelectChain
||
msg
instanceof
GM
.
MsgSelectPosition
||
msg
instanceof
GM
.
MsgSelectEffectYn
||
msg
instanceof
GM
.
MsgSelectYesNo
||
msg
instanceof
GM
.
MsgSelectBattleCmd
||
msg
instanceof
GM
.
MsgSelectUnselectCard
||
msg
instanceof
GM
.
MsgSelectOption
||
msg
instanceof
GM
.
MsgSelectPlace
||
msg
instanceof
GM
.
MsgAnnounce
)
{
return
msg
.
player
;
}
throw
new
Error
(
`Unsupported message type:
${
msg
}
`
);
}
interface
SelectAbleCard
{
location
:
CardLocation
;
response
:
number
;
}
interface
MsgSelectCard
{
export
interface
MsgSelectCard
{
msg_type
:
"
select_card
"
;
cancelable
:
boolean
;
min
:
number
;
...
...
@@ -532,28 +220,13 @@ interface MsgSelectCard {
export
type
MultiSelectMsg
=
MsgSelectCard
|
MsgSelectSum
|
MsgSelectTribute
;
function
convertMsgSelectCard
(
msg
:
GM
.
MsgSelectCard
):
MsgSelectCard
{
// response is -1 for finish
return
{
msg_type
:
"
select_card
"
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
cards
.
map
((
c
)
=>
({
location
:
convertCardLocation
(
c
.
location
,
msg
.
player
),
response
:
c
.
response
,
})),
selected
:
[],
};
}
interface
SelectTributeCard
{
location
:
CardLocation
;
level
:
number
;
response
:
number
;
}
interface
MsgSelectTribute
{
export
interface
MsgSelectTribute
{
msg_type
:
"
select_tribute
"
;
cancelable
:
boolean
;
min
:
number
;
...
...
@@ -562,21 +235,6 @@ interface MsgSelectTribute {
selected
:
number
[];
}
function
convertMsgSelectTribute
(
msg
:
GM
.
MsgSelectTribute
):
MsgSelectTribute
{
return
{
msg_type
:
"
select_tribute
"
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
selectable_cards
.
map
((
c
)
=>
({
location
:
convertCardLocation
(
c
.
location
,
msg
.
player
),
level
:
c
.
level
,
response
:
c
.
response
,
})),
selected
:
[],
};
}
interface
SelectSumCard
{
location
:
CardLocation
;
level1
:
number
;
...
...
@@ -595,46 +253,14 @@ export interface MsgSelectSum {
selected
:
number
[];
}
function
convertMsgSelectSum
(
msg
:
GM
.
MsgSelectSum
):
MsgSelectSum
{
return
{
msg_type
:
"
select_sum
"
,
overflow
:
msg
.
overflow
!==
0
,
level_sum
:
msg
.
level_sum
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
selectable_cards
.
map
((
c
)
=>
({
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
,
})),
selected
:
[],
};
}
interface
CardInfo
{
export
interface
CardInfo
{
code
:
number
;
controller
:
Controller
;
location
:
Location
;
sequence
:
number
;
}
function
convertCardInfo
(
cardInfo
:
ygopro
.
CardInfo
,
player
:
number
):
CardInfo
{
return
{
code
:
cardInfo
.
code
,
controller
:
convertController
(
cardInfo
.
controller
,
player
),
location
:
cardZoneToLocation
(
cardInfo
.
location
),
sequence
:
cardInfo
.
sequence
,
};
}
enum
IdleCmdType
{
export
enum
IdleCmdType
{
Summon
=
"
summon
"
,
SpSummon
=
"
sp_summon
"
,
Reposition
=
"
reposition
"
,
...
...
@@ -645,244 +271,83 @@ enum IdleCmdType {
ToEp
=
"
to_ep
"
,
}
import
_IdleType
=
GM
.
MsgSelectIdleCmd
.
IdleCmd
.
IdleType
;
function
convertIdleCmdType
(
cmdType
:
_IdleType
):
IdleCmdType
{
switch
(
cmdType
)
{
case
_IdleType
.
ACTIVATE
:
return
IdleCmdType
.
Activate
;
case
_IdleType
.
MSET
:
return
IdleCmdType
.
Mset
;
case
_IdleType
.
POS_CHANGE
:
return
IdleCmdType
.
Reposition
;
case
_IdleType
.
SSET
:
return
IdleCmdType
.
Set
;
case
_IdleType
.
SPSUMMON
:
return
IdleCmdType
.
SpSummon
;
case
_IdleType
.
SUMMON
:
return
IdleCmdType
.
Summon
;
case
_IdleType
.
TO_BP
:
return
IdleCmdType
.
ToBp
;
case
_IdleType
.
TO_EP
:
return
IdleCmdType
.
ToEp
;
default
:
throw
new
Error
(
`Unknown idle command type:
${
cmdType
}
`
);
}
}
interface
IdleCmdData
{
card_info
:
CardInfo
;
effect_description
:
number
;
response
:
number
;
}
interface
IdleCmd
{
export
interface
IdleCmd
{
cmd_type
:
IdleCmdType
;
data
?:
IdleCmdData
;
}
interface
MsgSelectIdleCmd
{
export
interface
MsgSelectIdleCmd
{
msg_type
:
"
select_idlecmd
"
;
idle_cmds
:
IdleCmd
[];
}
function
convertMsgSelectIdleCmd
(
msg
:
GM
.
MsgSelectIdleCmd
):
MsgSelectIdleCmd
{
const
idle_cmds
:
IdleCmd
[]
=
[];
for
(
const
cmd
of
msg
.
idle_cmds
)
{
for
(
const
data
of
cmd
.
idle_datas
)
{
const
cmd_type
=
convertIdleCmdType
(
cmd
.
idle_type
);
if
(
cmd_type
===
IdleCmdType
.
Summon
||
cmd_type
===
IdleCmdType
.
SpSummon
||
cmd_type
===
IdleCmdType
.
Reposition
||
cmd_type
===
IdleCmdType
.
Mset
||
cmd_type
===
IdleCmdType
.
Set
||
cmd_type
===
IdleCmdType
.
Activate
)
{
idle_cmds
.
push
({
cmd_type
,
data
:
{
card_info
:
convertCardInfo
(
data
.
card_info
,
msg
.
player
),
effect_description
:
cmd_type
===
IdleCmdType
.
Activate
?
data
.
effect_description
:
0
,
response
:
data
.
response
,
},
});
}
else
{
throw
new
Error
(
`Unsupported idle command type:
${
cmd_type
}
`
);
}
}
}
if
(
msg
.
enable_bp
)
{
// response will be 6
idle_cmds
.
push
({
cmd_type
:
IdleCmdType
.
ToBp
});
}
// TODO (ygo-agent): new models will support it
if
(
msg
.
enable_ep
&&
!
msg
.
enable_bp
)
{
// response will be 7
idle_cmds
.
push
({
cmd_type
:
IdleCmdType
.
ToEp
});
}
return
{
msg_type
:
"
select_idlecmd
"
,
idle_cmds
:
idle_cmds
,
};
}
interface
Chain
{
export
interface
Chain
{
code
:
number
;
location
:
CardLocation
;
effect_description
:
number
;
response
:
number
;
}
interface
MsgSelectChain
{
export
interface
MsgSelectChain
{
msg_type
:
"
select_chain
"
;
forced
:
boolean
;
chains
:
Chain
[];
}
function
convertChain
(
chain
:
GM
.
MsgSelectChain
.
Chain
,
player
:
number
):
Chain
{
return
{
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
{
msg_type
:
"
select_chain
"
,
forced
:
msg
.
forced
,
chains
:
msg
.
chains
.
map
((
c
)
=>
convertChain
(
c
,
msg
.
player
)),
};
}
interface
MsgSelectPosition
{
export
interface
MsgSelectPosition
{
msg_type
:
"
select_position
"
;
code
:
number
;
positions
:
Position
[];
}
function
convertMsgSelectPosition
(
msg
:
GM
.
MsgSelectPosition
,
):
MsgSelectPosition
{
return
{
msg_type
:
"
select_position
"
,
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
)),
};
}
interface
MsgSelectYesNo
{
export
interface
MsgSelectYesNo
{
msg_type
:
"
select_yesno
"
;
effect_description
:
number
;
}
function
convertMsgSelectYesNo
(
msg
:
GM
.
MsgSelectYesNo
):
MsgSelectYesNo
{
// response is 1 for yes and 0 for no
return
{
msg_type
:
"
select_yesno
"
,
effect_description
:
msg
.
effect_description
,
};
}
interface
MsgSelectEffectYn
{
export
interface
MsgSelectEffectYn
{
msg_type
:
"
select_effectyn
"
;
code
:
number
;
location
:
CardLocation
;
effect_description
:
number
;
}
function
convertMsgSelectEffectYn
(
msg
:
GM
.
MsgSelectEffectYn
,
):
MsgSelectEffectYn
{
// response is 1 for yes and 0 for no
return
{
msg_type
:
"
select_effectyn
"
,
code
:
msg
.
code
,
location
:
convertCardLocation
(
msg
.
location
,
msg
.
player
),
effect_description
:
msg
.
effect_description
,
};
}
enum
BattleCmdType
{
export
enum
BattleCmdType
{
Attack
=
"
attack
"
,
Activate
=
"
activate
"
,
ToM2
=
"
to_m2
"
,
ToEp
=
"
to_ep
"
,
}
import
_BattleCmdType
=
GM
.
MsgSelectBattleCmd
.
BattleCmd
.
BattleType
;
function
convertBattleCmdType
(
cmdType
:
_BattleCmdType
):
BattleCmdType
{
switch
(
cmdType
)
{
case
_BattleCmdType
.
ATTACK
:
return
BattleCmdType
.
Attack
;
case
_BattleCmdType
.
ACTIVATE
:
return
BattleCmdType
.
Activate
;
default
:
throw
new
Error
(
`Unknown battle command type:
${
cmdType
}
`
);
}
}
interface
BattleCmdData
{
export
interface
BattleCmdData
{
card_info
:
CardInfo
;
effect_description
:
number
;
direct_attackable
:
boolean
;
response
:
number
;
}
interface
BattleCmd
{
export
interface
BattleCmd
{
cmd_type
:
BattleCmdType
;
data
?:
BattleCmdData
;
}
interface
MsgSelectBattleCmd
{
export
interface
MsgSelectBattleCmd
{
msg_type
:
"
select_battlecmd
"
;
battle_cmds
:
BattleCmd
[];
}
function
convertMsgSelectBattleCmd
(
msg
:
GM
.
MsgSelectBattleCmd
,
):
MsgSelectBattleCmd
{
const
battle_cmds
:
BattleCmd
[]
=
[];
for
(
const
cmd
of
msg
.
battle_cmds
)
{
const
cmd_type
=
convertBattleCmdType
(
cmd
.
battle_type
);
for
(
const
data
of
cmd
.
battle_datas
)
{
const
battle_data
:
BattleCmdData
=
{
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
});
}
// TODO (ygo-agent): new models will support it
if
(
msg
.
enable_ep
&&
!
msg
.
enable_m2
)
{
// response will be 3
battle_cmds
.
push
({
cmd_type
:
BattleCmdType
.
ToEp
});
}
return
{
msg_type
:
"
select_battlecmd
"
,
battle_cmds
,
};
}
interface
SelectUnselectCard
{
export
interface
SelectUnselectCard
{
location
:
CardLocation
;
response
:
number
;
}
interface
MsgSelectUnselectCard
{
export
interface
MsgSelectUnselectCard
{
msg_type
:
"
select_unselect_card
"
;
finishable
:
boolean
;
cancelable
:
boolean
;
...
...
@@ -892,118 +357,50 @@ interface MsgSelectUnselectCard {
selectable_cards
:
SelectUnselectCard
[];
}
function
convertMsgSelectUnselectCard
(
msg
:
GM
.
MsgSelectUnselectCard
,
):
MsgSelectUnselectCard
{
return
{
msg_type
:
"
select_unselect_card
"
,
// response is -1 for finish
finishable
:
msg
.
finishable
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
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
{
export
interface
MsgSelectOption
{
msg_type
:
"
select_option
"
;
options
:
Option
[];
}
function
convertMsgSelectOption
(
msg
:
GM
.
MsgSelectOption
):
MsgSelectOption
{
return
{
msg_type
:
"
select_option
"
,
options
:
msg
.
options
.
map
((
o
)
=>
({
code
:
o
.
code
,
response
:
o
.
response
,
})),
};
}
interface
Place
{
controller
:
Controller
;
location
:
Location
;
sequence
:
number
;
}
interface
MsgSelectPlace
{
export
interface
MsgSelectPlace
{
msg_type
:
"
select_place
"
;
count
:
number
;
places
:
Place
[];
}
// TODO (ygo-agent): SelectDisfield is different from SelectPlace
function
convertMsgSelectPlace
(
msg
:
GM
.
MsgSelectPlace
):
MsgSelectPlace
{
return
{
msg_type
:
"
select_place
"
,
count
:
msg
.
count
,
places
:
msg
.
places
.
map
((
p
)
=>
({
// NOTICE: the response is the index of the place in the places array
controller
:
convertController
(
p
.
controller
,
msg
.
player
),
location
:
cardZoneToLocation
(
p
.
zone
),
sequence
:
p
.
sequence
,
})),
};
}
interface
AnnounceAttrib
{
attribute
:
Attribute
;
response
:
number
;
}
interface
MsgAnnounceAttrib
{
export
interface
MsgAnnounceAttrib
{
msg_type
:
"
announce_attrib
"
;
count
:
number
;
attributes
:
AnnounceAttrib
[];
}
function
convertMsgAnnounceAttrib
(
msg
:
GM
.
MsgAnnounce
):
MsgAnnounceAttrib
{
return
{
msg_type
:
"
announce_attrib
"
,
count
:
msg
.
min
,
// from api/ocgcore/ocgAdapter/stoc/stocGameMsg/announceAttrib.ts
attributes
:
msg
.
options
.
map
((
a
)
=>
({
attribute
:
numberToAttribute
(
1
<<
a
.
code
),
response
:
a
.
response
,
})),
};
}
interface
AnnounceNumber
{
number
:
number
;
response
:
number
;
}
interface
MsgAnnounceNumber
{
export
interface
MsgAnnounceNumber
{
msg_type
:
"
announce_number
"
;
count
:
number
;
numbers
:
AnnounceNumber
[];
}
function
convertMsgAnnounceNumber
(
msg
:
GM
.
MsgAnnounce
):
MsgAnnounceNumber
{
return
{
msg_type
:
"
announce_number
"
,
count
:
msg
.
min
,
numbers
:
msg
.
options
.
map
((
o
)
=>
({
number
:
o
.
code
,
response
:
o
.
response
,
})),
};
}
type
ActionMsgData
=
|
MsgSelectCard
|
MsgSelectTribute
...
...
@@ -1020,76 +417,10 @@ type ActionMsgData =
|
MsgAnnounceAttrib
|
MsgAnnounceNumber
;
interface
ActionMsg
{
export
interface
ActionMsg
{
data
:
ActionMsgData
;
}
export
function
convertActionMsg
(
msg
:
ygopro
.
StocGameMessage
):
ActionMsg
{
if
(
msg
instanceof
GM
.
MsgSelectCard
)
{
return
{
data
:
convertMsgSelectCard
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectTribute
)
{
return
{
data
:
convertMsgSelectTribute
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectSum
)
{
return
{
data
:
convertMsgSelectSum
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectIdleCmd
)
{
return
{
data
:
convertMsgSelectIdleCmd
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectChain
)
{
return
{
data
:
convertMsgSelectChain
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectPosition
)
{
return
{
data
:
convertMsgSelectPosition
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectEffectYn
)
{
return
{
data
:
convertMsgSelectEffectYn
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectYesNo
)
{
return
{
data
:
convertMsgSelectYesNo
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectBattleCmd
)
{
return
{
data
:
convertMsgSelectBattleCmd
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectUnselectCard
)
{
return
{
data
:
convertMsgSelectUnselectCard
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectOption
)
{
return
{
data
:
convertMsgSelectOption
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectPlace
)
{
return
{
data
:
convertMsgSelectPlace
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgAnnounce
)
{
if
(
msg
.
announce_type
===
GM
.
MsgAnnounce
.
AnnounceType
.
Attribute
)
{
return
{
data
:
convertMsgAnnounceAttrib
(
msg
),
};
}
else
if
(
msg
.
announce_type
===
GM
.
MsgAnnounce
.
AnnounceType
.
Number
)
{
return
{
data
:
convertMsgAnnounceNumber
(
msg
),
};
}
else
{
throw
new
Error
(
`Unsupported announce type:
${
msg
.
announce_type
}
`
);
}
}
else
{
throw
new
Error
(
`Unsupported message type:
${
msg
}
`
);
}
}
export
interface
Input
{
action_msg
:
ActionMsg
;
cards
:
Card
[];
...
...
src/api/ygoAgent/transaction.ts
0 → 100644
View file @
5e3a7019
import
{
CardMeta
,
ygopro
}
from
"
@/api
"
;
import
{
ATTRIBUTE_DARK
,
ATTRIBUTE_DEVINE
,
ATTRIBUTE_EARTH
,
ATTRIBUTE_FIRE
,
ATTRIBUTE_LIGHT
,
ATTRIBUTE_WATER
,
ATTRIBUTE_WIND
,
FACEDOWN_ATTACK
,
FACEDOWN_DEFENSE
,
FACEUP_ATTACK
,
FACEUP_DEFENSE
,
RACE_AQUA
,
RACE_BEAST
,
RACE_BEASTWARRIOR
,
RACE_CREATORGOD
,
RACE_CYBERSE
,
RACE_DEVINE
,
RACE_DINOSAUR
,
RACE_DRAGON
,
RACE_FAIRY
,
RACE_FIEND
,
RACE_FISH
,
RACE_INSECT
,
RACE_MACHINE
,
RACE_PLANT
,
RACE_PSYCHO
,
RACE_PYRO
,
RACE_REPTILE
,
RACE_ROCK
,
RACE_SEASERPENT
,
RACE_SPELLCASTER
,
RACE_THUNDER
,
RACE_WARRIOR
,
RACE_WINDBEAST
,
RACE_WYRM
,
RACE_ZOMBIE
,
TYPE_CONTINUOUS
,
TYPE_COUNTER
,
TYPE_DUAL
,
TYPE_EFFECT
,
TYPE_EQUIP
,
TYPE_FIELD
,
TYPE_FLIP
,
TYPE_FUSION
,
TYPE_LINK
,
TYPE_MONSTER
,
TYPE_NORMAL
,
TYPE_PENDULUM
,
TYPE_QUICKPLAY
,
TYPE_RITUAL
,
TYPE_SPELL
,
TYPE_SPIRIT
,
TYPE_SPSUMMON
,
TYPE_SYNCHRO
,
TYPE_TOKEN
,
TYPE_TOON
,
TYPE_TRAP
,
TYPE_TRAPMONSTER
,
TYPE_TUNER
,
TYPE_UNION
,
TYPE_XYZ
,
}
from
"
@/common
"
;
import
{
extraCardTypes
}
from
"
@/common
"
;
import
{
CardType
}
from
"
@/stores/cardStore
"
;
import
{
ActionMsg
,
Attribute
,
BattleCmd
,
BattleCmdData
,
BattleCmdType
,
Card
,
CardInfo
,
CardLocation
,
Chain
,
Controller
,
IdleCmd
,
IdleCmdType
,
Location
,
MsgAnnounceAttrib
,
MsgAnnounceNumber
,
MsgSelectBattleCmd
,
MsgSelectCard
,
MsgSelectChain
,
MsgSelectEffectYn
,
MsgSelectIdleCmd
,
MsgSelectOption
,
MsgSelectPlace
,
MsgSelectPosition
,
MsgSelectSum
,
MsgSelectTribute
,
MsgSelectUnselectCard
,
MsgSelectYesNo
,
Phase
,
Position
,
Race
,
Type
,
}
from
"
./schema
"
;
import
GM
=
ygopro
.
StocGameMessage
;
import
_Phase
=
GM
.
MsgNewPhase
.
PhaseType
;
import
_IdleType
=
GM
.
MsgSelectIdleCmd
.
IdleCmd
.
IdleType
;
import
_BattleCmdType
=
GM
.
MsgSelectBattleCmd
.
BattleCmd
.
BattleType
;
// from common.ts ATTRIBUTE_*
function
numberToAttribute
(
attributeNumber
:
number
):
Attribute
{
switch
(
attributeNumber
)
{
case
0x00
:
return
Attribute
.
None
;
case
ATTRIBUTE_EARTH
:
return
Attribute
.
Earth
;
case
ATTRIBUTE_WATER
:
return
Attribute
.
Water
;
case
ATTRIBUTE_FIRE
:
return
Attribute
.
Fire
;
case
ATTRIBUTE_WIND
:
return
Attribute
.
Wind
;
case
ATTRIBUTE_LIGHT
:
return
Attribute
.
Light
;
case
ATTRIBUTE_DARK
:
return
Attribute
.
Dark
;
case
ATTRIBUTE_DEVINE
:
return
Attribute
.
Divine
;
default
:
throw
new
Error
(
`Unknown attribute number:
${
attributeNumber
}
`
);
}
}
function
cardZoneToLocation
(
zone
:
ygopro
.
CardZone
):
Location
{
switch
(
zone
)
{
case
ygopro
.
CardZone
.
DECK
:
return
Location
.
Deck
;
case
ygopro
.
CardZone
.
HAND
:
return
Location
.
Hand
;
case
ygopro
.
CardZone
.
MZONE
:
return
Location
.
MZone
;
case
ygopro
.
CardZone
.
SZONE
:
return
Location
.
SZone
;
case
ygopro
.
CardZone
.
GRAVE
:
return
Location
.
Grave
;
case
ygopro
.
CardZone
.
REMOVED
:
return
Location
.
Removed
;
case
ygopro
.
CardZone
.
EXTRA
:
return
Location
.
Extra
;
default
:
throw
new
Error
(
`Unknown card zone:
${
zone
}
`
);
}
}
function
convertController
(
controller
:
number
,
player
:
number
):
Controller
{
return
controller
===
player
?
Controller
.
Me
:
Controller
.
Opponent
;
}
function
convertOverlaySequence
(
cl
:
ygopro
.
CardLocation
):
number
{
return
cl
.
is_overlay
?
cl
.
overlay_sequence
:
-
1
;
}
function
convertCardLocation
(
cl
:
ygopro
.
CardLocation
,
player
:
number
,
):
CardLocation
{
return
{
controller
:
convertController
(
cl
.
controller
,
player
),
location
:
cardZoneToLocation
(
cl
.
zone
),
overlay_sequence
:
convertOverlaySequence
(
cl
),
sequence
:
cl
.
sequence
,
};
}
function
convertPosition
(
position
:
ygopro
.
CardPosition
):
Position
{
switch
(
position
)
{
case
ygopro
.
CardPosition
.
FACEUP_ATTACK
:
return
Position
.
FaceupAttack
;
case
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
:
return
Position
.
FacedownAttack
;
case
ygopro
.
CardPosition
.
FACEUP_DEFENSE
:
return
Position
.
FaceupDefense
;
case
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
:
return
Position
.
FacedownDefense
;
case
ygopro
.
CardPosition
.
FACEUP
:
return
Position
.
Faceup
;
case
ygopro
.
CardPosition
.
FACEDOWN
:
return
Position
.
Facedown
;
case
ygopro
.
CardPosition
.
ATTACK
:
return
Position
.
Attack
;
case
ygopro
.
CardPosition
.
DEFENSE
:
return
Position
.
Defense
;
default
:
throw
new
Error
(
`Unknown card position:
${
position
}
`
);
}
}
function
numberToRace
(
raceNumber
:
number
):
Race
{
switch
(
raceNumber
)
{
case
0x0
:
return
Race
.
None
;
case
RACE_WARRIOR
:
return
Race
.
Warrior
;
case
RACE_SPELLCASTER
:
return
Race
.
Spellcaster
;
case
RACE_FAIRY
:
return
Race
.
Fairy
;
case
RACE_FIEND
:
return
Race
.
Fiend
;
case
RACE_ZOMBIE
:
return
Race
.
Zombie
;
case
RACE_MACHINE
:
return
Race
.
Machine
;
case
RACE_AQUA
:
return
Race
.
Aqua
;
case
RACE_PYRO
:
return
Race
.
Pyro
;
case
RACE_ROCK
:
return
Race
.
Rock
;
case
RACE_WINDBEAST
:
return
Race
.
Windbeast
;
case
RACE_PLANT
:
return
Race
.
Plant
;
case
RACE_INSECT
:
return
Race
.
Insect
;
case
RACE_THUNDER
:
return
Race
.
Thunder
;
case
RACE_DRAGON
:
return
Race
.
Dragon
;
case
RACE_BEAST
:
return
Race
.
Beast
;
case
RACE_BEASTWARRIOR
:
return
Race
.
BeastWarrior
;
case
RACE_DINOSAUR
:
return
Race
.
Dinosaur
;
case
RACE_FISH
:
return
Race
.
Fish
;
case
RACE_SEASERPENT
:
return
Race
.
SeaSerpent
;
case
RACE_REPTILE
:
return
Race
.
Reptile
;
case
RACE_PSYCHO
:
return
Race
.
Psycho
;
case
RACE_DEVINE
:
return
Race
.
Devine
;
case
RACE_CREATORGOD
:
return
Race
.
CreatorGod
;
case
RACE_WYRM
:
return
Race
.
Wyrm
;
case
RACE_CYBERSE
:
return
Race
.
Cyberse
;
default
:
throw
new
Error
(
`Unknown race number:
${
raceNumber
}
`
);
}
}
function
numberToType
(
typeNumber
:
number
):
Type
{
switch
(
typeNumber
)
{
case
TYPE_MONSTER
:
return
Type
.
Monster
;
case
TYPE_SPELL
:
return
Type
.
Spell
;
case
TYPE_TRAP
:
return
Type
.
Trap
;
case
TYPE_NORMAL
:
return
Type
.
Normal
;
case
TYPE_EFFECT
:
return
Type
.
Effect
;
case
TYPE_FUSION
:
return
Type
.
Fusion
;
case
TYPE_RITUAL
:
return
Type
.
Ritual
;
case
TYPE_TRAPMONSTER
:
return
Type
.
TrapMonster
;
case
TYPE_SPIRIT
:
return
Type
.
Spirit
;
case
TYPE_UNION
:
return
Type
.
Union
;
case
TYPE_DUAL
:
return
Type
.
Dual
;
case
TYPE_TUNER
:
return
Type
.
Tuner
;
case
TYPE_SYNCHRO
:
return
Type
.
Synchro
;
case
TYPE_TOKEN
:
return
Type
.
Token
;
case
TYPE_QUICKPLAY
:
return
Type
.
QuickPlay
;
case
TYPE_CONTINUOUS
:
return
Type
.
Continuous
;
case
TYPE_EQUIP
:
return
Type
.
Equip
;
case
TYPE_FIELD
:
return
Type
.
Field
;
case
TYPE_COUNTER
:
return
Type
.
Counter
;
case
TYPE_FLIP
:
return
Type
.
Flip
;
case
TYPE_TOON
:
return
Type
.
Toon
;
case
TYPE_XYZ
:
return
Type
.
Xyz
;
case
TYPE_PENDULUM
:
return
Type
.
Pendulum
;
case
TYPE_SPSUMMON
:
return
Type
.
Special
;
case
TYPE_LINK
:
return
Type
.
Link
;
default
:
throw
new
Error
(
`Unknown type number:
${
typeNumber
}
`
);
}
}
function
getCounter
(
counters
:
{
[
type
:
number
]:
number
})
{
if
(
counters
)
{
for
(
const
type
in
counters
)
{
return
counters
[
type
];
}
}
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): opponent's visible facedown cards (confirm_cards)
const
{
code
,
location
,
meta
,
counters
}
=
card
;
return
{
code
,
location
:
cardZoneToLocation
(
location
.
zone
),
sequence
:
location
.
sequence
,
controller
:
convertController
(
location
.
controller
,
player
),
position
:
convertPosition
(
location
.
position
),
overlay_sequence
:
convertOverlaySequence
(
location
),
attribute
:
numberToAttribute
(
meta
.
data
.
attribute
??
0
),
race
:
numberToRace
(
meta
.
data
.
race
??
0
),
level
:
meta
.
data
.
level
??
0
,
counter
:
getCounter
(
counters
),
// TODO (ygo-agent): add negated
negated
:
false
,
attack
:
meta
.
data
.
atk
??
0
,
defense
:
meta
.
data
.
def
??
0
,
types
:
extraCardTypes
(
meta
.
data
.
type
??
0
).
map
(
numberToType
),
};
}
export
function
convertPhase
(
phase
:
_Phase
):
Phase
{
switch
(
phase
)
{
case
_Phase
.
DRAW
:
return
Phase
.
Draw
;
case
_Phase
.
STANDBY
:
return
Phase
.
Standby
;
case
_Phase
.
MAIN1
:
return
Phase
.
Main1
;
case
_Phase
.
BATTLE_START
:
return
Phase
.
BattleStart
;
case
_Phase
.
BATTLE_STEP
:
return
Phase
.
BattleStep
;
case
_Phase
.
DAMAGE_GAL
:
return
Phase
.
DamageCalculation
;
case
_Phase
.
DAMAGE
:
return
Phase
.
Damage
;
case
_Phase
.
BATTLE
:
return
Phase
.
Battle
;
case
_Phase
.
MAIN2
:
return
Phase
.
Main2
;
case
_Phase
.
END
:
return
Phase
.
End
;
default
:
throw
new
Error
(
`Unknown phase:
${
phase
}
`
);
}
}
export
function
parsePlayerFromMsg
(
msg
:
GM
):
number
{
if
(
msg
instanceof
GM
.
MsgSelectCard
||
msg
instanceof
GM
.
MsgSelectTribute
||
msg
instanceof
GM
.
MsgSelectSum
||
msg
instanceof
GM
.
MsgSelectIdleCmd
||
msg
instanceof
GM
.
MsgSelectChain
||
msg
instanceof
GM
.
MsgSelectPosition
||
msg
instanceof
GM
.
MsgSelectEffectYn
||
msg
instanceof
GM
.
MsgSelectYesNo
||
msg
instanceof
GM
.
MsgSelectBattleCmd
||
msg
instanceof
GM
.
MsgSelectUnselectCard
||
msg
instanceof
GM
.
MsgSelectOption
||
msg
instanceof
GM
.
MsgSelectPlace
||
msg
instanceof
GM
.
MsgAnnounce
)
{
return
msg
.
player
;
}
throw
new
Error
(
`Unsupported message type:
${
msg
}
`
);
}
function
convertMsgSelectCard
(
msg
:
GM
.
MsgSelectCard
):
MsgSelectCard
{
// response is -1 for finish
return
{
msg_type
:
"
select_card
"
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
cards
.
map
((
c
)
=>
({
location
:
convertCardLocation
(
c
.
location
,
msg
.
player
),
response
:
c
.
response
,
})),
selected
:
[],
};
}
function
convertMsgSelectTribute
(
msg
:
GM
.
MsgSelectTribute
):
MsgSelectTribute
{
return
{
msg_type
:
"
select_tribute
"
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
selectable_cards
.
map
((
c
)
=>
({
location
:
convertCardLocation
(
c
.
location
,
msg
.
player
),
level
:
c
.
level
,
response
:
c
.
response
,
})),
selected
:
[],
};
}
function
convertMsgSelectSum
(
msg
:
GM
.
MsgSelectSum
):
MsgSelectSum
{
return
{
msg_type
:
"
select_sum
"
,
overflow
:
msg
.
overflow
!==
0
,
level_sum
:
msg
.
level_sum
,
min
:
msg
.
min
,
max
:
msg
.
max
,
cards
:
msg
.
selectable_cards
.
map
((
c
)
=>
({
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
,
})),
selected
:
[],
};
}
function
convertCardInfo
(
cardInfo
:
ygopro
.
CardInfo
,
player
:
number
):
CardInfo
{
return
{
code
:
cardInfo
.
code
,
controller
:
convertController
(
cardInfo
.
controller
,
player
),
location
:
cardZoneToLocation
(
cardInfo
.
location
),
sequence
:
cardInfo
.
sequence
,
};
}
function
convertIdleCmdType
(
cmdType
:
_IdleType
):
IdleCmdType
{
switch
(
cmdType
)
{
case
_IdleType
.
ACTIVATE
:
return
IdleCmdType
.
Activate
;
case
_IdleType
.
MSET
:
return
IdleCmdType
.
Mset
;
case
_IdleType
.
POS_CHANGE
:
return
IdleCmdType
.
Reposition
;
case
_IdleType
.
SSET
:
return
IdleCmdType
.
Set
;
case
_IdleType
.
SPSUMMON
:
return
IdleCmdType
.
SpSummon
;
case
_IdleType
.
SUMMON
:
return
IdleCmdType
.
Summon
;
case
_IdleType
.
TO_BP
:
return
IdleCmdType
.
ToBp
;
case
_IdleType
.
TO_EP
:
return
IdleCmdType
.
ToEp
;
default
:
throw
new
Error
(
`Unknown idle command type:
${
cmdType
}
`
);
}
}
function
convertMsgSelectIdleCmd
(
msg
:
GM
.
MsgSelectIdleCmd
):
MsgSelectIdleCmd
{
const
idle_cmds
:
IdleCmd
[]
=
[];
for
(
const
cmd
of
msg
.
idle_cmds
)
{
for
(
const
data
of
cmd
.
idle_datas
)
{
const
cmd_type
=
convertIdleCmdType
(
cmd
.
idle_type
);
if
(
cmd_type
===
IdleCmdType
.
Summon
||
cmd_type
===
IdleCmdType
.
SpSummon
||
cmd_type
===
IdleCmdType
.
Reposition
||
cmd_type
===
IdleCmdType
.
Mset
||
cmd_type
===
IdleCmdType
.
Set
||
cmd_type
===
IdleCmdType
.
Activate
)
{
idle_cmds
.
push
({
cmd_type
,
data
:
{
card_info
:
convertCardInfo
(
data
.
card_info
,
msg
.
player
),
effect_description
:
cmd_type
===
IdleCmdType
.
Activate
?
data
.
effect_description
:
0
,
response
:
data
.
response
,
},
});
}
else
{
throw
new
Error
(
`Unsupported idle command type:
${
cmd_type
}
`
);
}
}
}
if
(
msg
.
enable_bp
)
{
// response will be 6
idle_cmds
.
push
({
cmd_type
:
IdleCmdType
.
ToBp
});
}
// TODO (ygo-agent): new models will support it
if
(
msg
.
enable_ep
&&
!
msg
.
enable_bp
)
{
// response will be 7
idle_cmds
.
push
({
cmd_type
:
IdleCmdType
.
ToEp
});
}
return
{
msg_type
:
"
select_idlecmd
"
,
idle_cmds
:
idle_cmds
,
};
}
function
convertChain
(
chain
:
GM
.
MsgSelectChain
.
Chain
,
player
:
number
):
Chain
{
return
{
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
{
msg_type
:
"
select_chain
"
,
forced
:
msg
.
forced
,
chains
:
msg
.
chains
.
map
((
c
)
=>
convertChain
(
c
,
msg
.
player
)),
};
}
function
convertMsgSelectPosition
(
msg
:
GM
.
MsgSelectPosition
,
):
MsgSelectPosition
{
return
{
msg_type
:
"
select_position
"
,
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
)),
};
}
function
convertMsgSelectYesNo
(
msg
:
GM
.
MsgSelectYesNo
):
MsgSelectYesNo
{
// response is 1 for yes and 0 for no
return
{
msg_type
:
"
select_yesno
"
,
effect_description
:
msg
.
effect_description
,
};
}
function
convertMsgSelectEffectYn
(
msg
:
GM
.
MsgSelectEffectYn
,
):
MsgSelectEffectYn
{
// response is 1 for yes and 0 for no
return
{
msg_type
:
"
select_effectyn
"
,
code
:
msg
.
code
,
location
:
convertCardLocation
(
msg
.
location
,
msg
.
player
),
effect_description
:
msg
.
effect_description
,
};
}
function
convertBattleCmdType
(
cmdType
:
_BattleCmdType
):
BattleCmdType
{
switch
(
cmdType
)
{
case
_BattleCmdType
.
ATTACK
:
return
BattleCmdType
.
Attack
;
case
_BattleCmdType
.
ACTIVATE
:
return
BattleCmdType
.
Activate
;
default
:
throw
new
Error
(
`Unknown battle command type:
${
cmdType
}
`
);
}
}
function
convertMsgSelectBattleCmd
(
msg
:
GM
.
MsgSelectBattleCmd
,
):
MsgSelectBattleCmd
{
const
battle_cmds
:
BattleCmd
[]
=
[];
for
(
const
cmd
of
msg
.
battle_cmds
)
{
const
cmd_type
=
convertBattleCmdType
(
cmd
.
battle_type
);
for
(
const
data
of
cmd
.
battle_datas
)
{
const
battle_data
:
BattleCmdData
=
{
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
});
}
// TODO (ygo-agent): new models will support it
if
(
msg
.
enable_ep
&&
!
msg
.
enable_m2
)
{
// response will be 3
battle_cmds
.
push
({
cmd_type
:
BattleCmdType
.
ToEp
});
}
return
{
msg_type
:
"
select_battlecmd
"
,
battle_cmds
,
};
}
function
convertMsgSelectUnselectCard
(
msg
:
GM
.
MsgSelectUnselectCard
,
):
MsgSelectUnselectCard
{
return
{
msg_type
:
"
select_unselect_card
"
,
// response is -1 for finish
finishable
:
msg
.
finishable
,
cancelable
:
msg
.
cancelable
,
min
:
msg
.
min
,
max
:
msg
.
max
,
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
,
})),
};
}
function
convertMsgSelectOption
(
msg
:
GM
.
MsgSelectOption
):
MsgSelectOption
{
return
{
msg_type
:
"
select_option
"
,
options
:
msg
.
options
.
map
((
o
)
=>
({
code
:
o
.
code
,
response
:
o
.
response
,
})),
};
}
// TODO (ygo-agent): SelectDisfield is different from SelectPlace
function
convertMsgSelectPlace
(
msg
:
GM
.
MsgSelectPlace
):
MsgSelectPlace
{
return
{
msg_type
:
"
select_place
"
,
count
:
msg
.
count
,
places
:
msg
.
places
.
map
((
p
)
=>
({
// NOTICE: the response is the index of the place in the places array
controller
:
convertController
(
p
.
controller
,
msg
.
player
),
location
:
cardZoneToLocation
(
p
.
zone
),
sequence
:
p
.
sequence
,
})),
};
}
function
convertMsgAnnounceAttrib
(
msg
:
GM
.
MsgAnnounce
):
MsgAnnounceAttrib
{
return
{
msg_type
:
"
announce_attrib
"
,
count
:
msg
.
min
,
// from api/ocgcore/ocgAdapter/stoc/stocGameMsg/announceAttrib.ts
attributes
:
msg
.
options
.
map
((
a
)
=>
({
attribute
:
numberToAttribute
(
1
<<
a
.
code
),
response
:
a
.
response
,
})),
};
}
function
convertMsgAnnounceNumber
(
msg
:
GM
.
MsgAnnounce
):
MsgAnnounceNumber
{
return
{
msg_type
:
"
announce_number
"
,
count
:
msg
.
min
,
numbers
:
msg
.
options
.
map
((
o
)
=>
({
number
:
o
.
code
,
response
:
o
.
response
,
})),
};
}
export
function
convertActionMsg
(
msg
:
ygopro
.
StocGameMessage
):
ActionMsg
{
if
(
msg
instanceof
GM
.
MsgSelectCard
)
{
return
{
data
:
convertMsgSelectCard
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectTribute
)
{
return
{
data
:
convertMsgSelectTribute
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectSum
)
{
return
{
data
:
convertMsgSelectSum
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectIdleCmd
)
{
return
{
data
:
convertMsgSelectIdleCmd
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectChain
)
{
return
{
data
:
convertMsgSelectChain
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectPosition
)
{
return
{
data
:
convertMsgSelectPosition
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectEffectYn
)
{
return
{
data
:
convertMsgSelectEffectYn
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectYesNo
)
{
return
{
data
:
convertMsgSelectYesNo
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectBattleCmd
)
{
return
{
data
:
convertMsgSelectBattleCmd
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectUnselectCard
)
{
return
{
data
:
convertMsgSelectUnselectCard
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectOption
)
{
return
{
data
:
convertMsgSelectOption
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgSelectPlace
)
{
return
{
data
:
convertMsgSelectPlace
(
msg
),
};
}
else
if
(
msg
instanceof
GM
.
MsgAnnounce
)
{
if
(
msg
.
announce_type
===
GM
.
MsgAnnounce
.
AnnounceType
.
Attribute
)
{
return
{
data
:
convertMsgAnnounceAttrib
(
msg
),
};
}
else
if
(
msg
.
announce_type
===
GM
.
MsgAnnounce
.
AnnounceType
.
Number
)
{
return
{
data
:
convertMsgAnnounceNumber
(
msg
),
};
}
else
{
throw
new
Error
(
`Unsupported announce type:
${
msg
.
announce_type
}
`
);
}
}
else
{
throw
new
Error
(
`Unsupported message type:
${
msg
}
`
);
}
}
export
function
convertPositionResponse
(
response
:
number
):
ygopro
.
CardPosition
{
switch
(
response
)
{
case
FACEUP_ATTACK
:
return
ygopro
.
CardPosition
.
FACEUP_ATTACK
;
case
FACEDOWN_ATTACK
:
return
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
;
case
FACEUP_DEFENSE
:
return
ygopro
.
CardPosition
.
FACEUP_DEFENSE
;
case
FACEDOWN_DEFENSE
:
return
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
;
default
:
throw
new
Error
(
`Invalid position response:
${
response
}
`
);
}
}
src/common.ts
View file @
5e3a7019
...
...
@@ -3,31 +3,38 @@ import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType;
import
{
CardMeta
}
from
"
@/api
"
;
//! 一些Neos中基础的数据结构
// Position
export
const
FACEUP_ATTACK
=
0x1
;
export
const
FACEDOWN_ATTACK
=
0x2
;
export
const
FACEUP_DEFENSE
=
0x4
;
export
const
FACEDOWN_DEFENSE
=
0x8
;
// 类型
const
TYPE_MONSTER
=
0x1
;
//
const
TYPE_SPELL
=
0x2
;
//
const
TYPE_TRAP
=
0x4
;
//
const
TYPE_NORMAL
=
0x10
;
//
const
TYPE_EFFECT
=
0x20
;
//
const
TYPE_FUSION
=
0x40
;
//
const
TYPE_RITUAL
=
0x80
;
//
const
TYPE_TRAPMONSTER
=
0x100
;
//
const
TYPE_SPIRIT
=
0x200
;
//
const
TYPE_UNION
=
0x400
;
//
const
TYPE_DUAL
=
0x800
;
//
const
TYPE_TUNER
=
0x1000
;
//
const
TYPE_SYNCHRO
=
0x2000
;
//
export
const
TYPE_MONSTER
=
0x1
;
//
export
const
TYPE_SPELL
=
0x2
;
//
export
const
TYPE_TRAP
=
0x4
;
//
export
const
TYPE_NORMAL
=
0x10
;
//
export
const
TYPE_EFFECT
=
0x20
;
//
export
const
TYPE_FUSION
=
0x40
;
//
export
const
TYPE_RITUAL
=
0x80
;
//
export
const
TYPE_TRAPMONSTER
=
0x100
;
//
export
const
TYPE_SPIRIT
=
0x200
;
//
export
const
TYPE_UNION
=
0x400
;
//
export
const
TYPE_DUAL
=
0x800
;
//
export
const
TYPE_TUNER
=
0x1000
;
//
export
const
TYPE_SYNCHRO
=
0x2000
;
//
export
const
TYPE_TOKEN
=
0x4000
;
//
const
TYPE_QUICKPLAY
=
0x10000
;
//
const
TYPE_CONTINUOUS
=
0x20000
;
//
const
TYPE_EQUIP
=
0x40000
;
//
const
TYPE_FIELD
=
0x80000
;
//
const
TYPE_COUNTER
=
0x100000
;
//
const
TYPE_FLIP
=
0x200000
;
//
const
TYPE_TOON
=
0x400000
;
//
const
TYPE_XYZ
=
0x800000
;
//
const
TYPE_PENDULUM
=
0x1000000
;
//
const
TYPE_SPSUMMON
=
0x2000000
;
//
export
const
TYPE_QUICKPLAY
=
0x10000
;
//
export
const
TYPE_CONTINUOUS
=
0x20000
;
//
export
const
TYPE_EQUIP
=
0x40000
;
//
export
const
TYPE_FIELD
=
0x80000
;
//
export
const
TYPE_COUNTER
=
0x100000
;
//
export
const
TYPE_FLIP
=
0x200000
;
//
export
const
TYPE_TOON
=
0x400000
;
//
export
const
TYPE_XYZ
=
0x800000
;
//
export
const
TYPE_PENDULUM
=
0x1000000
;
//
export
const
TYPE_SPSUMMON
=
0x2000000
;
//
export
const
TYPE_LINK
=
0x4000000
;
//
/*
...
...
@@ -147,13 +154,13 @@ export function isPendulumMonster(typeCode: number): boolean {
// 属性
// const ATTRIBUTE_ALL = 0x7f; //
const
ATTRIBUTE_EARTH
=
0x01
;
//
const
ATTRIBUTE_WATER
=
0x02
;
//
const
ATTRIBUTE_FIRE
=
0x04
;
//
const
ATTRIBUTE_WIND
=
0x08
;
//
const
ATTRIBUTE_LIGHT
=
0x10
;
//
const
ATTRIBUTE_DARK
=
0x20
;
//
const
ATTRIBUTE_DEVINE
=
0x40
;
//
export
const
ATTRIBUTE_EARTH
=
0x01
;
//
export
const
ATTRIBUTE_WATER
=
0x02
;
//
export
const
ATTRIBUTE_FIRE
=
0x04
;
//
export
const
ATTRIBUTE_WIND
=
0x08
;
//
export
const
ATTRIBUTE_LIGHT
=
0x10
;
//
export
const
ATTRIBUTE_DARK
=
0x20
;
//
export
const
ATTRIBUTE_DEVINE
=
0x40
;
//
export
const
Attribute2StringCodeMap
:
Map
<
number
,
number
>
=
new
Map
([
[
ATTRIBUTE_EARTH
,
1010
],
...
...
@@ -166,31 +173,31 @@ export const Attribute2StringCodeMap: Map<number, number> = new Map([
]);
// 种族
const
RACE_WARRIOR
=
0x1
;
//
const
RACE_SPELLCASTER
=
0x2
;
//
const
RACE_FAIRY
=
0x4
;
//
const
RACE_FIEND
=
0x8
;
//
const
RACE_ZOMBIE
=
0x10
;
//
const
RACE_MACHINE
=
0x20
;
//
const
RACE_AQUA
=
0x40
;
//
const
RACE_PYRO
=
0x80
;
//
const
RACE_ROCK
=
0x100
;
//
const
RACE_WINDBEAST
=
0x200
;
//
const
RACE_PLANT
=
0x400
;
//
const
RACE_INSECT
=
0x800
;
//
const
RACE_THUNDER
=
0x1000
;
//
const
RACE_DRAGON
=
0x2000
;
//
const
RACE_BEAST
=
0x4000
;
//
const
RACE_BEASTWARRIOR
=
0x8000
;
//
const
RACE_DINOSAUR
=
0x10000
;
//
const
RACE_FISH
=
0x20000
;
//
const
RACE_SEASERPENT
=
0x40000
;
//
const
RACE_REPTILE
=
0x80000
;
//
const
RACE_PSYCHO
=
0x100000
;
//
const
RACE_DEVINE
=
0x200000
;
//
const
RACE_CREATORGOD
=
0x400000
;
//
const
RACE_WYRM
=
0x800000
;
//
const
RACE_CYBERSE
=
0x1000000
;
//
export
const
RACE_WARRIOR
=
0x1
;
//
export
const
RACE_SPELLCASTER
=
0x2
;
//
export
const
RACE_FAIRY
=
0x4
;
//
export
const
RACE_FIEND
=
0x8
;
//
export
const
RACE_ZOMBIE
=
0x10
;
//
export
const
RACE_MACHINE
=
0x20
;
//
export
const
RACE_AQUA
=
0x40
;
//
export
const
RACE_PYRO
=
0x80
;
//
export
const
RACE_ROCK
=
0x100
;
//
export
const
RACE_WINDBEAST
=
0x200
;
//
export
const
RACE_PLANT
=
0x400
;
//
export
const
RACE_INSECT
=
0x800
;
//
export
const
RACE_THUNDER
=
0x1000
;
//
export
const
RACE_DRAGON
=
0x2000
;
//
export
const
RACE_BEAST
=
0x4000
;
//
export
const
RACE_BEASTWARRIOR
=
0x8000
;
//
export
const
RACE_DINOSAUR
=
0x10000
;
//
export
const
RACE_FISH
=
0x20000
;
//
export
const
RACE_SEASERPENT
=
0x40000
;
//
export
const
RACE_REPTILE
=
0x80000
;
//
export
const
RACE_PSYCHO
=
0x100000
;
//
export
const
RACE_DEVINE
=
0x200000
;
//
export
const
RACE_CREATORGOD
=
0x400000
;
//
export
const
RACE_WYRM
=
0x800000
;
//
export
const
RACE_CYBERSE
=
0x1000000
;
//
export
const
Race2StringCodeMap
:
Map
<
number
,
number
>
=
new
Map
([
[
RACE_WARRIOR
,
1020
],
...
...
src/service/duel/agent.ts
View file @
5e3a7019
...
...
@@ -12,61 +12,32 @@ import {
}
from
"
@/api
"
;
import
{
predictDuel
}
from
"
@/api/ygoAgent/predict
"
;
import
{
convertActionMsg
,
convertCard
,
convertDeckCard
,
convertPhase
,
Global
,
Input
,
MsgSelectSum
,
MultiSelectMsg
,
parsePlayerFromMsg
,
}
from
"
@/api/ygoAgent/schema
"
;
import
{
convertActionMsg
,
convertCard
,
convertDeckCard
,
convertPhase
,
convertPositionResponse
,
parsePlayerFromMsg
,
}
from
"
@/api/ygoAgent/transaction
"
;
import
{
cardStore
,
matStore
}
from
"
@/stores
"
;
function
computeSetDifference
(
a1
:
number
[],
a2
:
number
[]):
number
[]
{
const
freq1
=
new
Map
<
number
,
number
>
();
const
freq2
=
new
Map
<
number
,
number
>
();
for
(
const
num
of
a1
)
{
freq1
.
set
(
num
,
(
freq1
.
get
(
num
)
||
0
)
+
1
);
}
for
(
const
num
of
a2
)
{
freq2
.
set
(
num
,
(
freq2
.
get
(
num
)
||
0
)
+
1
);
}
for
(
const
[
num
,
count
]
of
freq2
)
{
if
(
freq1
.
has
(
num
))
{
freq1
.
set
(
num
,
freq1
.
get
(
num
)
!
-
count
);
}
}
const
difference
:
number
[]
=
[];
for
(
const
[
num
,
count
]
of
freq1
)
{
if
(
count
>
0
)
{
difference
.
push
(...
Array
(
count
).
fill
(
num
));
}
}
import
{
argmax
,
computeSetDifference
}
from
"
./util
"
;
return
difference
;
}
const
{
DECK
,
HAND
,
MZONE
,
SZONE
,
GRAVE
,
REMOVED
,
EXTRA
}
=
ygopro
.
CardZone
;
export
function
genInput
(
msg
:
ygopro
.
StocGameMessage
):
Input
{
// 全局信息可以从 `matStore` 里面拿
/*
* Generate input data for `YgoAgent Server`
* */
export
function
genAgentInput
(
msg
:
ygopro
.
StocGameMessage
):
Input
{
const
mat
=
matStore
;
// 卡片信息可以从 `cardStore` 里面拿
// TODO (ygo-agent): TZONE
const
zones
=
[
ygopro
.
CardZone
.
DECK
,
ygopro
.
CardZone
.
HAND
,
ygopro
.
CardZone
.
MZONE
,
ygopro
.
CardZone
.
SZONE
,
ygopro
.
CardZone
.
GRAVE
,
ygopro
.
CardZone
.
REMOVED
,
ygopro
.
CardZone
.
EXTRA
,
];
// select_xxx msg 从参数 `msg` 里获取
// 这里已经保证 `msg` 是众多 `select_xxx` msg 中的一个
const
zones
=
[
DECK
,
HAND
,
MZONE
,
SZONE
,
GRAVE
,
REMOVED
,
EXTRA
];
const
player
=
parsePlayerFromMsg
(
msg
);
const
opponent
=
1
-
player
;
...
...
@@ -74,10 +45,7 @@ export function genInput(msg: ygopro.StocGameMessage): Input {
.
filter
(
(
card
)
=>
zones
.
includes
(
card
.
location
.
zone
)
&&
!
(
card
.
location
.
zone
===
ygopro
.
CardZone
.
DECK
&&
card
.
location
.
controller
===
player
),
!
(
card
.
location
.
zone
===
DECK
&&
card
.
location
.
controller
===
player
),
)
.
map
((
card
)
=>
convertCard
(
card
,
player
));
...
...
@@ -108,23 +76,22 @@ export function genInput(msg: ygopro.StocGameMessage): Input {
const
actionMsg
=
convertActionMsg
(
msg
);
return
{
global
:
global
,
global
,
cards
:
deckCardsMe
.
concat
(
cards
),
action_msg
:
actionMsg
,
};
}
async
function
sendRequest
(
req
:
PredictReq
)
{
console
.
log
(
"
Sending predict request:
"
,
req
);
const
duelId
=
matStore
.
duelId
;
const
resp
=
await
predictDuel
(
duelId
,
req
);
console
.
log
(
"
Got predict response:
"
,
resp
);
if
(
resp
!==
undefined
)
{
matStore
.
agentIndex
=
resp
.
index
;
}
else
{
throw
new
Error
(
"
Failed to get predict response
"
);
}
// TODO: 下面这些逻辑不应该在这个函数里面,应该在UI组件里面实现
const
preds
=
resp
.
predict_results
.
action_preds
;
const
actionIdx
=
argmax
(
preds
,
(
r
)
=>
r
.
prob
);
matStore
.
prevActionIndex
=
actionIdx
;
...
...
@@ -132,8 +99,9 @@ async function sendRequest(req: PredictReq) {
return
pred
;
}
// TODO: 这个函数的逻辑也需要拆分下
export
async
function
sendAIPredictAsResponse
(
msg
:
ygopro
.
StocGameMessage
)
{
const
input
=
genInput
(
msg
);
const
input
=
gen
Agent
Input
(
msg
);
const
msgName
=
input
.
action_msg
.
data
.
msg_type
;
const
multiSelectMsgs
=
[
"
select_card
"
,
"
select_tribute
"
,
"
select_sum
"
];
...
...
@@ -240,37 +208,3 @@ export async function sendAIPredictAsResponse(msg: ygopro.StocGameMessage) {
}
}
}
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
}
`
);
}
}
src/service/duel/util.ts
View file @
5e3a7019
...
...
@@ -8,3 +8,48 @@ export function isAllOnField(locations: ygopro.CardLocation[]): boolean {
return
locations
.
find
((
location
)
=>
!
isOnField
(
location
))
===
undefined
;
}
export
function
computeSetDifference
(
set1
:
number
[],
set2
:
number
[]):
number
[]
{
const
freq1
=
new
Map
<
number
,
number
>
();
const
freq2
=
new
Map
<
number
,
number
>
();
for
(
const
num
of
set1
)
{
freq1
.
set
(
num
,
(
freq1
.
get
(
num
)
||
0
)
+
1
);
}
for
(
const
num
of
set2
)
{
freq2
.
set
(
num
,
(
freq2
.
get
(
num
)
||
0
)
+
1
);
}
for
(
const
[
num
,
count
]
of
freq2
)
{
if
(
freq1
.
has
(
num
))
{
freq1
.
set
(
num
,
freq1
.
get
(
num
)
!
-
count
);
}
}
const
difference
:
number
[]
=
[];
for
(
const
[
num
,
count
]
of
freq1
)
{
if
(
count
>
0
)
{
difference
.
push
(...
Array
(
count
).
fill
(
num
));
}
}
return
difference
;
}
export
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
;
}
src/stores/matStore/types.ts
View file @
5e3a7019
...
...
@@ -50,6 +50,7 @@ export interface MatState {
/** 根据自己的先后手判断是否是自己 */
isMe
:
(
player
:
number
)
=>
boolean
;
// 下面其中一些貌似可以封装成为`AgentInfo`
turnCount
:
number
;
duelId
:
string
;
agentIndex
:
number
;
...
...
src/ui/Duel/Message/NeosModal/index.tsx
View file @
5e3a7019
...
...
@@ -35,7 +35,7 @@ export const NeosModal: React.FC<ModalProps> = (props) => {
maskClosable=
{
true
}
onCancel=
{
()
=>
setMini
(
!
mini
)
}
closeIcon=
{
mini
?
<
UpOutlined
/>
:
<
MinusOutlined
/>
}
bodyS
tyle=
{
{
padding
:
"
10px 0
"
}
}
s
tyle=
{
{
padding
:
"
10px 0
"
}
}
mask=
{
!
mini
}
wrapClassName=
{
classNames
({
[
styles
.
wrap
]:
mini
})
}
closable=
{
true
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment