Commit e319152f authored by Biluo Shen's avatar Biluo Shen

multi select for select_card and select_triton

parent 377541bf
......@@ -32,7 +32,7 @@
35059553
24224830
24224830
97268402
84211599
73628505
40155014
40155014
......
......@@ -28,7 +28,7 @@
51697825
51697825
51697825
98645731
84211599
28126717
55521751
24224830
......
......@@ -19,7 +19,7 @@
80433039
17827173
24508238
98645731
84211599
75500286
98645731
49238328
......
......@@ -23,7 +23,7 @@
97268402
2511
2511
97268402
84211599
24224830
24224830
33407125
......
......@@ -24,7 +24,7 @@
1984618
1984618
35261759
49238328
84211599
49238328
84797028
84797028
......
......@@ -24,7 +24,7 @@
24508238
2295440
51405049
27204311
84211599
89023486
24224830
24224830
......
# Change log
## 0.2.0 (March 12, 2024)
- A feature of negated is added to cards. This feature is used to indicate whether a card is negated or not.
- Positional encoding is added to history actions. When it wasn't added before, the model cannot distinguish the order of history actions.
- Multi-selet action is removed and implemented by multiple single-select actions. It means that the number of selections is now unlimited.
# Features
## Definitions
- float transform: max 65535 -> 2 bytes
- count
## Card (39)
- id: 2, uint16 -> 2 uint8, name+desc
- location: 1, int, 0: N/A, 1+: same as location2str (9)
- seq: 1, int, 0: N/A, 1+: seq in location
- owner: 1, int, 0: me, 1: oppo (2)
- position: 1, int, 0: N/A, same as position2str
- overlay: 1, int, 0: not, 1: xyz material
- attribute: 1, int, 0: N/A, same as attribute2str[2:]
- race: 1, int, 0: N/A, same as race2str
- level: 1, int, 0: N/A
- counter: 1, int, 0: N/A
- negated: 1, int, 0: False, 1: True
- atk: 2, max 65535 to 2 bytes
- def: 2, max 65535 to 2 bytes
- type: 25, multi-hot, same as type2str
- 0,1: card id, uint16 -> 2 uint8, name+desc
- 2: location, discrete, 0: N/A, 1+: same as location2str (9)
- 3: seq, discrete, 0: N/A, 1+: seq in location
- 4: owner, discrete, 0: me, 1: oppo (2)
- 5: position, discrete, 0: N/A, 1+: same as position2str
- 6: overlay, discrete, 0: not, 1: xyz material
- 7: attribute, discrete, 0: N/A, 1+: same as attribute2str[2:]
- 8: race, discrete, 0: N/A, 1+: same as race2str
- 9: level, discrete, 0: N/A
- 10: counter, discrete, 0: N/A
- 11: negated, discrete, 0: False, 1: True
- 12,13: atk, float transform
- 14,15: def: float transform
- 16-40: type, multi-hot, same as type2str (25)
## Global
- lp: 2, max 65535 to 2 bytes
- oppo_lp: 2, max 65535 to 2 bytes
- n_my_decks: 1, int
- n_my_hands:
- n_my_monsters:
- n_my_spell_traps:
- n_my_graves:
- n_my_removes:
- n_my_extras:
- n_op_decks:
- n_op_hands:
- n_op_monsters:
- n_op_spell_traps:
- n_op_graves:
- n_op_removes:
- n_op_extras:
- n_my_hands: (another embed, to enhance)
- n_op_hands: (another embed, to enhance)
- turn: 1, int, trunc to 8
- phase: 1, int, one-hot (10)
- is_first: 1, int, 0: False, 1: True
- is_my_turn: 1, int, 0: False, 1: True
- is_end: 1, int, 0: False, 1: True
- 0,1: my_lp, float transform
- 2,3: op_lp, float transform
- 4: turn, discrete, trunc to 16
- 5: phase, discrete (10)
- 6: is_first, discrete, 0: False, 1: True
- 7: is_my_turn, discrete, 0: False, 1: True
- 8: n_my_decks, count
- 9: n_my_hands, count
- 10: n_my_monsters, count
- 11: n_my_spell_traps, count
- 12: n_my_graves, count
- 13: n_my_removes, count
- 14: n_my_extras, count
- 15: n_op_decks, count
- 16: n_op_hands, count
- 17: n_op_monsters, count
- 18: n_op_spell_traps, count
- 19: n_op_graves, count
- 20: n_op_removes, count
- 21: n_op_extras, count
- 22: is_end, discrete, 0: False, 1: True
## Legal Actions (max 24)
- spec index: 2, int, select target
- msg: 1, int (16)
- 0,1: spec index or card id, uint16 -> 2 uint8
- 2: msg, discrete, 0: N/A, 1+: same as msg2str (11)
- act: 1, int (11)
- N/A
- t: Set
......
......@@ -3,22 +3,9 @@
## Unsupported
- Many (Crossout Designator)
- Magician (pendulum)
- Shiranui (Fairy Tail - Snow)
# Messgae
## add_counter
Not supported
## select_card
- `min` and `max` <= 5 are supported
- `min` > 5 throws an error
- `max` > 5 is truncated to 5
### related cards
- Fairy Tail - Snow (min=max=7)
- Pot of Prosperity (min=max=6)
## announce_card
Not supported:
- Alsei, the Sylvan High Protector
......
......@@ -841,3 +841,24 @@
92731385
74018812
92907249
49959355
41562624
74586817
98558751
59843383
40364916
99423156
83283063
92826944
57288064
26326541
52467217
4333086
91420202
86926989
55623480
52711246
36630403
13965201
94801854
79783880
......@@ -11,6 +11,8 @@
#include <fstream>
#include <shared_mutex>
#include <iostream>
#include <set>
#include <fmt/core.h>
#include <fmt/ranges.h>
......@@ -1381,10 +1383,12 @@ protected:
// multi select
int ms_idx_ = -1;
int ms_mode_ = 0;
int ms_min_ = 0;
int ms_max_ = 0;
int ms_must_ = 0;
std::vector<std::string> ms_specs_;
std::vector<int> ms_weights_;
std::vector<std::vector<int>> ms_combs_;
ankerl::unordered_dense::map<std::string, int> ms_spec2idx_;
std::vector<int> ms_r_idxs_;
......@@ -1590,47 +1594,110 @@ public:
// }
}
void init_multi_select(int min, int max, const std::vector<std::string> &specs) {
void init_multi_select(
int min, int max, int must, const std::vector<std::string> &specs,
int mode = 0, const std::vector<std::vector<int>> &combs = {}) {
ms_idx_ = 0;
ms_mode_ = mode;
ms_min_ = min;
ms_max_ = max;
ms_must_ = must;
ms_specs_ = specs;
ms_r_idxs_.clear();
ms_spec2idx_.clear();
for (int j = 0; j < ms_specs_.size(); ++j) {
const auto &spec = ms_specs_[j];
options_.push_back(spec);
ms_spec2idx_[spec] = j;
}
if (ms_mode_ == 0) {
for (int j = 0; j < ms_specs_.size(); ++j) {
const auto &spec = ms_specs_[j];
options_.push_back(spec);
}
} else {
ms_combs_ = combs;
_callback_multi_select_2_prepare();
}
}
void handle_multi_select() {
options_ = {};
for (int j = 0; j < ms_specs_.size(); ++j) {
if (ms_spec2idx_.find(ms_specs_[j]) != ms_spec2idx_.end()) {
options_.push_back(ms_specs_[j]);
if (ms_mode_ == 0) {
for (int j = 0; j < ms_specs_.size(); ++j) {
if (ms_spec2idx_.find(ms_specs_[j]) != ms_spec2idx_.end()) {
options_.push_back(ms_specs_[j]);
}
}
if (ms_idx_ == ms_max_ - 1) {
callback_ = [this](int idx) {
_callback_multi_select(idx, true);
};
} else if (ms_idx_ >= ms_min_) {
options_.push_back("f");
callback_ = [this](int idx) {
_callback_multi_select(idx, false);
};
} else {
callback_ = [this](int idx) {
_callback_multi_select(idx, false);
};
}
}
if (ms_idx_ == ms_max_ - 1) {
callback_ = [this](int idx) {
_callback_multi_select(idx, true);
};
} else if (ms_idx_ >= ms_min_) {
options_.push_back("f");
callback_ = [this](int idx) {
_callback_multi_select(idx, false);
};
} else {
_callback_multi_select_2_prepare();
callback_ = [this](int idx) {
_callback_multi_select(idx, false);
};
_callback_multi_select_2(idx);
};
}
}
void _callback_multi_select_2(int idx) {
const auto &option = options_[idx];
idx = ms_spec2idx_.at(option);
ms_r_idxs_.push_back(idx);
std::vector<std::vector<int>> combs;
for (auto &c : ms_combs_) {
if (c[0] == idx) {
c.erase(c.begin());
if (c.empty()) {
_callback_multi_select_2_finish();
return;
} else {
combs.push_back(c);
}
}
}
ms_idx_++;
ms_combs_ = combs;
}
void _callback_multi_select_2_prepare() {
std::set<int> comb;
for (const auto &c : ms_combs_) {
comb.insert(c[0]);
}
for (auto &i : comb) {
const auto &spec = ms_specs_[i];
options_.push_back(spec);
}
}
void _callback_multi_select_2_finish() {
ms_idx_ = -1;
resp_buf_[0] = ms_r_idxs_.size() + ms_must_;
for (int i = 0; i < ms_must_; ++i) {
resp_buf_[i + 1] = 0;
}
for (int i = 0; i < ms_r_idxs_.size(); ++i) {
resp_buf_[i + ms_must_ + 1] = ms_r_idxs_[i];
}
YGO_SetResponseb(pduel_, resp_buf_);
}
void _callback_multi_select(int idx, bool finish) {
const auto &option = options_[idx];
fmt::println("Select card: {}, finish: {}", option, finish);
// fmt::println("Select card: {}, finish: {}", option, finish);
if (option == "f") {
finish = true;
} else {
......@@ -1643,7 +1710,7 @@ public:
for (int i = 0; i < ms_r_idxs_.size(); ++i) {
resp_buf_[i + 1] = ms_r_idxs_[i];
}
fmt::println("{}, {}", ms_r_idxs_.size(), ms_r_idxs_);
// fmt::println("{}, {}", ms_r_idxs_.size(), ms_r_idxs_);
YGO_SetResponseb(pduel_, resp_buf_);
} else {
ms_idx_++;
......@@ -3639,7 +3706,7 @@ private:
return;
}
init_multi_select(min, max, specs);
init_multi_select(min, max, 0, specs);
to_play_ = player;
callback_ = [this](int idx) {
......@@ -3710,7 +3777,7 @@ private:
// combs = combinations_with_weight(release_params, min);
}
init_multi_select(min, max, specs);
init_multi_select(min, max, 0, specs);
to_play_ = player;
callback_ = [this](int idx) {
......@@ -3721,8 +3788,8 @@ private:
auto mode = read_u8();
auto player = read_u8();
auto val = read_u32();
int min = read_u8();
int max = read_u8();
int _min = read_u8();
int _max = read_u8();
auto must_select_size = read_u8();
if (mode == 0) {
......@@ -3736,14 +3803,9 @@ private:
" not implemented for MSG_SELECT_SUM");
}
std::vector<int> must_select_params;
std::vector<std::string> must_select_specs;
std::vector<int> select_params;
std::vector<std::string> select_specs;
must_select_params.reserve(must_select_size);
must_select_specs.reserve(must_select_size);
int expected = val;
if (verbose_) {
std::vector<Card> must_select;
......@@ -3756,7 +3818,6 @@ private:
auto param = read_u32();
Card card = get_card(controller, loc, seq);
must_select.push_back(card);
must_select_params.push_back(param);
expected -= (param & 0xff);
}
auto pl = players_[player];
......@@ -3764,7 +3825,6 @@ private:
std::to_string(expected) + ", seperated by spaces.");
for (const auto &card : must_select) {
auto spec = card.get_spec(player);
must_select_specs.push_back(spec);
pl->notify(card.name_ + " (" + spec +
") must be selected, automatically selected.");
}
......@@ -3777,8 +3837,6 @@ private:
auto param = read_u32();
auto spec = ls_to_spec(loc, seq, 0, controller != player);
must_select_specs.push_back(spec);
must_select_params.push_back(param);
expected -= (param & 0xff);
}
}
......@@ -3834,32 +3892,21 @@ private:
card_levels.push_back(levels);
}
// We assume any card_level can be the first
std::vector<std::vector<int>> combs =
combinations_with_weight2(card_levels, expected);
for (const auto &comb : combs) {
std::string option = "";
int size = comb.size();
for (int j = 0; j < size; ++j) {
option += select_specs[comb[j]];
if (j < size - 1) {
option += " ";
}
}
options_.push_back(option);
for (auto &comb : combs) {
std::sort(comb.begin(), comb.end());
}
init_multi_select(
_min, _max, must_select_size, select_specs, 1, combs);
to_play_ = player;
callback_ = [this, combs, must_select_size](int idx) {
const auto &comb = combs[idx];
resp_buf_[0] = must_select_size + comb.size();
for (int i = 0; i < must_select_size; ++i) {
resp_buf_[i + 1] = 0;
}
for (int i = 0; i < comb.size(); ++i) {
resp_buf_[i + must_select_size + 1] = comb[i];
}
YGO_SetResponseb(pduel_, resp_buf_);
callback_ = [this](int idx) {
_callback_multi_select_2(idx);
};
} else if (msg_ == MSG_SELECT_CHAIN) {
......
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